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