From 4d673aa1dc86e9e71c8d612c9d515dc6c5243639 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=92=D0=BB=D0=B0=D0=B4=D0=B8=D0=BC=D0=B8=D1=80=20=D0=94?= =?UTF-8?q?=D0=B0=D0=BD=D0=B8=D0=BB=D0=BE=D0=B2?= Date: Wed, 16 Oct 2024 14:03:22 +0400 Subject: [PATCH] =?UTF-8?q?=D0=92=20=D0=BF=D1=80=D0=BE=D1=86=D0=B5=D1=81?= =?UTF-8?q?=D1=81=D0=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Components/Components.csproj | 4 + .../NonVisual/HeaderedTablePDF.Designer.cs | 36 +++++++ Components/NonVisual/HeaderedTablePDF.cs | 96 +++++++++++++++++++ Components/NonVisual/HistogramPDF.Designer.cs | 36 +++++++ Components/NonVisual/HistogramPDF.cs | 25 +++++ Components/NonVisual/TablePDF.Designer.cs | 36 +++++++ Components/NonVisual/TablePDF.cs | 75 +++++++++++++++ Components/SaveToPdfHelpers/ChartData.cs | 14 +++ .../SaveToPdfHelpers/LegendPositions.cs | 16 ++++ .../SaveToPdfHelpers/PDFTableSettings.cs | 18 ++++ .../SaveToPdfHelpers/PdfDocumentData.cs | 22 +++++ .../CustomDataGridView.Designer.cs | 0 Components/{ => Visual}/CustomDataGridView.cs | 0 .../{ => Visual}/CustomDataGridView.resx | 0 .../{ => Visual}/CustomListBox.Designer.cs | 0 Components/{ => Visual}/CustomListBox.cs | 0 Components/{ => Visual}/CustomListBox.resx | 0 .../{ => Visual}/CustomTextBox.Designer.cs | 0 Components/{ => Visual}/CustomTextBox.cs | 0 Components/{ => Visual}/CustomTextBox.resx | 0 20 files changed, 378 insertions(+) create mode 100644 Components/NonVisual/HeaderedTablePDF.Designer.cs create mode 100644 Components/NonVisual/HeaderedTablePDF.cs create mode 100644 Components/NonVisual/HistogramPDF.Designer.cs create mode 100644 Components/NonVisual/HistogramPDF.cs create mode 100644 Components/NonVisual/TablePDF.Designer.cs create mode 100644 Components/NonVisual/TablePDF.cs create mode 100644 Components/SaveToPdfHelpers/ChartData.cs create mode 100644 Components/SaveToPdfHelpers/LegendPositions.cs create mode 100644 Components/SaveToPdfHelpers/PDFTableSettings.cs create mode 100644 Components/SaveToPdfHelpers/PdfDocumentData.cs rename Components/{ => Visual}/CustomDataGridView.Designer.cs (100%) rename Components/{ => Visual}/CustomDataGridView.cs (100%) rename Components/{ => Visual}/CustomDataGridView.resx (100%) rename Components/{ => Visual}/CustomListBox.Designer.cs (100%) rename Components/{ => Visual}/CustomListBox.cs (100%) rename Components/{ => Visual}/CustomListBox.resx (100%) rename Components/{ => Visual}/CustomTextBox.Designer.cs (100%) rename Components/{ => Visual}/CustomTextBox.cs (100%) rename Components/{ => Visual}/CustomTextBox.resx (100%) diff --git a/Components/Components.csproj b/Components/Components.csproj index 3e210aa..7fbd9ef 100644 --- a/Components/Components.csproj +++ b/Components/Components.csproj @@ -7,4 +7,8 @@ enable + + + + diff --git a/Components/NonVisual/HeaderedTablePDF.Designer.cs b/Components/NonVisual/HeaderedTablePDF.Designer.cs new file mode 100644 index 0000000..d04d6a7 --- /dev/null +++ b/Components/NonVisual/HeaderedTablePDF.Designer.cs @@ -0,0 +1,36 @@ +namespace Components.NonVisual +{ + partial class HeaderedTablePDF + { + /// + /// Обязательная переменная конструктора. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Освободить все используемые ресурсы. + /// + /// истинно, если управляемый ресурс должен быть удален; иначе ложно. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Код, автоматически созданный конструктором компонентов + + /// + /// Требуемый метод для поддержки конструктора — не изменяйте + /// содержимое этого метода с помощью редактора кода. + /// + private void InitializeComponent() + { + components = new System.ComponentModel.Container(); + } + + #endregion + } +} diff --git a/Components/NonVisual/HeaderedTablePDF.cs b/Components/NonVisual/HeaderedTablePDF.cs new file mode 100644 index 0000000..3bb6343 --- /dev/null +++ b/Components/NonVisual/HeaderedTablePDF.cs @@ -0,0 +1,96 @@ +using MigraDoc.DocumentObjectModel.Tables; +using MigraDoc.DocumentObjectModel; +using MigraDoc.Rendering; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Diagnostics; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Threading.Tasks; +using Components.SaveToPdfHelpers; + +namespace Components.NonVisual +{ + public partial class HeaderedTablePDF : Component + { + public HeaderedTablePDF() + { + InitializeComponent(); + System.Text.Encoding.RegisterProvider(System.Text.CodePagesEncodingProvider.Instance); + } + + public HeaderedTablePDF(IContainer container) + { + container.Add(this); + + InitializeComponent(); + } + + public void GeneratePDFWithHead(PDFTableSettings settings) + { + if (settings == null || + string.IsNullOrEmpty(settings.FilePath) || + string.IsNullOrEmpty(settings.DocumentTitle) || + settings.Columns == null || settings.Columns.Count == 0 || + settings.DataList == null) + throw new ArgumentException("Заполнены не все необходимые данные для генерации документа."); + + Document document = new Document(); + Section section = document.AddSection(); + section.AddParagraph(settings.DocumentTitle, "Heading1"); + + Table table = new Table(); + table.Borders.Width = 0.75; + + // столбцы + foreach (var (_, width, _, _) in settings.Columns) + { + Column column = table.AddColumn(Unit.FromCentimeter(width)); + column.Format.Alignment = ParagraphAlignment.Center; + } + + // заголовки + Row headerRow = table.AddRow(); + headerRow.Height = Unit.FromCentimeter(settings.HeaderRowHeight); + + for (int columnIndex = 0; columnIndex < settings.Columns.Count; columnIndex++) + { + var (headerTitle, _, _, _) = settings.Columns[columnIndex]; + headerRow.Cells[columnIndex].AddParagraph(headerTitle); + headerRow.Cells[columnIndex].Format.Font.Bold = true; + headerRow.Cells[columnIndex].Format.Alignment = ParagraphAlignment.Center; + } + + // данные + foreach (var dataItem in settings.DataList) + { + Row row = table.AddRow(); + row.Height = Unit.FromCentimeter(settings.DataRowHeight); + + for (int columnIndex = 0; columnIndex < settings.Columns.Count; columnIndex++) + { + var (_, _, propertyName, _) = settings.Columns[columnIndex]; + + PropertyInfo propertyInfo = typeof(T).GetProperty(propertyName); + if (propertyInfo == null) + throw new ArgumentException($"Свойство {propertyName} не найдено в классе {typeof(T).Name}."); + + object value = propertyInfo.GetValue(dataItem); + if (columnIndex == 0) + { + row.Cells[columnIndex].Format.Font.Bold = true; + } + row.Cells[columnIndex].AddParagraph(value != null ? value.ToString() : ""); + } + } + + section.Add(table); + + PdfDocumentRenderer renderer = new PdfDocumentRenderer(true) { Document = document }; + renderer.RenderDocument(); + renderer.Save(settings.FilePath); + } + } +} diff --git a/Components/NonVisual/HistogramPDF.Designer.cs b/Components/NonVisual/HistogramPDF.Designer.cs new file mode 100644 index 0000000..35a7179 --- /dev/null +++ b/Components/NonVisual/HistogramPDF.Designer.cs @@ -0,0 +1,36 @@ +namespace Components.NonVisual +{ + partial class HistogramPDF + { + /// + /// Обязательная переменная конструктора. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Освободить все используемые ресурсы. + /// + /// истинно, если управляемый ресурс должен быть удален; иначе ложно. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Код, автоматически созданный конструктором компонентов + + /// + /// Требуемый метод для поддержки конструктора — не изменяйте + /// содержимое этого метода с помощью редактора кода. + /// + private void InitializeComponent() + { + components = new System.ComponentModel.Container(); + } + + #endregion + } +} diff --git a/Components/NonVisual/HistogramPDF.cs b/Components/NonVisual/HistogramPDF.cs new file mode 100644 index 0000000..454a8e9 --- /dev/null +++ b/Components/NonVisual/HistogramPDF.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Diagnostics; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Components.NonVisual +{ + public partial class HistogramPDF : Component + { + public HistogramPDF() + { + InitializeComponent(); + } + + public HistogramPDF(IContainer container) + { + container.Add(this); + + InitializeComponent(); + } + } +} diff --git a/Components/NonVisual/TablePDF.Designer.cs b/Components/NonVisual/TablePDF.Designer.cs new file mode 100644 index 0000000..3f781d7 --- /dev/null +++ b/Components/NonVisual/TablePDF.Designer.cs @@ -0,0 +1,36 @@ +namespace Components.NonVisual +{ + partial class TablePDF + { + /// + /// Обязательная переменная конструктора. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Освободить все используемые ресурсы. + /// + /// истинно, если управляемый ресурс должен быть удален; иначе ложно. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Код, автоматически созданный конструктором компонентов + + /// + /// Требуемый метод для поддержки конструктора — не изменяйте + /// содержимое этого метода с помощью редактора кода. + /// + private void InitializeComponent() + { + components = new System.ComponentModel.Container(); + } + + #endregion + } +} diff --git a/Components/NonVisual/TablePDF.cs b/Components/NonVisual/TablePDF.cs new file mode 100644 index 0000000..f964a50 --- /dev/null +++ b/Components/NonVisual/TablePDF.cs @@ -0,0 +1,75 @@ +using MigraDoc.DocumentObjectModel.Tables; +using MigraDoc.DocumentObjectModel; +using MigraDoc.Rendering; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Diagnostics; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Document = MigraDoc.DocumentObjectModel.Document; +using Components.SaveToPdfHelpers; + +namespace Components.NonVisual +{ + public partial class TablePDF : Component + { + public TablePDF() + { + InitializeComponent(); + System.Text.Encoding.RegisterProvider(System.Text.CodePagesEncodingProvider.Instance); + } + + public TablePDF(IContainer container) + { + container.Add(this); + + InitializeComponent(); + } + + public void GeneratePdf(PdfDocumentData pdfData) + { + if (string.IsNullOrWhiteSpace(pdfData.FileName)) throw new ArgumentException("Имя файла не может быть пустым."); + if (string.IsNullOrWhiteSpace(pdfData.DocumentTitle)) throw new ArgumentException("Название документа не может быть пустым."); + if (pdfData.Tables == null || pdfData.Tables.Count == 0) throw new ArgumentException("Необходимо передать хотя бы одну таблицу."); + + Document document = new Document(); + Section section = document.AddSection(); + + Paragraph title = section.AddParagraph(); + title.AddFormattedText(pdfData.DocumentTitle, TextFormat.Bold); + title.Format.Alignment = ParagraphAlignment.Center; + section.AddParagraph(); + + foreach (var tableData in pdfData.Tables) + { + Table table = section.AddTable(); + int columnsCount = tableData.GetLength(1); + + for (int i = 0; i < columnsCount; i++) + { + Column column = table.AddColumn(Unit.FromCentimeter(3)); + } + + table.Borders.Width = 0.75; + table.Borders.Color = Colors.Black; + + for (int i = 0; i < tableData.GetLength(0); i++) + { + Row row = table.AddRow(); + for (int j = 0; j < tableData.GetLength(1); j++) + { + row.Cells[j].AddParagraph(tableData[i, j]); + } + } + + section.AddParagraph(); + } + PdfDocumentRenderer pdfRenderer = new PdfDocumentRenderer(true); + pdfRenderer.Document = document; + pdfRenderer.RenderDocument(); + pdfRenderer.PdfDocument.Save(pdfData.FileName); + } + } +} diff --git a/Components/SaveToPdfHelpers/ChartData.cs b/Components/SaveToPdfHelpers/ChartData.cs new file mode 100644 index 0000000..09dbda2 --- /dev/null +++ b/Components/SaveToPdfHelpers/ChartData.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Components.SaveToPdfHelpers +{ + public class ChartData + { + public string SeriesName { get; set; } = string.Empty; + public Dictionary? Data { get; set; } // Ключ — категория, значение — значение для гистограммы + } +} diff --git a/Components/SaveToPdfHelpers/LegendPositions.cs b/Components/SaveToPdfHelpers/LegendPositions.cs new file mode 100644 index 0000000..ea1ef09 --- /dev/null +++ b/Components/SaveToPdfHelpers/LegendPositions.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Components.SaveToPdfHelpers +{ + public enum LegendPositions + { + Top, + Bottom, + Left, + Right + } +} diff --git a/Components/SaveToPdfHelpers/PDFTableSettings.cs b/Components/SaveToPdfHelpers/PDFTableSettings.cs new file mode 100644 index 0000000..e7ef4d7 --- /dev/null +++ b/Components/SaveToPdfHelpers/PDFTableSettings.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Components.SaveToPdfHelpers +{ + public class PDFTableSettings + { + public string FilePath { get; set; } = string.Empty; + public string DocumentTitle { get; set; } = string.Empty; + public List<(string HeaderTitle, float Width, string PropertyName, int ColumnIndex)> Columns { get; set; } + public float HeaderRowHeight { get; set; } + public float DataRowHeight { get; set; } + public List? DataList { get; set; } + } +} diff --git a/Components/SaveToPdfHelpers/PdfDocumentData.cs b/Components/SaveToPdfHelpers/PdfDocumentData.cs new file mode 100644 index 0000000..0ab8350 --- /dev/null +++ b/Components/SaveToPdfHelpers/PdfDocumentData.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Components.SaveToPdfHelpers +{ + public class PdfDocumentData + { + public string FileName { get; set; } + public string DocumentTitle { get; set; } + public List Tables { get; set; } + + public PdfDocumentData(string fileName, string documentTitle, List tables) + { + FileName = fileName ?? throw new ArgumentNullException(nameof(fileName)); + DocumentTitle = documentTitle ?? throw new ArgumentNullException(nameof(documentTitle)); + Tables = tables ?? throw new ArgumentNullException(nameof(tables)); + } + } +} diff --git a/Components/CustomDataGridView.Designer.cs b/Components/Visual/CustomDataGridView.Designer.cs similarity index 100% rename from Components/CustomDataGridView.Designer.cs rename to Components/Visual/CustomDataGridView.Designer.cs diff --git a/Components/CustomDataGridView.cs b/Components/Visual/CustomDataGridView.cs similarity index 100% rename from Components/CustomDataGridView.cs rename to Components/Visual/CustomDataGridView.cs diff --git a/Components/CustomDataGridView.resx b/Components/Visual/CustomDataGridView.resx similarity index 100% rename from Components/CustomDataGridView.resx rename to Components/Visual/CustomDataGridView.resx diff --git a/Components/CustomListBox.Designer.cs b/Components/Visual/CustomListBox.Designer.cs similarity index 100% rename from Components/CustomListBox.Designer.cs rename to Components/Visual/CustomListBox.Designer.cs diff --git a/Components/CustomListBox.cs b/Components/Visual/CustomListBox.cs similarity index 100% rename from Components/CustomListBox.cs rename to Components/Visual/CustomListBox.cs diff --git a/Components/CustomListBox.resx b/Components/Visual/CustomListBox.resx similarity index 100% rename from Components/CustomListBox.resx rename to Components/Visual/CustomListBox.resx diff --git a/Components/CustomTextBox.Designer.cs b/Components/Visual/CustomTextBox.Designer.cs similarity index 100% rename from Components/CustomTextBox.Designer.cs rename to Components/Visual/CustomTextBox.Designer.cs diff --git a/Components/CustomTextBox.cs b/Components/Visual/CustomTextBox.cs similarity index 100% rename from Components/CustomTextBox.cs rename to Components/Visual/CustomTextBox.cs diff --git a/Components/CustomTextBox.resx b/Components/Visual/CustomTextBox.resx similarity index 100% rename from Components/CustomTextBox.resx rename to Components/Visual/CustomTextBox.resx