diff --git a/COP-26/DesktopWithMyVisualComponents/FormMain.Designer.cs b/COP-26/DesktopWithMyVisualComponents/FormMain.Designer.cs index 1f52402..d01fd37 100644 --- a/COP-26/DesktopWithMyVisualComponents/FormMain.Designer.cs +++ b/COP-26/DesktopWithMyVisualComponents/FormMain.Designer.cs @@ -57,8 +57,8 @@ customSelectedCheckedListBoxProperty = new MyCustomComponents.CustomSelectedCheckedListBoxProperty(); tabControl = new TabControl(); wordWithImages = new MyCustomComponents.WordWithImages(components); - wordWithTable = new MyCustomComponents.WordWithTable(components); wordWithDiagram = new MyCustomComponents.WordWithDiagram(components); + wordWithTable = new CustomComponents.NonViewComponents.WordWithTable(components); Docs.SuspendLayout(); Visual.SuspendLayout(); groupBoxData.SuspendLayout(); @@ -411,7 +411,7 @@ private Button buttonWordWithTable; private Button buttonWordWithImage; private MyCustomComponents.WordWithImages wordWithImages; - private MyCustomComponents.WordWithTable wordWithTable; private MyCustomComponents.WordWithDiagram wordWithDiagram; + private CustomComponents.NonViewComponents.WordWithTable wordWithTable; } } \ No newline at end of file diff --git a/COP-26/DesktopWithMyVisualComponents/FormMain.cs b/COP-26/DesktopWithMyVisualComponents/FormMain.cs index 1a0a4f2..810f95c 100644 --- a/COP-26/DesktopWithMyVisualComponents/FormMain.cs +++ b/COP-26/DesktopWithMyVisualComponents/FormMain.cs @@ -1,4 +1,5 @@ using DocumentFormat.OpenXml.Drawing.Charts; +using DocumentFormat.OpenXml.Office2010.Excel; using MyCustomComponents; using MyCustomComponents.Models; using System; @@ -157,30 +158,16 @@ namespace DesktopWithMyVisualComponents private void buttonWordWithTable_Click(object sender, EventArgs e) { - wordWithTable.CreateDoc(new WordWithTableDataConfig + var head = new List<(string, string, string)> { - FilePath = "C:\\Users\\Вера\\Desktop\\WordWithTableDocx.docx", - Header = "Таблица:", - UseUnion = true, - ColumnsRowsWidth = new List<(int, int)> { (0, 5), (0, 5), (0, 10), (0, 10), (0, 7), (0, 7), (0, 10), (0, 10), (0, 8) }, - ColumnUnion = new List<(int StartIndex, int Count)> { (2, 3), (5, 3) }, - Headers = new List<(int ColumnIndex, int RowIndex, string Header, string PropertyName)> - { - (0, 0, "Номер", "Id"), - (1, 0, "Парт номер", "Number"), - (2, 0, "Данные о заказе", ""), - (2, 1, "Цена", "Price"), - (3, 1, "Тип комплектующего", "PartType"), - (4, 1, "Модель комплектующего", "Model"), - (5, 0, "Общие данные", ""), - (5, 1, "Энергопотребление", "PowerCons"), - (6, 1, "Наличие гарантии", "Warr"), - (7, 1, "Статус", "State"), - (8, 0, "Заказ", "Order"), + ("Данные о заказе", "Цена", "Price"), + ("Данные о заказе", "Тип комплектующего", "PartType"), + ("Заказ", "", "Order"), + }; - }, - Data = parts - }); + wordWithTable.AddTable("C:\\Users\\Вера\\Desktop\\WordWithTableDocx.docx", "таблица", head, null, parts); + + MessageBox.Show("Word документ с таблицей создан!", "Успех"); } diff --git a/COP-26/DesktopWithMyVisualComponents/FormMain.resx b/COP-26/DesktopWithMyVisualComponents/FormMain.resx index 94806b1..fe078dd 100644 --- a/COP-26/DesktopWithMyVisualComponents/FormMain.resx +++ b/COP-26/DesktopWithMyVisualComponents/FormMain.resx @@ -120,10 +120,10 @@ 17, 17 - - 194, 17 - 358, 17 + + 537, 17 + \ No newline at end of file diff --git a/COP-26/MyCustomComponents/Helpers/ExtensionConfig.cs b/COP-26/MyCustomComponents/Helpers/ExtensionConfig.cs index 47ffadd..30e52db 100644 --- a/COP-26/MyCustomComponents/Helpers/ExtensionConfig.cs +++ b/COP-26/MyCustomComponents/Helpers/ExtensionConfig.cs @@ -74,10 +74,10 @@ namespace MyCustomComponents.Helpers return; } - //if (config2.ColumnsRowsWidth.Count < config2.ColumnUnion[config2.ColumnUnion.Count - 1].StartIndex + config2.ColumnUnion[config2.ColumnUnion.Count - 1].Count - 1) - //{ - // throw new IndexOutOfRangeException("Последнее объединение ячеек выходит за границы таблицы"); - //} + if (config2.ColumnsRowsWidth.Count < config2.ColumnUnion[config2.ColumnUnion.Count - 1].StartIndex + config2.ColumnUnion[config2.ColumnUnion.Count - 1].Count - 1) + { + throw new IndexOutOfRangeException("Последнее объединение ячеек выходит за границы таблицы"); + } int k; for (k = 0; k < config2.ColumnUnion[0].StartIndex; k++) diff --git a/COP-26/MyCustomComponents/MyCustomComponents.csproj b/COP-26/MyCustomComponents/MyCustomComponents.csproj index e495e80..292d374 100644 --- a/COP-26/MyCustomComponents/MyCustomComponents.csproj +++ b/COP-26/MyCustomComponents/MyCustomComponents.csproj @@ -20,5 +20,12 @@ + + + + + + + diff --git a/COP-26/MyCustomComponents/WordLogicBase.cs b/COP-26/MyCustomComponents/WordLogicBase.cs new file mode 100644 index 0000000..b6b17ed --- /dev/null +++ b/COP-26/MyCustomComponents/WordLogicBase.cs @@ -0,0 +1,29 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Xceed.Document.NET; +using Xceed.Words.NET; + +namespace CustomComponents.NonViewComponents.Logic +{ + public abstract class WordLogicBase : IDisposable + { + protected DocX _doc; + + public WordLogicBase(DocX doc) + { + _doc = doc; + } + + public void Dispose() + { + _doc.Dispose(); + } + + protected Paragraph findParagraph(string paragraph) + => _doc.Paragraphs.FirstOrDefault(p => p.Text.Contains(paragraph)) + ?? throw new Exception($"Paragraph '{paragraph}' not found."); + } +} diff --git a/COP-26/MyCustomComponents/WordTableLogic.cs b/COP-26/MyCustomComponents/WordTableLogic.cs new file mode 100644 index 0000000..141f646 --- /dev/null +++ b/COP-26/MyCustomComponents/WordTableLogic.cs @@ -0,0 +1,114 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Threading.Tasks; +using Xceed.Document.NET; +using Xceed.Words.NET; + +namespace CustomComponents.NonViewComponents.Logic +{ + public class WordTableLogic : WordLogicBase + { + private const float DEF_HEIGHT = 20; + + public WordTableLogic(DocX doc) : base(doc) + { + } + + public void AddTable(string paragraph, IEnumerable<(string, string, string)> header, + IEnumerable? rowHeight, IEnumerable values) + { + var p = findParagraph(paragraph); + + var table = _doc.AddTable(header.Count(), values.Count() + 2); + + table.Alignment = Alignment.center; + + _addHeader(table, header); + _fillTable(table, values, header); + _formatHeader(table, header, rowHeight); + + p.InsertTableAfterSelf(table); + + _doc.Save(); + } + + private void _addHeader(Table table, IEnumerable<(string, string, string)> header) + { + for (int i = 0; i < header.Count(); i++) + { + var headerElem = header.ElementAtOrDefault(i); + table.Rows[i].Cells[0].Paragraphs[0].Append(headerElem.Item1); + table.Rows[i].Cells[1].Paragraphs[0].Append(headerElem.Item2); + } + } + + private void _formatHeader(Table table, IEnumerable<(string, string,string)> header, IEnumerable? rowHeight) + { + for (int i = 0; i < header.Count(); i++) + { + var headerElem = header.ElementAtOrDefault(i); + if (string.IsNullOrWhiteSpace(headerElem.Item2)) + { + table.Rows[i].MergeCells(0, 1); + } + var nextElem = header.ElementAtOrDefault(i + 1); + if (headerElem.Item1 == nextElem.Item1) + { + table.MergeCellsInColumn(0, i, i + 1); + } + + table.Rows[i].Height = rowHeight?.ElementAtOrDefault(i) ?? DEF_HEIGHT; + } + } + + private void _fillTable(Table table, IEnumerable values, IEnumerable<(string, string, string)> header) + { + for (int i = 0; i < values.Count(); i++) + { + var data = _getValueData(values.ElementAt(i), header); + _addData(table, i + 2, data); + } + } + + private void _addData(Table table, int indexColumn, Dictionary data) + { + for (int i = 0; i < data.Count; i++) + { + foreach (var cell in data) + { + if (table.Rows[i].Cells[0].Paragraphs[0].Text == cell.Key + || table.Rows[i].Cells[1].Paragraphs[0].Text == cell.Key) + { + table.Rows[i].Cells[indexColumn].Paragraphs[0].Append(cell.Value.ToString()); + } + } + } + } + + private Dictionary _getValueData(T value, IEnumerable<(string, string, string)> header) + { + var result = new Dictionary(); + if (value is null) + { + throw new ArgumentNullException($"The {nameof(value)} is null."); + } + if (value.GetType().GetProperties() is null) + { + throw new ArgumentNullException($"Properties in {nameof(value)} is null"); + } + foreach (var h in header) + { + var headerElem = string.IsNullOrWhiteSpace(h.Item2) ? h.Item1 : h.Item2; + + var propVal = h.Item3 as string; + + result[headerElem] = value.GetType().GetProperty(propVal).GetValue(value, null); + } + return result; + } + } +} diff --git a/COP-26/MyCustomComponents/WordWithTable.Designer.cs b/COP-26/MyCustomComponents/WordWithTable.Designer.cs index 80c96df..1b62f83 100644 --- a/COP-26/MyCustomComponents/WordWithTable.Designer.cs +++ b/COP-26/MyCustomComponents/WordWithTable.Designer.cs @@ -1,4 +1,4 @@ -namespace MyCustomComponents +namespace CustomComponents.NonViewComponents { partial class WordWithTable { diff --git a/COP-26/MyCustomComponents/WordWithTable.cs b/COP-26/MyCustomComponents/WordWithTable.cs index 24ce8de..23a5bfb 100644 --- a/COP-26/MyCustomComponents/WordWithTable.cs +++ b/COP-26/MyCustomComponents/WordWithTable.cs @@ -1,10 +1,4 @@ -using DocumentFormat.OpenXml; -using DocumentFormat.OpenXml.Drawing.Charts; -using DocumentFormat.OpenXml.Drawing; -using DocumentFormat.OpenXml.Packaging; -using DocumentFormat.OpenXml.Wordprocessing; -using MyCustomComponents.Helpers; -using MyCustomComponents.Models; +using CustomComponents.NonViewComponents.Logic; using System; using System.Collections.Generic; using System.ComponentModel; @@ -12,56 +6,12 @@ using System.Diagnostics; using System.Linq; using System.Text; using System.Threading.Tasks; +using Xceed.Words.NET; -namespace MyCustomComponents +namespace CustomComponents.NonViewComponents { public partial class WordWithTable : Component { - private Document _document = null; - - private Body _body = null; - - private DocumentFormat.OpenXml.Wordprocessing.Table _table = null; - - private Document Document - { - get - { - if (_document == null) - { - _document = new Document(); - } - - return _document; - } - } - - private Body Body - { - get - { - if (_body == null) - { - _body = Document.AppendChild(new Body()); - } - - return _body; - } - } - - private DocumentFormat.OpenXml.Wordprocessing.Table Table - { - get - { - if (_table == null) - { - _table = new DocumentFormat.OpenXml.Wordprocessing.Table(); - } - - return _table; - } - } - public WordWithTable() { InitializeComponent(); @@ -73,228 +23,51 @@ namespace MyCustomComponents InitializeComponent(); } - public void CreateDoc(WordWithTableDataConfig config) - { - config.CheckFields(); - config.ColumnsRowsDataCount = (config.Data.Count + 2, config.ColumnsRowsWidth.Count); - CreateHeader(config.Header); - CreateTableWithHeader(); - CreateColumnHeader(config); - string[,] array = new string[config.ColumnsRowsWidth.Count, config.Data.Count]; - for (int j = 0; j < config.Data.Count; j++) - { - int i; - for (i = 0; i < config.ColumnsRowsWidth.Count; i++) - { - (int, int, string, string) tuple = config.Headers.FirstOrDefault<(int, int, string, string)>(((int ColumnIndex, int RowIndex, string Header, string PropertyName) x) => x.ColumnIndex == i && x.RowIndex == 1); - if (tuple.Equals(default((int, int, string, string)))) - { - tuple = config.Headers.FirstOrDefault<(int, int, string, string)>(((int ColumnIndex, int RowIndex, string Header, string PropertyName) x) => x.ColumnIndex == i && x.RowIndex == 0); - } - array[i, j] = config.Data[j].GetType().GetProperty(tuple.Item4)!.GetValue(config.Data[j], null)!.ToString(); + /// + /// Добавляет таблицу данных после параграфа в *.docx документ. + /// Свойства объектов коллекции должны иметь атрибут [DisplayName(string)]. + /// Значения атрибутов должны соответствовать шапке таблицы. + /// Если в коллекции шапки ("Имя", ""), то ячейки объединяются в одну колонку. + /// Если подряд идут те же названия: ("Личная информация", "Фио"), ("Личная информация", "Город"), то ячейки объединяются в одну строку. + /// + /// Любой класс, главное, чтобы свойства имели атрибут [DisplayName(string)] + /// Путь к документу + /// Текст внутри параграфа + /// Информация о шапке + /// Высота строки. Может быть null. + /// Если не указать в какой-то строке значение (поставить null, вместо float значения), то будет установлена высота по умолчанию + /// Коллекция объектов, которые будут добавлены в таблицу + public void AddTable(string pathToFile, string paragraph, + IEnumerable<(string, string, string)> header, + IEnumerable? rowHeight, + IEnumerable values + ) + { + _check(pathToFile, paragraph, header, rowHeight, values); + using (var logic = new WordTableLogic(DocX.Load(pathToFile))) + { + logic.AddTable(paragraph, header, rowHeight, values); + } + } + + private void _check(string pathToFile, string paragraph, + IEnumerable<(string, string, string)> header, + IEnumerable? rowHeight, + IEnumerable values) + { + if (string.IsNullOrWhiteSpace(pathToFile) + || string.IsNullOrWhiteSpace(paragraph)) + { + throw new ArgumentNullException("The path to file or paragraph is null or empty."); + } + if (rowHeight is not null) + { + if (rowHeight.Count() != header.Count()) + { + throw new ArgumentException($"{nameof(rowHeight)} is not of equal length with {nameof(header)}"); } } - - LoadDataToTableWithColumnHeader(array); - SaveDoc(config.FilePath); - } - - public void SaveDoc(string filepath) - { - if (filepath.IsEmpty()) - { - throw new ArgumentNullException("Имя файла не задано"); - } - - if (_document == null || _body == null) - { - throw new ArgumentNullException("Документ не сформирован, сохранять нечего"); - } - - if (_table != null) - { - Body.Append(Table); - } - - using WordprocessingDocument wordprocessingDocument = WordprocessingDocument.Create(filepath, WordprocessingDocumentType.Document); - MainDocumentPart mainDocumentPart = wordprocessingDocument.AddMainDocumentPart(); - mainDocumentPart.Document = Document; - - } - - public void LoadDataToTableWithColumnHeader(string[,] data) - { - for (int i = 0; i < data.GetLength(0); i++) - { - DocumentFormat.OpenXml.Wordprocessing.TableRow tableRow = Table.Elements().ElementAt(i); - for (int j = 0; j < data.GetLength(1); j++) - { - DocumentFormat.OpenXml.Wordprocessing.TableCell tableCell = new DocumentFormat.OpenXml.Wordprocessing.TableCell(); - tableCell.Append(new DocumentFormat.OpenXml.Wordprocessing.Paragraph(new DocumentFormat.OpenXml.Wordprocessing.Run(new DocumentFormat.OpenXml.Wordprocessing.Text(data[i, j])))); - tableRow.Append(tableCell); - } - } - } - - private void AddColumnHeaderCellMergeByCols(string header, int rowHeight) - { - DocumentFormat.OpenXml.Wordprocessing.TableRow tableRow = new DocumentFormat.OpenXml.Wordprocessing.TableRow(); - int? rowHeight2 = rowHeight; - DocumentFormat.OpenXml.Wordprocessing.TableCell tableCell = CreateCell(header, null, rowHeight2); - tableCell.Append(new HorizontalMerge - { - Val = (EnumValue)MergedCellValues.Restart - }); - tableRow.Append(tableCell); - tableCell = new DocumentFormat.OpenXml.Wordprocessing.TableCell(new DocumentFormat.OpenXml.Wordprocessing.Paragraph()); - tableCell.Append(new HorizontalMerge - { - Val = (EnumValue)MergedCellValues.Continue - }); - tableRow.Append(tableCell); - Table.Append(tableRow); - } - - private static DocumentFormat.OpenXml.Wordprocessing.TableCell CreateCell(string header, int? columnWidth = null, int? rowHeight = null) - { - DocumentFormat.OpenXml.Wordprocessing.TableCell tableCell = new DocumentFormat.OpenXml.Wordprocessing.TableCell(); - DocumentFormat.OpenXml.Wordprocessing.TableCellProperties tableCellProperties = new DocumentFormat.OpenXml.Wordprocessing.TableCellProperties(); - if (rowHeight.HasValue) - { - tableCellProperties.Append(new TableRowHeight - { - Val = (UInt32Value)Convert.ToUInt32(rowHeight) - }); - } - - if (columnWidth.HasValue) - { - tableCellProperties.Append(new TableCellWidth - { - Width = (StringValue)columnWidth.Value.ToString() - }); - } - - tableCellProperties.Append(new TableCellVerticalAlignment - { - Val = (EnumValue)TableVerticalAlignmentValues.Center - }); - tableCell.Append(tableCellProperties); - DocumentFormat.OpenXml.Wordprocessing.Paragraph paragraph = new DocumentFormat.OpenXml.Wordprocessing.Paragraph(); - DocumentFormat.OpenXml.Wordprocessing.ParagraphProperties paragraphProperties = new DocumentFormat.OpenXml.Wordprocessing.ParagraphProperties(); - paragraphProperties.Append(new Justification - { - Val = (EnumValue)JustificationValues.Center - }); - paragraph.Append(paragraphProperties); - if (header.HaveText()) - { - DocumentFormat.OpenXml.Wordprocessing.Run run = new DocumentFormat.OpenXml.Wordprocessing.Run(); - run.AppendChild(new DocumentFormat.OpenXml.Wordprocessing.RunProperties(new Bold())); - run.AppendChild(new DocumentFormat.OpenXml.Wordprocessing.Text(header)); - paragraph.Append(run); - } - - tableCell.Append(paragraph); - return tableCell; - } - - public void CreateColumnHeader(WordWithTableConfig config) - { - int k; - for (k = 0; k < config.ColumnUnion[0].StartIndex; k++) - { - AddColumnHeaderCellMergeByCols(config.Headers.FirstOrDefault<(int, int, string, string)>(((int ColumnIndex, int RowIndex, string Header, string PropertyName) x) => x.ColumnIndex == k).Item3, config.ColumnsRowsWidth[k].Row); - } - - int j; - for (j = 0; j < config.ColumnUnion.Count; j++) - { - int l; - for (l = config.ColumnUnion[j].StartIndex; l < config.ColumnUnion[j].StartIndex + config.ColumnUnion[j].Count; l++) - { - DocumentFormat.OpenXml.Wordprocessing.TableRow tableRow = new DocumentFormat.OpenXml.Wordprocessing.TableRow(); - if (l == config.ColumnUnion[j].StartIndex) - { - DocumentFormat.OpenXml.Wordprocessing.TableCell tableCell = CreateCell(config.Headers.FirstOrDefault<(int, int, string, string)>(((int ColumnIndex, int RowIndex, string Header, string PropertyName) x) => x.ColumnIndex == l && x.RowIndex == 0).Item3); - tableCell.Append(new VerticalMerge - { - Val = (EnumValue)MergedCellValues.Restart - }); - tableRow.Append(tableCell); - } - else - { - DocumentFormat.OpenXml.Wordprocessing.TableCell tableCell2 = new DocumentFormat.OpenXml.Wordprocessing.TableCell(new DocumentFormat.OpenXml.Wordprocessing.Paragraph()); - tableCell2.Append(new VerticalMerge - { - Val = (EnumValue)MergedCellValues.Continue - }); - tableRow.Append(tableCell2); - } - - OpenXmlElement[] array = new OpenXmlElement[1]; - string item = config.Headers.FirstOrDefault<(int, int, string, string)>(((int ColumnIndex, int RowIndex, string Header, string PropertyName) x) => x.ColumnIndex == l && x.RowIndex == 1).Item3; - int? rowHeight = config.ColumnsRowsWidth[l].Row; - array[0] = CreateCell(item, null, rowHeight); - tableRow.Append(array); - Table.Append(tableRow); - } - - if (j >= config.ColumnUnion.Count - 1) - { - continue; - } - - for (int m = config.ColumnUnion[j].StartIndex + config.ColumnUnion[j].Count; m < config.ColumnUnion[j + 1].StartIndex; m++) - { - AddColumnHeaderCellMergeByCols(config.Headers.FirstOrDefault<(int, int, string, string)>(((int ColumnIndex, int RowIndex, string Header, string PropertyName) x) => x.ColumnIndex == j).Item3, config.ColumnsRowsWidth[j].Row); - } - } - - int i; - for (i = config.ColumnUnion[config.ColumnUnion.Count - 1].StartIndex + config.ColumnUnion[config.ColumnUnion.Count - 1].Count; i < config.ColumnsRowsWidth.Count; i++) - { - AddColumnHeaderCellMergeByCols(config.Headers.FirstOrDefault<(int, int, string, string)>(((int ColumnIndex, int RowIndex, string Header, string PropertyName) x) => x.ColumnIndex == i).Item3, config.ColumnsRowsWidth[i].Row); - } - } - - public void CreateTableWithHeader() - { - Table.AppendChild(new DocumentFormat.OpenXml.Wordprocessing.TableProperties(new TableBorders(new DocumentFormat.OpenXml.Wordprocessing.TopBorder - { - Val = new EnumValue(BorderValues.Single), - Size = (UInt32Value)12u - }, new DocumentFormat.OpenXml.Wordprocessing.BottomBorder - { - Val = new EnumValue(BorderValues.Single), - Size = (UInt32Value)12u - }, new DocumentFormat.OpenXml.Wordprocessing.LeftBorder - { - Val = new EnumValue(BorderValues.Single), - Size = (UInt32Value)12u - }, new DocumentFormat.OpenXml.Wordprocessing.RightBorder - { - Val = new EnumValue(BorderValues.Single), - Size = (UInt32Value)12u - }, new DocumentFormat.OpenXml.Wordprocessing.InsideHorizontalBorder - { - Val = new EnumValue(BorderValues.Single), - Size = (UInt32Value)12u - }, new DocumentFormat.OpenXml.Wordprocessing.InsideVerticalBorder - { - Val = new EnumValue(BorderValues.Single), - Size = (UInt32Value)12u - }))); - } - - public void CreateHeader(string header) - { - DocumentFormat.OpenXml.Wordprocessing.Paragraph paragraph = Body.AppendChild(new DocumentFormat.OpenXml.Wordprocessing.Paragraph()); - DocumentFormat.OpenXml.Wordprocessing.Run run = paragraph.AppendChild(new DocumentFormat.OpenXml.Wordprocessing.Run()); - run.AppendChild(new DocumentFormat.OpenXml.Wordprocessing.RunProperties(new Bold())); - run.AppendChild(new DocumentFormat.OpenXml.Wordprocessing.Text(header)); } } }