Compare commits

...

9 Commits

Author SHA1 Message Date
bekodeg
e825cc46c2 Добавил каталог 2024-11-07 15:54:02 +04:00
bekodeg
09a8bc1857 Добил добавление и изменение 2024-11-07 14:04:13 +04:00
bekodeg
2c0e6b13f5 Добавление записи работает 2024-11-07 13:41:24 +04:00
bekodeg
8563387642 Реализация репозитория для справочной таблицы 2024-11-06 23:20:09 +04:00
bekodeg
d9abe59250 Сверстал первые формы, настроил di 2024-11-06 23:06:47 +04:00
bekodeg
22041e42bc Dal 2024-11-06 21:04:19 +04:00
bekodeg
cea8e833e8 Изменения на паре 2024-11-04 13:04:34 +04:00
bekodeg
2d50eff660 3 компонент 2024-10-28 20:24:52 +04:00
bekodeg
098e5a1d3c Сделал 2 компонент 2024-10-25 22:29:47 +04:00
59 changed files with 2790 additions and 25 deletions

304
.editorconfig Normal file
View File

@ -0,0 +1,304 @@
# Удалите строку ниже, если вы хотите наследовать параметры .editorconfig из каталогов, расположенных выше в иерархии
root = true
# Файлы C#
[*.cs]
#### Основные параметры EditorConfig ####
# Отступы и интервалы
indent_size = 4
indent_style = space
tab_width = 4
# Предпочтения для новых строк
end_of_line = crlf
insert_final_newline = false
#### Рекомендации по написанию кода .NET ####
# Упорядочение Using
dotnet_separate_import_directive_groups = false
dotnet_sort_system_directives_first = false
file_header_template = unset
# Предпочтения для this. и Me.
dotnet_style_qualification_for_event = false:suggestion
dotnet_style_qualification_for_field = false
dotnet_style_qualification_for_method = false:suggestion
dotnet_style_qualification_for_property = false:suggestion
# Параметры использования ключевых слов языка и типов BCL
dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion
dotnet_style_predefined_type_for_member_access = true:suggestion
# Предпочтения для скобок
dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:suggestion
dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:suggestion
dotnet_style_parentheses_in_other_operators = never_if_unnecessary:suggestion
dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:suggestion
# Предпочтения модификатора
dotnet_style_require_accessibility_modifiers = for_non_interface_members
# Выражения уровень предпочтения
dotnet_style_coalesce_expression = true
dotnet_style_collection_initializer = true:warning
dotnet_style_explicit_tuple_names = true:warning
dotnet_style_namespace_match_folder = true
dotnet_style_null_propagation = true
dotnet_style_object_initializer = true:warning
dotnet_style_operator_placement_when_wrapping = beginning_of_line
dotnet_style_prefer_auto_properties = true:warning
dotnet_style_prefer_collection_expression = when_types_loosely_match
dotnet_style_prefer_compound_assignment = true:warning
dotnet_style_prefer_conditional_expression_over_assignment = true:suggestion
dotnet_style_prefer_conditional_expression_over_return = true:suggestion
dotnet_style_prefer_foreach_explicit_cast_in_source = when_strongly_typed
dotnet_style_prefer_inferred_anonymous_type_member_names = true:warning
dotnet_style_prefer_inferred_tuple_names = true:warning
dotnet_style_prefer_is_null_check_over_reference_equality_method = true
dotnet_style_prefer_simplified_boolean_expressions = true:warning
dotnet_style_prefer_simplified_interpolation = true
# Предпочтения для полей
dotnet_style_readonly_field = true
# Настройки параметров
dotnet_code_quality_unused_parameters = all
# Параметры подавления
dotnet_remove_unnecessary_suppression_exclusions = none
# Предпочтения для новых строк
dotnet_style_allow_multiple_blank_lines_experimental = false:warning
dotnet_style_allow_statement_immediately_after_block_experimental = false:suggestion
#### Рекомендации по написанию кода C# ####
# Предпочтения var
csharp_style_var_elsewhere = false:suggestion
csharp_style_var_for_built_in_types = false:suggestion
csharp_style_var_when_type_is_apparent = true:suggestion
# Члены, заданные выражениями
csharp_style_expression_bodied_accessors = true:warning
csharp_style_expression_bodied_constructors = false:suggestion
csharp_style_expression_bodied_indexers = true:warning
csharp_style_expression_bodied_lambdas = true:warning
csharp_style_expression_bodied_local_functions = false
csharp_style_expression_bodied_methods = true:suggestion
csharp_style_expression_bodied_operators = true:suggestion
csharp_style_expression_bodied_properties = true:warning
# Настройки соответствия шаблонов
csharp_style_pattern_matching_over_as_with_null_check = true
csharp_style_pattern_matching_over_is_with_cast_check = true
csharp_style_prefer_extended_property_pattern = true
csharp_style_prefer_not_pattern = true
csharp_style_prefer_pattern_matching = true:suggestion
csharp_style_prefer_switch_expression = true:warning
# Настройки проверки на null
csharp_style_conditional_delegate_call = true
# Предпочтения модификатора
csharp_prefer_static_local_function = true
csharp_preferred_modifier_order = public,private,protected,internal,file,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,required,volatile,async
csharp_style_prefer_readonly_struct = true
csharp_style_prefer_readonly_struct_member = true
# Предпочтения для блоков кода
csharp_prefer_braces = true:suggestion
csharp_prefer_simple_using_statement = true
csharp_style_namespace_declarations = block_scoped:warning
csharp_style_prefer_method_group_conversion = true:warning
csharp_style_prefer_primary_constructors = true
csharp_style_prefer_top_level_statements = true:warning
# Выражения уровень предпочтения
csharp_prefer_simple_default_expression = true:warning
csharp_style_deconstructed_variable_declaration = true
csharp_style_implicit_object_creation_when_type_is_apparent = true:warning
csharp_style_inlined_variable_declaration = true
csharp_style_prefer_index_operator = true:warning
csharp_style_prefer_local_over_anonymous_function = true
csharp_style_prefer_null_check_over_type_check = true
csharp_style_prefer_range_operator = true:warning
csharp_style_prefer_tuple_swap = true:warning
csharp_style_prefer_utf8_string_literals = true
csharp_style_throw_expression = true
csharp_style_unused_value_assignment_preference = discard_variable:warning
csharp_style_unused_value_expression_statement_preference = discard_variable:suggestion
# предпочтения для директивы using
csharp_using_directive_placement = outside_namespace:warning
# Предпочтения для новых строк
csharp_style_allow_blank_line_after_colon_in_constructor_initializer_experimental = false:warning
csharp_style_allow_blank_line_after_token_in_arrow_expression_clause_experimental = false:warning
csharp_style_allow_blank_line_after_token_in_conditional_expression_experimental = false:warning
csharp_style_allow_blank_lines_between_consecutive_braces_experimental = false:warning
csharp_style_allow_embedded_statements_on_same_line_experimental = true:warning
#### Правила форматирования C# ####
# Предпочтения для новых строк
csharp_new_line_before_catch = true
csharp_new_line_before_else = true
csharp_new_line_before_finally = true
csharp_new_line_before_members_in_anonymous_types = true
csharp_new_line_before_members_in_object_initializers = true
csharp_new_line_before_open_brace = all
csharp_new_line_between_query_expression_clauses = true
# Предпочтения для отступов
csharp_indent_block_contents = true
csharp_indent_braces = false
csharp_indent_case_contents = true
csharp_indent_case_contents_when_block = true
csharp_indent_labels = one_less_than_current
csharp_indent_switch_labels = true
# Предпочтения для интервалов
csharp_space_after_cast = false
csharp_space_after_colon_in_inheritance_clause = true
csharp_space_after_comma = true
csharp_space_after_dot = false
csharp_space_after_keywords_in_control_flow_statements = true
csharp_space_after_semicolon_in_for_statement = true
csharp_space_around_binary_operators = before_and_after
csharp_space_around_declaration_statements = false
csharp_space_before_colon_in_inheritance_clause = true
csharp_space_before_comma = false
csharp_space_before_dot = false
csharp_space_before_open_square_brackets = false
csharp_space_before_semicolon_in_for_statement = false
csharp_space_between_empty_square_brackets = false
csharp_space_between_method_call_empty_parameter_list_parentheses = false
csharp_space_between_method_call_name_and_opening_parenthesis = false
csharp_space_between_method_call_parameter_list_parentheses = false
csharp_space_between_method_declaration_empty_parameter_list_parentheses = false
csharp_space_between_method_declaration_name_and_open_parenthesis = false
csharp_space_between_method_declaration_parameter_list_parentheses = false
csharp_space_between_parentheses = false
csharp_space_between_square_brackets = false
# Предпочтения переноса
csharp_preserve_single_line_blocks = true
csharp_preserve_single_line_statements = true
#### Стили именования ####
# Правила именования
dotnet_naming_rule.asyncmethods_should_be_pascalcaseasync.severity = warning
dotnet_naming_rule.asyncmethods_should_be_pascalcaseasync.symbols = asyncmethods
dotnet_naming_rule.asyncmethods_should_be_pascalcaseasync.style = pascalcaseasync
dotnet_naming_rule.staticasyncmethods_should_be_pascalcaseasync.severity = warning
dotnet_naming_rule.staticasyncmethods_should_be_pascalcaseasync.symbols = staticasyncmethods
dotnet_naming_rule.staticasyncmethods_should_be_pascalcaseasync.style = pascalcaseasync
dotnet_naming_rule.constmember_should_be_constant_case.severity = warning
dotnet_naming_rule.constmember_should_be_constant_case.symbols = constmember
dotnet_naming_rule.constmember_should_be_constant_case.style = constant_case
dotnet_naming_rule.genericparametrs_should_be_typepascalcase.severity = warning
dotnet_naming_rule.genericparametrs_should_be_typepascalcase.symbols = genericparametrs
dotnet_naming_rule.genericparametrs_should_be_typepascalcase.style = typepascalcase
dotnet_naming_rule.localmembers_should_be_camelcase.severity = warning
dotnet_naming_rule.localmembers_should_be_camelcase.symbols = localmembers
dotnet_naming_rule.localmembers_should_be_camelcase.style = camelcase
dotnet_naming_rule.privatfealds_should_be__privatcamalcase.severity = warning
dotnet_naming_rule.privatfealds_should_be__privatcamalcase.symbols = privatfealds
dotnet_naming_rule.privatfealds_should_be__privatcamalcase.style = _privatcamalcase
dotnet_naming_rule.interface_should_be_begins_with_i.severity = warning
dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface
dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i
dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = warning
dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members
dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case
dotnet_naming_rule.types_should_be_pascal_case.severity = warning
dotnet_naming_rule.types_should_be_pascal_case.symbols = types
dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case
# Спецификации символов
dotnet_naming_symbols.interface.applicable_kinds = interface
dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.interface.required_modifiers =
dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum
dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.types.required_modifiers =
dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method
dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.non_field_members.required_modifiers =
dotnet_naming_symbols.asyncmethods.applicable_kinds = method
dotnet_naming_symbols.asyncmethods.applicable_accessibilities = *
dotnet_naming_symbols.asyncmethods.required_modifiers = async
dotnet_naming_symbols.staticasyncmethods.applicable_kinds = method, local_function
dotnet_naming_symbols.staticasyncmethods.applicable_accessibilities = *
dotnet_naming_symbols.staticasyncmethods.required_modifiers = async, static
dotnet_naming_symbols.constmember.applicable_kinds = property, field
dotnet_naming_symbols.constmember.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.constmember.required_modifiers = const
dotnet_naming_symbols.localmembers.applicable_kinds = property, field, parameter, local, method
dotnet_naming_symbols.localmembers.applicable_accessibilities = local
dotnet_naming_symbols.localmembers.required_modifiers =
dotnet_naming_symbols.privatfealds.applicable_kinds = property, field
dotnet_naming_symbols.privatfealds.applicable_accessibilities = private
dotnet_naming_symbols.privatfealds.required_modifiers =
dotnet_naming_symbols.genericparametrs.applicable_kinds = type_parameter
dotnet_naming_symbols.genericparametrs.applicable_accessibilities = *
dotnet_naming_symbols.genericparametrs.required_modifiers =
# Стили именования
dotnet_naming_style.pascal_case.required_prefix =
dotnet_naming_style.pascal_case.required_suffix =
dotnet_naming_style.pascal_case.word_separator =
dotnet_naming_style.pascal_case.capitalization = pascal_case
dotnet_naming_style.begins_with_i.required_prefix = I
dotnet_naming_style.begins_with_i.required_suffix =
dotnet_naming_style.begins_with_i.word_separator =
dotnet_naming_style.begins_with_i.capitalization = pascal_case
dotnet_naming_style.camelcase.required_prefix =
dotnet_naming_style.camelcase.required_suffix =
dotnet_naming_style.camelcase.word_separator =
dotnet_naming_style.camelcase.capitalization = camel_case
dotnet_naming_style.constant_case.required_prefix =
dotnet_naming_style.constant_case.required_suffix =
dotnet_naming_style.constant_case.word_separator = _
dotnet_naming_style.constant_case.capitalization = all_upper
dotnet_naming_style._privatcamalcase.required_prefix = _
dotnet_naming_style._privatcamalcase.required_suffix =
dotnet_naming_style._privatcamalcase.word_separator =
dotnet_naming_style._privatcamalcase.capitalization = camel_case
dotnet_naming_style.pascalcaseasync.required_prefix =
dotnet_naming_style.pascalcaseasync.required_suffix = Async
dotnet_naming_style.pascalcaseasync.word_separator =
dotnet_naming_style.pascalcaseasync.capitalization = pascal_case
dotnet_naming_style.typepascalcase.required_prefix = T
dotnet_naming_style.typepascalcase.required_suffix =
dotnet_naming_style.typepascalcase.word_separator =
dotnet_naming_style.typepascalcase.capitalization = pascal_case

View File

@ -9,6 +9,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestCustomComponents", "Tes
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PIHelperSh.PdfCreator", "PIHelperSh.PdfCreater\PIHelperSh.PdfCreator.csproj", "{572BD835-A500-43C9-A66F-648540F4A1C8}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PIHelperSh.PdfCreator", "PIHelperSh.PdfCreater\PIHelperSh.PdfCreator.csproj", "{572BD835-A500-43C9-A66F-648540F4A1C8}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lab3", "Lab3\Lab3.csproj", "{1E630CC7-090F-471C-ADA1-74107CF3DC2A}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lab3.Database", "Lab3.Database\Lab3.Database.csproj", "{698DE9E8-7885-4F98-AFE3-9A9C6CD2FCF5}"
EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU Debug|Any CPU = Debug|Any CPU
@ -27,6 +31,14 @@ Global
{572BD835-A500-43C9-A66F-648540F4A1C8}.Debug|Any CPU.Build.0 = Debug|Any CPU {572BD835-A500-43C9-A66F-648540F4A1C8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{572BD835-A500-43C9-A66F-648540F4A1C8}.Release|Any CPU.ActiveCfg = Release|Any CPU {572BD835-A500-43C9-A66F-648540F4A1C8}.Release|Any CPU.ActiveCfg = Release|Any CPU
{572BD835-A500-43C9-A66F-648540F4A1C8}.Release|Any CPU.Build.0 = Release|Any CPU {572BD835-A500-43C9-A66F-648540F4A1C8}.Release|Any CPU.Build.0 = Release|Any CPU
{1E630CC7-090F-471C-ADA1-74107CF3DC2A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{1E630CC7-090F-471C-ADA1-74107CF3DC2A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1E630CC7-090F-471C-ADA1-74107CF3DC2A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1E630CC7-090F-471C-ADA1-74107CF3DC2A}.Release|Any CPU.Build.0 = Release|Any CPU
{698DE9E8-7885-4F98-AFE3-9A9C6CD2FCF5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{698DE9E8-7885-4F98-AFE3-9A9C6CD2FCF5}.Debug|Any CPU.Build.0 = Debug|Any CPU
{698DE9E8-7885-4F98-AFE3-9A9C6CD2FCF5}.Release|Any CPU.ActiveCfg = Release|Any CPU
{698DE9E8-7885-4F98-AFE3-9A9C6CD2FCF5}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE

View File

@ -0,0 +1,36 @@
namespace Cop.Borovkov.Var3.Components
{
partial class CustomPdfHistogram
{
/// <summary>
/// Обязательная переменная конструктора.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Освободить все используемые ресурсы.
/// </summary>
/// <param name="disposing">истинно, если управляемый ресурс должен быть удален; иначе ложно.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Код, автоматически созданный конструктором компонентов
/// <summary>
/// Требуемый метод для поддержки конструктора — не изменяйте
/// содержимое этого метода с помощью редактора кода.
/// </summary>
private void InitializeComponent()
{
components = new System.ComponentModel.Container();
}
#endregion
}
}

View File

@ -0,0 +1,68 @@
using Cop.Borovkov.Var3.Models;
using PIHelperSh.PdfCreator;
using PIHelperSh.PdfCreator.Enums;
using PIHelperSh.PdfCreator.Models.PieChartModel;
using System.ComponentModel;
using static System.Runtime.InteropServices.Marshalling.IIUnknownCacheStrategy;
namespace Cop.Borovkov.Var3.Components
{
public partial class CustomPdfHistogram : Component
{
public CustomPdfHistogram()
{
InitializeComponent();
}
public CustomPdfHistogram(IContainer container)
{
container.Add(this);
InitializeComponent();
}
/// <summary>
/// Сохранить гистограмму в пдф
/// </summary>
/// <param name="histogramInfo"></param>
public void SaveToPdf(PdfHistigramInfo histogramInfo)
{
if (string.IsNullOrEmpty(histogramInfo.FilePath))
throw new ArgumentException("Путь к файлу не должен быть пустым.");
if (string.IsNullOrEmpty(histogramInfo.DocumentTitle))
throw new ArgumentException("Название документа не должно быть пустым.");
if (string.IsNullOrEmpty(histogramInfo.HistogramTitle))
throw new ArgumentException("Заголовок диаграммы не должен быть пустым.");
if (histogramInfo.Values == null || histogramInfo.Values.Count() == 0)
throw new ArgumentException("Набор данных не должен быть пустым.");
foreach (var data in histogramInfo.Values)
{
if (string.IsNullOrEmpty(data.Name) || data.Points == null || data.Points.Count() == 0)
throw new ArgumentException($"Набор данных для серии '{data.Name}' некорректен.");
}
PdfCreator creator = new PdfCreator();
creator.AddParagraph(new()
{
Style = PdfStyleType.Title,
Text = histogramInfo.DocumentTitle,
MarginAfter = PdfMargin.Smal,
});
creator.AddHistogram(new()
{
ChartName = histogramInfo.HistogramTitle,
LegendPosition = histogramInfo.LegendPosition,
DataSet = histogramInfo.Values.Select(l => new PdfHistogramData()
{
DisplayName = l.Name,
Value = l.Points.Select(p => (p.Name, p.Value))
}).ToList()
});
creator.SavePdf(histogramInfo.FilePath);
}
}
}

View File

@ -0,0 +1,36 @@
namespace Cop.Borovkov.Var3.Components
{
partial class CustomPdfTableWithGrouping
{
/// <summary>
/// Обязательная переменная конструктора.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Освободить все используемые ресурсы.
/// </summary>
/// <param name="disposing">истинно, если управляемый ресурс должен быть удален; иначе ложно.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Код, автоматически созданный конструктором компонентов
/// <summary>
/// Требуемый метод для поддержки конструктора — не изменяйте
/// содержимое этого метода с помощью редактора кода.
/// </summary>
private void InitializeComponent()
{
components = new System.ComponentModel.Container();
}
#endregion
}
}

View File

@ -0,0 +1,73 @@
using Cop.Borovkov.Var3.Models;
using PIHelperSh.PdfCreator.Models.TableModels;
using PIHelperSh.PdfCreator;
using System.ComponentModel;
using PIHelperSh.PdfCreator.Enums;
using PIHelperSh.PdfCreator.Interfaces;
namespace Cop.Borovkov.Var3.Components
{
/// <summary>
/// Компонент создающий таблицу и группирует элементы по 1 столбцу
/// </summary>
public partial class CustomPdfTableWithGrouping : Component
{
/// <summary>
/// </summary>
public CustomPdfTableWithGrouping()
{
InitializeComponent();
}
/// <summary>
/// </summary>
/// <param name="container"></param>
public CustomPdfTableWithGrouping(IContainer container)
{
container.Add(this);
InitializeComponent();
}
/// <summary>
/// Сохранить набор таблиц в пдф
/// </summary>
/// <param name="fileName"></param>
/// <param name="title"></param>
/// <param name="tables"></param>
public void SaveToPdf<TType>(PdfTableWithGroupingInfo<TType> tableInfo) where TType : class
{
if (!tableInfo.Columns.Any() || !tableInfo.Rows.Any())
{
return;
}
PdfCreator creator = new PdfCreator();
creator.AddParagraph(new()
{
Style = PdfStyleType.Title,
Text = tableInfo.Title,
MarginAfter = PdfMargin.Smal,
});
creator.AddTable(new PdfTable<TType>()
{
Header = tableInfo.Columns.Select(c => new PdfTableColumn()
{
Title = c.Header,
Size = c.Width,
PropertyName = c.PropertyName,
} as IPdfColumnItem).ToList(),
Records = tableInfo.Rows
.OrderBy(r => typeof(TType)
.GetProperty(tableInfo.Columns.First().PropertyName)!.GetValue(r.Value))
.Select(r => (r.Value, r.Height))
.ToList(),
HeaderHeaight = tableInfo.HeaderHeight,
});
creator.SavePdf(tableInfo.FilePath);
}
}
}

View File

@ -5,6 +5,9 @@
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<UseWindowsForms>true</UseWindowsForms> <UseWindowsForms>true</UseWindowsForms>
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
<PackageId>$(AssemblyName)</PackageId>
<Version>$(VersionPrefix)8.0.1</Version>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>

View File

@ -0,0 +1,18 @@
namespace Cop.Borovkov.Var3.Models
{
/// <summary>
/// Точка
/// </summary>
public record ChartPoint
{
/// <summary>
/// Имя
/// </summary>
public required string Name { get; init; } = string.Empty;
/// <summary>
/// Значение
/// </summary>
public required double Value { get; init; }
}
}

View File

@ -0,0 +1,9 @@
namespace Cop.Borovkov.Var3.Models
{
public record ColumnInfo
{
public string Header { get; init; } = null!;
public string PropertyName { get; init; } = null!;
public float Width { get; init; } = 3;
}
}

View File

@ -0,0 +1,35 @@
using PIHelperSh.PdfCreator.Enums;
namespace Cop.Borovkov.Var3.Models
{
/// <summary>
/// Параметры для создания линейной диограммы
/// </summary>
public record PdfHistigramInfo
{
/// <summary>
/// Имя файла (включая путь до файла)
/// </summary>
public string FilePath { get; init; } = @"C:\pdfTable.pdf";
/// <summary>
/// Заголовок документа
/// </summary>
public string DocumentTitle { get; init; } = "Гистограмма";
/// <summary>
/// Заголовок диограммы
/// </summary>
public string HistogramTitle { get; init; } = "Гистограмма";
/// <summary>
/// Расположение легенды
/// </summary>
public PdfLegendPosition LegendPosition { get; init; } = PdfLegendPosition.Bottom;
/// <summary>
/// Значения
/// </summary>
public required IEnumerable<PdfHistogramLineInfo> Values { get; init; }
}
}

View File

@ -0,0 +1,15 @@
namespace Cop.Borovkov.Var3.Models
{
public record PdfHistogramLineInfo
{
/// <summary>
/// Название графика
/// </summary>
public required string Name { get; init; }
/// <summary>
/// Значения
/// </summary>
public required IEnumerable<ChartPoint> Points { get; init; }
}
}

View File

@ -0,0 +1,33 @@
namespace Cop.Borovkov.Var3.Models
{
/// <summary>
/// Параметры для создания таблици в пдф с группировкой по 1 столбцу
/// </summary>
public class PdfTableWithGroupingInfo<TType> where TType : class
{
/// <summary>
/// имя файла (включая путь до файла)
/// </summary>
public string FilePath { get; init; } = @"C:\pdfTable.pdf";
/// <summary>
/// название документа(заголовок в документе)
/// </summary>
public string Title { get; init; } = "Таблица";
/// <summary>
/// Высота заголовков
/// </summary>
public float HeaderHeight { get; init; } = 0.5f;
/// <summary>
/// Параметры столбцов
/// </summary>
public IEnumerable<ColumnInfo> Columns { get; init; } = [];
/// <summary>
/// Список таблиц
/// </summary>
public IEnumerable<RowInfo<TType>> Rows { get; init; } = [];
}
}

View File

@ -0,0 +1,19 @@
namespace Cop.Borovkov.Var3.Models
{
/// <summary>
/// Информация о строке
/// </summary>
/// <typeparam name="T"></typeparam>
public record RowInfo<T> where T : class
{
/// <summary>
/// Высота строки
/// </summary>
public float Height { get; set; } = 0.5f;
/// <summary>
/// Значение строки
/// </summary>
public T Value { get; set; } = null!;
}
}

View File

@ -0,0 +1,34 @@
// <auto-generated> This file has been auto generated by EF Core Power Tools. </auto-generated>
using Lab3.Database.Context.Configurations;
using Lab3.Database.Models;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
#nullable disable
namespace Lab3.Database.Context;
public partial class COPContext : DbContext
{
public COPContext(DbContextOptions<COPContext> options)
: base(options)
{
}
public virtual DbSet<EducationForm> EducationForms { get; set; }
public virtual DbSet<Student> Students { get; set; }
public virtual DbSet<StudentSession> StudentSessions { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.ApplyConfiguration(new Configurations.EducationFormConfiguration());
modelBuilder.ApplyConfiguration(new Configurations.StudentConfiguration());
modelBuilder.ApplyConfiguration(new Configurations.StudentSessionConfiguration());
OnModelCreatingPartial(modelBuilder);
}
partial void OnModelCreatingPartial(ModelBuilder modelBuilder);
}

View File

@ -0,0 +1,46 @@
// <auto-generated> This file has been auto generated by EF Core Power Tools. </auto-generated>
#nullable disable
using Lab3.Database.Models;
using Microsoft.EntityFrameworkCore;
using Npgsql;
using System;
using System.Collections.Generic;
using System.Data;
using System.Threading;
using System.Threading.Tasks;
namespace Lab3.Database.Context
{
public partial class COPContext
{
private ICOPContextFunctions _procedures;
public virtual ICOPContextFunctions Functions
{
get
{
if (_procedures is null) _procedures = new COPContextFunctions(this);
return _procedures;
}
set
{
_procedures = value;
}
}
public ICOPContextFunctions GetFunctions()
{
return Functions;
}
}
public partial class COPContextFunctions : ICOPContextFunctions
{
private readonly COPContext _context;
public COPContextFunctions(COPContext context)
{
_context = context;
}
}
}

View File

@ -0,0 +1,31 @@
// <auto-generated> This file has been auto generated by EF Core Power Tools. </auto-generated>
using Lab3.Database.Context;
using Lab3.Database.Models;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using System;
using System.Collections.Generic;
namespace Lab3.Database.Context.Configurations
{
public partial class EducationFormConfiguration : IEntityTypeConfiguration<EducationForm>
{
public void Configure(EntityTypeBuilder<EducationForm> entity)
{
entity.HasKey(e => e.Id).HasName("EducationForm_pkey");
entity.ToTable("EducationForm");
entity.HasIndex(e => e.Name, "EducationForm_Name_Name1_key").IsUnique();
entity.Property(e => e.Id).ValueGeneratedNever();
entity.Property(e => e.Name)
.IsRequired()
.HasColumnType("character varying");
OnConfigurePartial(entity);
}
partial void OnConfigurePartial(EntityTypeBuilder<EducationForm> entity);
}
}

View File

@ -0,0 +1,33 @@
// <auto-generated> This file has been auto generated by EF Core Power Tools. </auto-generated>
using Lab3.Database.Context;
using Lab3.Database.Models;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using System;
using System.Collections.Generic;
namespace Lab3.Database.Context.Configurations
{
public partial class StudentConfiguration : IEntityTypeConfiguration<Student>
{
public void Configure(EntityTypeBuilder<Student> entity)
{
entity.HasKey(e => e.Id).HasName("Student_pkey");
entity.ToTable("Student");
entity.Property(e => e.Id).ValueGeneratedNever();
entity.Property(e => e.EducationForm)
.IsRequired()
.HasColumnType("character varying");
entity.Property(e => e.Name)
.IsRequired()
.HasColumnType("character varying");
entity.Property(e => e.StartEducation).HasColumnType("timestamp without time zone");
OnConfigurePartial(entity);
}
partial void OnConfigurePartial(EntityTypeBuilder<Student> entity);
}
}

View File

@ -0,0 +1,30 @@
// <auto-generated> This file has been auto generated by EF Core Power Tools. </auto-generated>
using Lab3.Database.Context;
using Lab3.Database.Models;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using System;
using System.Collections.Generic;
namespace Lab3.Database.Context.Configurations
{
public partial class StudentSessionConfiguration : IEntityTypeConfiguration<StudentSession>
{
public void Configure(EntityTypeBuilder<StudentSession> entity)
{
entity.HasKey(e => e.Id).HasName("StudentSession_pkey");
entity.ToTable("StudentSession");
entity.Property(e => e.Id).ValueGeneratedNever();
entity.HasOne(d => d.Student).WithMany(p => p.StudentSessions)
.HasForeignKey(d => d.StudentId)
.HasConstraintName("StudentSession");
OnConfigurePartial(entity);
}
partial void OnConfigurePartial(EntityTypeBuilder<StudentSession> entity);
}
}

View File

@ -0,0 +1,62 @@
// <auto-generated> This file has been auto generated by EF Core Power Tools. </auto-generated>
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Storage;
using System;
using System.Collections.Generic;
using System.Data.Common;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace Lab3.Database.Context
{
public static class DbContextExtensions
{
public static async Task<List<T>> SqlQueryAsync<T>(this DbContext db, string sql, object[] parameters = null, CancellationToken cancellationToken = default)
where T : class
{
if (parameters is null)
{
parameters = new object[] { };
}
if (typeof(T).GetProperties().Any())
{
return await db.Database
.SqlQueryRaw<T>(sql, parameters)
.ToListAsync(cancellationToken);
}
else
{
await db.Database.ExecuteSqlRawAsync(sql, parameters, cancellationToken);
return default;
}
}
}
public class OutputParameter<TValue>
{
private bool _valueSet = false;
public TValue _value;
public TValue Value
{
get
{
if (!_valueSet)
throw new InvalidOperationException("Value not set.");
return _value;
}
}
internal void SetValue(object value)
{
_valueSet = true;
_value = null == value || Convert.IsDBNull(value) ? default(TValue) : (TValue)value;
}
}
}

View File

@ -0,0 +1,17 @@
// <auto-generated> This file has been auto generated by EF Core Power Tools. </auto-generated>
#nullable disable
using Lab3.Database.Models;
using Microsoft.EntityFrameworkCore;
using Npgsql;
using System;
using System.Collections.Generic;
using System.Data;
using System.Threading;
using System.Threading.Tasks;
namespace Lab3.Database.Context
{
public partial interface ICOPContextFunctions
{
}
}

View File

@ -0,0 +1,15 @@
namespace Lab3.Database.DTO
{
public record StudentDTO
{
public Guid Id { get; init; }
public string Name { get; init; } = null!;
public DateTime StartEducation { get; init; }
public string EducationForm { get; init; } = null!;
public List<StudentSessionDTO> StudentSessions { get; init; } = [];
}
}

View File

@ -0,0 +1,9 @@
namespace Lab3.Database.DTO
{
public record StudentSessionDTO
{
public decimal Score { get; init; }
public int Number { get; init; }
}
}

View File

@ -0,0 +1,49 @@
using Lab3.Database.Context;
using Lab3.Database.MappingProfiles;
using Lab3.Database.Repository.Implementations;
using Lab3.Database.Repository.Interfaces;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
namespace Lab3.Database.Extensions
{
public static class DiExtension
{
public static IServiceCollection AddDatabase(
this IServiceCollection services,
IConfiguration configuration)
{
services.AddDbContextPool<COPContext>(
dbContextOptions =>
{
AppContext.SetSwitch("Npgsql.EnableLegacyTimestampBehavior", true);
AppContext.SetSwitch("Npgsql.DisableDateTimeInfinityConversions", true);
_ = dbContextOptions.UseNpgsql(configuration.GetConnectionString("COPDataBase"));
_ = dbContextOptions.ConfigureWarnings(warnings => { });
}
);
return services;
}
public static IServiceCollection AddDbMapping(
this IServiceCollection services)
{
services.AddAutoMapper(typeof(StudentMappingProfile));
services.AddAutoMapper(typeof(SessionMappingProfile));
return services;
}
public static IServiceCollection AddRepositories(
this IServiceCollection services)
{
services.AddScoped<IStudentRepository, StudentRepository>();
services.AddScoped<IEducationFormRepository, EducationFormRepository>();
return services;
}
}
}

View File

@ -0,0 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="AutoMapper" Version="13.0.1" />
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="8.0.4" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,16 @@
using AutoMapper;
using Lab3.Database.DTO;
using Lab3.Database.Models;
namespace Lab3.Database.MappingProfiles
{
public class SessionMappingProfile : Profile
{
public SessionMappingProfile()
{
_ = CreateMap<StudentSession, StudentSessionDTO>();
_ = CreateMap<StudentSessionDTO, StudentSession>()
.ForMember(s => s.Id, opt => opt.MapFrom(s => Guid.NewGuid()));
}
}
}

View File

@ -0,0 +1,15 @@
using AutoMapper;
using Lab3.Database.DTO;
using Lab3.Database.Models;
namespace Lab3.Database.MappingProfiles
{
public class StudentMappingProfile : Profile
{
public StudentMappingProfile()
{
_ = CreateMap<Student, StudentDTO>();
_ = CreateMap<StudentDTO, Student>();
}
}
}

View File

@ -0,0 +1,13 @@
// <auto-generated> This file has been auto generated by EF Core Power Tools. </auto-generated>
#nullable disable
using System;
using System.Collections.Generic;
namespace Lab3.Database.Models;
public partial class EducationForm
{
public Guid Id { get; set; }
public string Name { get; set; }
}

View File

@ -0,0 +1,19 @@
// <auto-generated> This file has been auto generated by EF Core Power Tools. </auto-generated>
#nullable disable
using System;
using System.Collections.Generic;
namespace Lab3.Database.Models;
public partial class Student
{
public Guid Id { get; set; }
public string Name { get; set; }
public DateTime StartEducation { get; set; }
public string EducationForm { get; set; }
public virtual ICollection<StudentSession> StudentSessions { get; set; } = new List<StudentSession>();
}

View File

@ -0,0 +1,19 @@
// <auto-generated> This file has been auto generated by EF Core Power Tools. </auto-generated>
#nullable disable
using System;
using System.Collections.Generic;
namespace Lab3.Database.Models;
public partial class StudentSession
{
public Guid Id { get; set; }
public Guid StudentId { get; set; }
public decimal Score { get; set; }
public int Number { get; set; }
public virtual Student Student { get; set; }
}

View File

@ -0,0 +1,35 @@
using Lab3.Database.Context;
using Lab3.Database.Models;
using Lab3.Database.Repository.Interfaces;
using Microsoft.EntityFrameworkCore;
namespace Lab3.Database.Repository.Implementations
{
public class EducationFormRepository : IEducationFormRepository
{
private readonly COPContext _context;
public EducationFormRepository(COPContext context)
{
_context = context;
}
public async Task<IEnumerable<string>> Get()
{
return await _context.EducationForms
.Select(f => f.Name)
.ToListAsync();
}
public async Task Update(IEnumerable<string> educationForms)
{
await _context.EducationForms.ExecuteDeleteAsync();
await _context.EducationForms.AddRangeAsync(educationForms.Select(f => new EducationForm()
{
Id = Guid.NewGuid(),
Name = f,
}));
await _context.SaveChangesAsync();
}
}
}

View File

@ -0,0 +1,76 @@
using AutoMapper;
using Lab3.Database.Context;
using Lab3.Database.DTO;
using Lab3.Database.Models;
using Lab3.Database.Repository.Interfaces;
using Microsoft.EntityFrameworkCore;
namespace Lab3.Database.Repository.Implementations
{
public class StudentRepository : IStudentRepository
{
private readonly IMapper _mapper;
private readonly COPContext _context;
public StudentRepository(IMapper mapper, COPContext context)
{
_mapper = mapper;
_context = context;
}
public async Task<StudentDTO> CreateAsync(StudentDTO studentDTO)
{
var student = _mapper.Map<Student>(studentDTO);
var result = await _context.Students.AddAsync(student);
await _context.SaveChangesAsync();
return _mapper.Map<StudentDTO>(result.Entity);
}
public Task<StudentDTO> DeleteAsync(Guid id) => throw new NotImplementedException();
public async Task<List<StudentDTO>> GetAsync(int limit = 10000, int offset = 0)
=> _mapper.Map<List<StudentDTO>>(
await _context.Students
.Include(s => s.StudentSessions)
.Skip(offset)
.Take(limit)
.ToListAsync());
public async Task<StudentDTO?> GetAsync(Guid id)
{
return _mapper.Map<StudentDTO>(
await _context.Students
.Include(s => s.StudentSessions)
.FirstOrDefaultAsync());
}
public async Task<StudentDTO?> UpdateAsync(StudentDTO studentDTO)
{
var student = _mapper.Map<Student>(studentDTO);
var currStudent = await _context.Students.FindAsync(student.Id);
if (currStudent == null)
{
return null;
}
currStudent.Name = student.Name;
currStudent.StartEducation = student.StartEducation;
currStudent.EducationForm = student.EducationForm;
foreach (var session in currStudent.StudentSessions)
{
session.Score = student.StudentSessions
.FirstOrDefault(s => s.Number == session.Number)?.Score ?? session.Score;
}
await _context.SaveChangesAsync();
return _mapper.Map<StudentDTO>(currStudent);
}
}
}

View File

@ -0,0 +1,9 @@
namespace Lab3.Database.Repository.Interfaces
{
public interface IEducationFormRepository
{
Task<IEnumerable<string>> Get();
Task Update(IEnumerable<string> educationForms);
}
}

View File

@ -0,0 +1,17 @@
using Lab3.Database.DTO;
namespace Lab3.Database.Repository.Interfaces
{
public interface IStudentRepository
{
Task<List<StudentDTO>> GetAsync(int limit = 10000, int offset = 0);
Task<StudentDTO?> GetAsync(Guid id);
Task<StudentDTO?> UpdateAsync(StudentDTO studentDTO);
Task<StudentDTO> DeleteAsync(Guid id);
Task<StudentDTO> CreateAsync(StudentDTO studentDTO);
}
}

View File

@ -0,0 +1,54 @@
{
"CodeGenerationMode": 4,
"ContextClassName": "COPContext",
"ContextNamespace": "Context",
"FilterSchemas": false,
"IncludeConnectionString": false,
"ModelNamespace": "Models",
"OutputContextPath": "Context",
"OutputPath": "Models",
"PreserveCasingWithRegex": true,
"ProjectRootNamespace": "Lab3.Database",
"Schemas": null,
"SelectedHandlebarsLanguage": 2,
"SelectedToBeGenerated": 0,
"T4TemplatePath": null,
"Tables": [
{
"Name": "public.EducationForm",
"ObjectType": 0
},
{
"Name": "public.Student",
"ObjectType": 0
},
{
"Name": "public.StudentSession",
"ObjectType": 0
}
],
"UiHint": null,
"UncountableWords": null,
"UseAsyncStoredProcedureCalls": true,
"UseBoolPropertiesWithoutDefaultSql": false,
"UseDatabaseNames": false,
"UseDateOnlyTimeOnly": true,
"UseDbContextSplitting": true,
"UseDecimalDataAnnotationForSprocResult": true,
"UseFluentApiOnly": true,
"UseHandleBars": false,
"UseHierarchyId": false,
"UseInflector": true,
"UseLegacyPluralizer": false,
"UseManyToManyEntity": false,
"UseNoDefaultConstructor": true,
"UseNoNavigations": false,
"UseNoObjectFilter": true,
"UseNodaTime": false,
"UseNullableReferences": false,
"UsePrefixNavigationNaming": false,
"UseSchemaFolders": false,
"UseSchemaNamespaces": false,
"UseSpatial": false,
"UseT4": false
}

View File

@ -0,0 +1,48 @@
using DocumentFormat.OpenXml.Office2010.Excel;
using Lab3.Database.Extensions;
using Lab3.Database.Repository.Interfaces;
using Lab3.Forms;
using Lab3.MappingProfiles;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
namespace Lab3.Extensions
{
public static class DIExtension
{
public static IServiceCollection ConfigureDAL(
this IServiceCollection services,
IConfiguration configuration)
{
services.AddDatabase(configuration);
services.AddDbMapping();
services.AddRepositories();
return services;
}
public static IServiceCollection AddForms(
this IServiceCollection services)
{
services.AddScoped<MainForm>();
services.AddScoped<CatalogForm>();
services.AddScoped<Func<Guid?, CreateForm>>(sp => (id
=> new CreateForm(
sp.GetRequiredService<IStudentRepository>(),
sp.GetRequiredService<IEducationFormRepository>(),
id
)));
return services;
}
public static IServiceCollection AddMapping(
this IServiceCollection services)
{
services.AddAutoMapper(typeof(StudentViewMappingProfile));
return services;
}
}
}

View File

@ -0,0 +1,76 @@
namespace Lab3.Forms
{
partial class CatalogForm
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
Catalog = new DataGridView();
EducationForm = new DataGridViewTextBoxColumn();
((System.ComponentModel.ISupportInitialize)Catalog).BeginInit();
SuspendLayout();
//
// Catalog
//
Catalog.AllowUserToAddRows = false;
Catalog.ColumnHeadersHeightSizeMode = DataGridViewColumnHeadersHeightSizeMode.AutoSize;
Catalog.Columns.AddRange(new DataGridViewColumn[] { EducationForm });
Catalog.Dock = DockStyle.Fill;
Catalog.Location = new Point(0, 0);
Catalog.Name = "Catalog";
Catalog.RowHeadersWidth = 51;
Catalog.SelectionMode = DataGridViewSelectionMode.FullRowSelect;
Catalog.Size = new Size(800, 450);
Catalog.TabIndex = 0;
Catalog.CellEndEdit += Catalog_CellEndEditAsync;
Catalog.KeyDown += Catalog_KeyDownAsync;
//
// EducationForm
//
EducationForm.AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill;
EducationForm.HeaderText = "Форма обучения";
EducationForm.MinimumWidth = 6;
EducationForm.Name = "EducationForm";
//
// CatalogForm
//
AutoScaleDimensions = new SizeF(8F, 20F);
AutoScaleMode = AutoScaleMode.Font;
ClientSize = new Size(800, 450);
Controls.Add(Catalog);
Name = "CatalogForm";
Text = "CatalogForm";
Load += CatalogForm_LoadAsync;
((System.ComponentModel.ISupportInitialize)Catalog).EndInit();
ResumeLayout(false);
}
#endregion
private DataGridView Catalog;
private DataGridViewTextBoxColumn EducationForm;
}
}

View File

@ -0,0 +1,97 @@
using Lab3.Database.Repository.Interfaces;
namespace Lab3.Forms
{
public partial class CatalogForm : Form
{
private readonly IEducationFormRepository _repository;
public CatalogForm(IEducationFormRepository educationFormRepository)
{
_repository = educationFormRepository;
InitializeComponent();
}
private async void CatalogForm_LoadAsync(object sender, EventArgs e)
{
Catalog.Rows.Clear();
var values = (await _repository.Get()).ToList();
for (int i = 0; i < values.Count; i++)
{
Catalog.Rows.Add();
Catalog.Rows[i].Cells[0].Value = values[i];
}
}
private async void Catalog_CellEndEditAsync(object sender, DataGridViewCellEventArgs e)
{
await LoadAsync();
}
private async Task LoadAsync()
{
try
{
List<string> values = new List<string>();
for (int i = 0; i < Catalog.Rows.Count; ++i)
{
string? val = (string?)Catalog.Rows[i].Cells[0].Value;
if (string.IsNullOrEmpty(val))
{
MessageBox.Show(
"Неверные данные",
"Ошибка",
MessageBoxButtons.OK,
MessageBoxIcon.Error);
return;
}
values.Add(val);
}
await _repository.Update(values);
}
catch (Exception ex)
{
MessageBox.Show(
ex.Message,
"Ошибка",
MessageBoxButtons.OK,
MessageBoxIcon.Error);
}
}
private async void Catalog_KeyDownAsync(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Insert)
{
Catalog.Rows.Add();
}
if (e.KeyCode == Keys.Delete && Catalog.SelectedRows.Count == 1)
{
if (MessageBox.Show(
"Удалить?",
"Удаление",
MessageBoxButtons.YesNo,
MessageBoxIcon.Question) == DialogResult.Yes)
{
try
{
Catalog.Rows.RemoveAt(Catalog.SelectedRows[0].Index);
await LoadAsync();
}
catch (Exception ex)
{
MessageBox.Show(
ex.Message,
"Ошибка",
MessageBoxButtons.OK,
MessageBoxIcon.Error);
}
}
}
}
}
}

View File

@ -0,0 +1,123 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<metadata name="EducationForm.UserAddedColumn" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>True</value>
</metadata>
</root>

View File

@ -0,0 +1,317 @@
namespace Lab3.Forms
{
partial class CreateForm
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
StartEducationDataPicker = new CustomsComponentsVar2.CustomDateTimePicker();
FormSelector = new ComponentsLab.VisualSelectionComponent();
label1 = new Label();
label2 = new Label();
NameTextBox = new TextBox();
label3 = new Label();
session1Score = new NumericUpDown();
label4 = new Label();
label5 = new Label();
label6 = new Label();
session2Score = new NumericUpDown();
label7 = new Label();
session3Score = new NumericUpDown();
label8 = new Label();
session6Score = new NumericUpDown();
label9 = new Label();
session5Score = new NumericUpDown();
label10 = new Label();
session4Score = new NumericUpDown();
ButtonSave = new Button();
buttonCancel = new Button();
((System.ComponentModel.ISupportInitialize)session1Score).BeginInit();
((System.ComponentModel.ISupportInitialize)session2Score).BeginInit();
((System.ComponentModel.ISupportInitialize)session3Score).BeginInit();
((System.ComponentModel.ISupportInitialize)session6Score).BeginInit();
((System.ComponentModel.ISupportInitialize)session5Score).BeginInit();
((System.ComponentModel.ISupportInitialize)session4Score).BeginInit();
SuspendLayout();
//
// StartEducationDataPicker
//
StartEducationDataPicker.Location = new Point(430, 32);
StartEducationDataPicker.Name = "StartEducationDataPicker";
StartEducationDataPicker.Size = new Size(456, 55);
StartEducationDataPicker.TabIndex = 0;
StartEducationDataPicker.Value = new DateTime(2024, 11, 6, 22, 54, 54, 353);
//
// FormSelector
//
FormSelector.BorderStyle = BorderStyle.FixedSingle;
FormSelector.ComboBoxSelectedValue = "";
FormSelector.Location = new Point(436, 133);
FormSelector.Margin = new Padding(3, 4, 3, 4);
FormSelector.Name = "FormSelector";
FormSelector.Size = new Size(352, 31);
FormSelector.TabIndex = 1;
//
// label1
//
label1.AutoSize = true;
label1.Location = new Point(430, 9);
label1.Name = "label1";
label1.Size = new Size(134, 20);
label1.TabIndex = 2;
label1.Text = "Дата поступления";
//
// label2
//
label2.AutoSize = true;
label2.Location = new Point(436, 101);
label2.Name = "label2";
label2.Size = new Size(128, 20);
label2.TabIndex = 3;
label2.Text = "Форма обучения";
//
// NameTextBox
//
NameTextBox.Location = new Point(12, 60);
NameTextBox.Name = "NameTextBox";
NameTextBox.Size = new Size(315, 27);
NameTextBox.TabIndex = 4;
//
// label3
//
label3.AutoSize = true;
label3.Location = new Point(12, 18);
label3.Name = "label3";
label3.Size = new Size(42, 20);
label3.TabIndex = 5;
label3.Text = "ФИО";
//
// session1Score
//
session1Score.DecimalPlaces = 2;
session1Score.Increment = new decimal(new int[] { 1, 0, 0, 131072 });
session1Score.Location = new Point(12, 156);
session1Score.Maximum = new decimal(new int[] { 5, 0, 0, 0 });
session1Score.Name = "session1Score";
session1Score.Size = new Size(150, 27);
session1Score.TabIndex = 6;
//
// label4
//
label4.AutoSize = true;
label4.Location = new Point(12, 101);
label4.Name = "label4";
label4.Size = new Size(107, 20);
label4.TabIndex = 7;
label4.Text = "Успеваемость";
//
// label5
//
label5.AutoSize = true;
label5.Location = new Point(12, 133);
label5.Name = "label5";
label5.Size = new Size(65, 20);
label5.TabIndex = 8;
label5.Text = "Сессия1";
//
// label6
//
label6.AutoSize = true;
label6.Location = new Point(12, 188);
label6.Name = "label6";
label6.Size = new Size(65, 20);
label6.TabIndex = 10;
label6.Text = "Сессия2";
//
// session2Score
//
session2Score.DecimalPlaces = 2;
session2Score.Increment = new decimal(new int[] { 1, 0, 0, 131072 });
session2Score.Location = new Point(12, 211);
session2Score.Maximum = new decimal(new int[] { 5, 0, 0, 0 });
session2Score.Name = "session2Score";
session2Score.Size = new Size(150, 27);
session2Score.TabIndex = 9;
//
// label7
//
label7.AutoSize = true;
label7.Location = new Point(12, 247);
label7.Name = "label7";
label7.Size = new Size(65, 20);
label7.TabIndex = 12;
label7.Text = "Сессия3";
//
// session3Score
//
session3Score.DecimalPlaces = 2;
session3Score.Increment = new decimal(new int[] { 1, 0, 0, 131072 });
session3Score.Location = new Point(12, 270);
session3Score.Maximum = new decimal(new int[] { 5, 0, 0, 0 });
session3Score.Name = "session3Score";
session3Score.Size = new Size(150, 27);
session3Score.TabIndex = 11;
//
// label8
//
label8.AutoSize = true;
label8.Location = new Point(177, 247);
label8.Name = "label8";
label8.Size = new Size(65, 20);
label8.TabIndex = 18;
label8.Text = "Сессия6";
//
// session6Score
//
session6Score.DecimalPlaces = 2;
session6Score.Increment = new decimal(new int[] { 1, 0, 0, 131072 });
session6Score.Location = new Point(177, 270);
session6Score.Maximum = new decimal(new int[] { 5, 0, 0, 0 });
session6Score.Name = "session6Score";
session6Score.Size = new Size(150, 27);
session6Score.TabIndex = 17;
//
// label9
//
label9.AutoSize = true;
label9.Location = new Point(177, 188);
label9.Name = "label9";
label9.Size = new Size(65, 20);
label9.TabIndex = 16;
label9.Text = "Сессия5";
//
// session5Score
//
session5Score.DecimalPlaces = 2;
session5Score.Increment = new decimal(new int[] { 1, 0, 0, 131072 });
session5Score.Location = new Point(177, 211);
session5Score.Maximum = new decimal(new int[] { 5, 0, 0, 0 });
session5Score.Name = "session5Score";
session5Score.Size = new Size(150, 27);
session5Score.TabIndex = 15;
//
// label10
//
label10.AutoSize = true;
label10.Location = new Point(177, 133);
label10.Name = "label10";
label10.Size = new Size(65, 20);
label10.TabIndex = 14;
label10.Text = "Сессия4";
//
// session4Score
//
session4Score.DecimalPlaces = 2;
session4Score.Increment = new decimal(new int[] { 1, 0, 0, 131072 });
session4Score.Location = new Point(177, 156);
session4Score.Maximum = new decimal(new int[] { 5, 0, 0, 0 });
session4Score.Name = "session4Score";
session4Score.Size = new Size(150, 27);
session4Score.TabIndex = 13;
//
// ButtonSave
//
ButtonSave.Location = new Point(430, 258);
ButtonSave.Name = "ButtonSave";
ButtonSave.Size = new Size(164, 39);
ButtonSave.TabIndex = 19;
ButtonSave.Text = "Сохранить";
ButtonSave.UseVisualStyleBackColor = true;
ButtonSave.Click += ButtonSave_ClickAsync;
//
// buttonCancel
//
buttonCancel.Location = new Point(624, 258);
buttonCancel.Name = "buttonCancel";
buttonCancel.Size = new Size(164, 39);
buttonCancel.TabIndex = 20;
buttonCancel.Text = "Отмена";
buttonCancel.UseVisualStyleBackColor = true;
//
// CreateForm
//
AutoScaleDimensions = new SizeF(8F, 20F);
AutoScaleMode = AutoScaleMode.Font;
ClientSize = new Size(800, 309);
Controls.Add(buttonCancel);
Controls.Add(ButtonSave);
Controls.Add(label8);
Controls.Add(session6Score);
Controls.Add(label9);
Controls.Add(session5Score);
Controls.Add(label10);
Controls.Add(session4Score);
Controls.Add(label7);
Controls.Add(session3Score);
Controls.Add(label6);
Controls.Add(session2Score);
Controls.Add(label5);
Controls.Add(label4);
Controls.Add(session1Score);
Controls.Add(label3);
Controls.Add(NameTextBox);
Controls.Add(label2);
Controls.Add(label1);
Controls.Add(StartEducationDataPicker);
Controls.Add(FormSelector);
Name = "CreateForm";
Text = "CreateForm";
Load += CreateForm_LoadAsync;
((System.ComponentModel.ISupportInitialize)session1Score).EndInit();
((System.ComponentModel.ISupportInitialize)session2Score).EndInit();
((System.ComponentModel.ISupportInitialize)session3Score).EndInit();
((System.ComponentModel.ISupportInitialize)session6Score).EndInit();
((System.ComponentModel.ISupportInitialize)session5Score).EndInit();
((System.ComponentModel.ISupportInitialize)session4Score).EndInit();
ResumeLayout(false);
PerformLayout();
}
#endregion
private CustomsComponentsVar2.CustomDateTimePicker StartEducationDataPicker;
private ComponentsLab.VisualSelectionComponent FormSelector;
private Label label1;
private Label label2;
private TextBox NameTextBox;
private Label label3;
private NumericUpDown session1Score;
private Label label4;
private Label label5;
private Label label6;
private NumericUpDown session2Score;
private Label label7;
private NumericUpDown session3Score;
private Label label8;
private NumericUpDown session6Score;
private Label label9;
private NumericUpDown session5Score;
private Label label10;
private NumericUpDown session4Score;
private Button ButtonSave;
private Button buttonCancel;
}
}

View File

@ -0,0 +1,110 @@
using Lab3.Database.DTO;
using Lab3.Database.Models;
using Lab3.Database.Repository.Interfaces;
namespace Lab3.Forms
{
public partial class CreateForm : Form
{
private readonly IStudentRepository _studentRepository;
private readonly IEducationFormRepository _educationFormRepository;
private readonly Guid? _updatedStudentGuid = null;
public CreateForm(
IStudentRepository studentRepository,
IEducationFormRepository educationFormRepository,
Guid? updatedStudentGuid)
{
_studentRepository = studentRepository;
_educationFormRepository = educationFormRepository;
_updatedStudentGuid = updatedStudentGuid;
InitializeComponent();
}
private async void CreateForm_LoadAsync(object sender, EventArgs e)
{
FormSelector.Fill(await _educationFormRepository.Get());
StartEducationDataPicker.DateStart = DateTime.Now.AddYears(-6);
StartEducationDataPicker.DateEnd = DateTime.Now;
if (_updatedStudentGuid == null)
{
return;
}
var student = await _studentRepository.GetAsync(_updatedStudentGuid.Value);
NameTextBox.Text = student.Name;
StartEducationDataPicker.Value = student.StartEducation;
FormSelector.ComboBoxSelectedValue = student.EducationForm;
session1Score.Value = student.StudentSessions
.FirstOrDefault(s => s.Number == 1)?.Score ?? decimal.Zero;
session2Score.Value = student.StudentSessions
.FirstOrDefault(s => s.Number == 2)?.Score ?? decimal.Zero;
session3Score.Value = student.StudentSessions
.FirstOrDefault(s => s.Number == 3)?.Score ?? decimal.Zero;
session4Score.Value = student.StudentSessions
.FirstOrDefault(s => s.Number == 4)?.Score ?? decimal.Zero;
session5Score.Value = student.StudentSessions
.FirstOrDefault(s => s.Number == 5)?.Score ?? decimal.Zero;
session6Score.Value = student.StudentSessions
.FirstOrDefault(s => s.Number == 6)?.Score ?? decimal.Zero;
}
private async void ButtonSave_ClickAsync(object sender, EventArgs e)
{
if (string.IsNullOrEmpty(NameTextBox.Text)
|| string.IsNullOrEmpty(FormSelector.ComboBoxSelectedValue))
{
throw new Exception();
}
StudentDTO student = new()
{
Id = _updatedStudentGuid ?? Guid.NewGuid(),
Name = NameTextBox.Text,
StartEducation = StartEducationDataPicker.Value,
EducationForm = FormSelector.ComboBoxSelectedValue,
StudentSessions = [
new(){
Score = session1Score.Value,
Number = 1,
},
new(){
Score = session2Score.Value,
Number = 2,
},
new(){
Score = session3Score.Value,
Number = 3,
},
new(){
Score = session4Score.Value,
Number = 4,
},
new(){
Score = session5Score.Value,
Number = 5,
},
new(){
Score = session6Score.Value,
Number = 6,
},
],
};
if (_updatedStudentGuid != null)
{
await _studentRepository.UpdateAsync(student);
}
else
{
await _studentRepository.CreateAsync(student);
}
}
}
}

View File

@ -0,0 +1,120 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

View File

@ -0,0 +1,60 @@
namespace Lab3.Forms
{
partial class MainForm
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
StudentsListBox = new Cop.Borovkov.Var3.Components.CustomListBox();
SuspendLayout();
//
// StudentsListBox
//
StudentsListBox.Dock = DockStyle.Fill;
StudentsListBox.Location = new Point(0, 0);
StudentsListBox.Name = "StudentsListBox";
StudentsListBox.Selected = "";
StudentsListBox.Size = new Size(800, 450);
StudentsListBox.TabIndex = 0;
//
// MainForm
//
AutoScaleDimensions = new SizeF(8F, 20F);
AutoScaleMode = AutoScaleMode.Font;
ClientSize = new Size(800, 450);
Controls.Add(StudentsListBox);
Name = "MainForm";
Text = "MainForm";
Load += MainForm_LoadAsync;
KeyDown += MainForm_KeyDown;
ResumeLayout(false);
}
#endregion
private Cop.Borovkov.Var3.Components.CustomListBox StudentsListBox;
}
}

View File

@ -0,0 +1,64 @@
using AutoMapper;
using Lab3.Database.Repository.Interfaces;
using Lab3.Models;
namespace Lab3.Forms
{
public partial class MainForm : Form
{
private readonly IStudentRepository _studentRepository;
private readonly IMapper _mapper;
private readonly Func<Guid?, CreateForm> _getCreateOrUpdateForm;
public MainForm(
IStudentRepository repository,
IMapper mapper,
Func<Guid?, CreateForm> getCreateOrUpdateForm)
{
_studentRepository = repository;
_mapper = mapper;
_getCreateOrUpdateForm = getCreateOrUpdateForm;
InitializeComponent();
}
private async void MainForm_LoadAsync(object sender, EventArgs e)
{
var students = _mapper.Map<List<StudentViewModel>>(await _studentRepository.GetAsync());
StudentsListBox.FillValues(
students.Select(s => string.Join(" ",
[
s.Id,
s.Name,
s.EducationForm,
s.StartEducation.ToLongDateString(),
s.SessionMarks,
]
))
);
}
private void MainForm_KeyDown(object sender, KeyEventArgs e)
{
if (e.Modifiers != Keys.Control)
{
return;
}
switch (e.KeyCode)
{
case Keys.A:
_getCreateOrUpdateForm(null).Show(this);
break;
case Keys.U:
_getCreateOrUpdateForm(
Guid.Parse(StudentsListBox.Selected.Split()[0])
).Show(this);
break;
default:
return;
}
}
}
}

View File

@ -0,0 +1,120 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

View File

@ -0,0 +1,33 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net8.0-windows</TargetFramework>
<Nullable>enable</Nullable>
<UseWindowsForms>true</UseWindowsForms>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<Folder Include="Logic\" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="ComponentsLibraryLab1" Version="8.0.2" />
<PackageReference Include="ComponentsLibraryLab2" Version="8.0.1" />
<PackageReference Include="Cop.Borovkov.Var3" Version="8.0.1" />
<PackageReference Include="CustomComponentsVar2" Version="8.0.2" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.1" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Lab3.Database\Lab3.Database.csproj" />
</ItemGroup>
<ItemGroup>
<None Update="appsettings.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>

View File

@ -0,0 +1,15 @@
using AutoMapper;
using Lab3.Database.DTO;
using Lab3.Models;
namespace Lab3.MappingProfiles
{
public class StudentViewMappingProfile : Profile
{
public StudentViewMappingProfile()
{
_ = CreateMap<StudentDTO, StudentViewModel>()
;
}
}
}

View File

@ -0,0 +1,12 @@
using Lab3.Database.DTO;
using System.Globalization;
namespace Lab3.Models
{
public record StudentViewModel : StudentDTO
{
public string SessionMarks => string.Join("; ", StudentSessions
.OrderBy(s => s.Number)
.Select(s => $"сессия{s.Number:d}: {s.Score:n2}"));
}
}

View File

@ -0,0 +1,41 @@
using Lab3.Extensions;
using Lab3.Forms;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
namespace Lab3
{
internal static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
// To customize application configuration such as set high DPI settings or default font,
// see https://aka.ms/applicationconfiguration.
ApplicationConfiguration.Initialize();
var app = CreateHostBuilder().Build();
Application.Run(app.Services.GetRequiredService<CatalogForm>());
}
static IHostBuilder CreateHostBuilder()
{
return Host.CreateDefaultBuilder()
.ConfigureAppConfiguration(c
=> c.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true))
.ConfigureServices((context, services) => {
services.ConfigureDAL(context.Configuration);
services.AddMapping();
services.AddForms();
});
}
}
}

View File

@ -0,0 +1,5 @@
{
"ConnectionStrings": {
"COPDataBase": "Host=localhost;Username=postgres;Password=postgres;Database=COP"
}
}

View File

@ -1,10 +1,4 @@
using System; namespace PIHelperSh.PdfCreator.Enums
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace PIHelperSh.PdfCreator.Enums
{ {
/// <summary> /// <summary>
/// Варианты расположения легенды /// Варианты расположения легенды

View File

@ -0,0 +1,42 @@
using System.Drawing;
namespace PIHelperSh.PdfCreator.Models.PieChartModel
{
/// <summary>
/// Элемент данных. Одно значение для диаграммы.
/// </summary>
public class PdfHistogramData
{
/// <summary>
/// Название варианта
/// </summary>
public string DisplayName { get; set; } = null!;
/// <summary>
/// Значение варианта(по ним строят диаграмму)
/// </summary>
public IEnumerable<(string Name, double Value)> Value { get; set; }
/// <summary>
/// Цвет области на диаграме. При null будет использоватсся выдача цветов по умолчанию)
/// </summary>
public Color? Color { get; set; } = null;
/// <summary>
/// </summary>
public PdfHistogramData()
{
Value = [];
}
/// <summary>
/// </summary>
/// <param name="displayName"></param>
/// <param name="value"></param>
public PdfHistogramData(string displayName, IEnumerable<(string, double)> value)
{
DisplayName = displayName;
Value = value;
}
}
}

View File

@ -0,0 +1,13 @@
namespace PIHelperSh.PdfCreator.Models.PieChartModel
{
/// <summary>
/// Модель линейной диограммы
/// </summary>
public class PdfHistogramModel : PdfPieChartModel
{
/// <summary>
/// Набор данных для создания диаграммы
/// </summary>
public new List<PdfHistogramData> DataSet { get; set; } = [];
}
}

View File

@ -1,9 +1,4 @@
using PIHelperSh.PdfCreator.Enums; using PIHelperSh.PdfCreator.Enums;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace PIHelperSh.PdfCreator.Models.PieChartModel namespace PIHelperSh.PdfCreator.Models.PieChartModel
{ {

View File

@ -27,7 +27,7 @@ namespace PIHelperSh.PdfCreator.Models.TableModels
/// <summary> /// <summary>
/// Список объектов, информация о которых будет в таблице. /// Список объектов, информация о которых будет в таблице.
/// </summary> /// </summary>
public List<T> Records { get; set; } = new(); public List<(T value, float height)> Records { get; set; } = new();
/// <summary> /// <summary>
/// Стиль объектов в таблице (по умолчанию - базовый) /// Стиль объектов в таблице (по умолчанию - базовый)
@ -38,5 +38,10 @@ namespace PIHelperSh.PdfCreator.Models.TableModels
/// Выравнивание текста объектов в таблице (по умолчанию - по левой строне) /// Выравнивание текста объектов в таблице (по умолчанию - по левой строне)
/// </summary> /// </summary>
public PdfAlignmentType RecordHorizontalAlignment { get; set; } = PdfAlignmentType.Left; public PdfAlignmentType RecordHorizontalAlignment { get; set; } = PdfAlignmentType.Left;
/// <summary>
/// Высота заголовка
/// </summary>
public float HeaderHeaight { get; set; }
} }
} }

View File

@ -253,6 +253,9 @@ namespace PIHelperSh.PdfCreator
} }
} }
upRow.Height = header.HeaderHeaight;
downRow.Height = header.HeaderHeaight;
return objectFields; return objectFields;
} }
@ -428,9 +431,14 @@ namespace PIHelperSh.PdfCreator
foreach (var item in header.Records) foreach (var item in header.Records)
{ {
var row = table.AddRow(); var row = table.AddRow();
for (int i = 0; i < maper.Count; i++)
row.Height = item.height;
ConfigurateCell(row.Cells[0], maper[0](item.value)?.ToString()!, header.RecordHorizontalAlignment, header.HeaderStyle);
for (int i = 1; i < maper.Count; i++)
{ {
ConfigurateCell(row.Cells[i], maper[i](item)?.ToString()!, header.RecordHorizontalAlignment, header.RecordStyle); ConfigurateCell(row.Cells[i], maper[i](item.value)?.ToString()!, header.RecordHorizontalAlignment, header.RecordStyle);
} }
} }
} }
@ -499,6 +507,50 @@ namespace PIHelperSh.PdfCreator
_section?.Add(chart); _section?.Add(chart);
} }
/// <summary>
/// Создаёт линейную диаграмму.
/// </summary>
/// <param name="pieHistogram">Модель для круговой диаграммы</param>
public void AddHistogram(PdfHistogramModel pieHistogram)
{
if (_document == null)
{
return;
}
Chart chart = new Chart(ChartType.Line);
ConfiguratePieChart(chart, pieHistogram);
XSeries xseries = chart.XValues.AddXSeries();
foreach (var val in pieHistogram.DataSet.First().Value.Select(v => v.Name))
xseries.Add(val);
foreach (var item in pieHistogram.DataSet)
{
Series series = chart.SeriesCollection.AddSeries();
series.Name = item.DisplayName;
series.Add(item.Value.Select(v => v.Value).ToArray());
if (item.Color.HasValue)
{
series.FillFormat.Color = new Color((uint)item.Color.Value.ToArgb());
}
}
chart.XAxis.MajorTickMark = TickMarkType.Outside;
chart.XAxis.Title.Caption = "X";
chart.XAxis.Title.Alignment = HorizontalAlignment.Right;
chart.YAxis.MajorTickMark = TickMarkType.Outside;
chart.YAxis.Title.Caption = "Y";
chart.YAxis.HasMajorGridlines = true;
_section?.Add(chart);
}
/// <summary> /// <summary>
/// Добавляем на лист изображение. Можно по пути, можно по потоку, можно по Base64 строке /// Добавляем на лист изображение. Можно по пути, можно по потоку, можно по Base64 строке
/// </summary> /// </summary>

View File

@ -31,24 +31,52 @@
components = new System.ComponentModel.Container(); components = new System.ComponentModel.Container();
customPdfTable1 = new Cop.Borovkov.Var3.Components.CustomPdfTable(components); customPdfTable1 = new Cop.Borovkov.Var3.Components.CustomPdfTable(components);
button1 = new Button(); button1 = new Button();
button2 = new Button();
customPdfTableWithGrouping1 = new Cop.Borovkov.Var3.Components.CustomPdfTableWithGrouping(components);
button3 = new Button();
customPdfHistogram1 = new Cop.Borovkov.Var3.Components.CustomPdfHistogram(components);
SuspendLayout(); SuspendLayout();
// //
// button1 // button1
// //
button1.Location = new Point(12, 12); button1.Location = new Point(14, 16);
button1.Margin = new Padding(3, 4, 3, 4);
button1.Name = "button1"; button1.Name = "button1";
button1.Size = new Size(75, 23); button1.Size = new Size(86, 31);
button1.TabIndex = 0; button1.TabIndex = 0;
button1.Text = "button1"; button1.Text = "button1";
button1.UseVisualStyleBackColor = true; button1.UseVisualStyleBackColor = true;
button1.Click += button1_Click; button1.Click += button1_Click;
// //
// button2
//
button2.Location = new Point(106, 16);
button2.Name = "button2";
button2.Size = new Size(94, 29);
button2.TabIndex = 1;
button2.Text = "button2";
button2.UseVisualStyleBackColor = true;
button2.Click += button2_Click;
//
// button3
//
button3.Location = new Point(206, 18);
button3.Name = "button3";
button3.Size = new Size(94, 29);
button3.TabIndex = 2;
button3.Text = "button3";
button3.UseVisualStyleBackColor = true;
button3.Click += button3_Click;
//
// Form2 // Form2
// //
AutoScaleDimensions = new SizeF(7F, 15F); AutoScaleDimensions = new SizeF(8F, 20F);
AutoScaleMode = AutoScaleMode.Font; AutoScaleMode = AutoScaleMode.Font;
ClientSize = new Size(800, 450); ClientSize = new Size(914, 600);
Controls.Add(button3);
Controls.Add(button2);
Controls.Add(button1); Controls.Add(button1);
Margin = new Padding(3, 4, 3, 4);
Name = "Form2"; Name = "Form2";
Text = "Form2"; Text = "Form2";
ResumeLayout(false); ResumeLayout(false);
@ -58,5 +86,9 @@
private Cop.Borovkov.Var3.Components.CustomPdfTable customPdfTable1; private Cop.Borovkov.Var3.Components.CustomPdfTable customPdfTable1;
private Button button1; private Button button1;
private Button button2;
private Cop.Borovkov.Var3.Components.CustomPdfTableWithGrouping customPdfTableWithGrouping1;
private Button button3;
private Cop.Borovkov.Var3.Components.CustomPdfHistogram customPdfHistogram1;
} }
} }

View File

@ -1,4 +1,9 @@
namespace TestCustomComponents.Forms using Cop.Borovkov.Var3.Components;
using Newtonsoft.Json.Serialization;
using PIHelperSh.PdfCreator.Enums;
using TestCustomComponents.Models;
namespace TestCustomComponents.Forms
{ {
public partial class Form2 : Form public partial class Form2 : Form
{ {
@ -11,9 +16,9 @@
{ {
customPdfTable1.SaveToPdf(new() customPdfTable1.SaveToPdf(new()
{ {
FilePath = @"F:\test\test.pdf", FilePath = @"E:\test\test.pdf",
Title = "Текст заголовка", Title = "Текст заголовка",
Tables = [new[,] Tables = [new[,]
{ {
{ "00", "01", "02" }, { "00", "01", "02" },
{ "10", "11", "12" }, { "10", "11", "12" },
@ -23,7 +28,7 @@
customPdfTable1.SaveToPdf(new() customPdfTable1.SaveToPdf(new()
{ {
FilePath = @"F:\test\test.pdf", FilePath = @"E:\test\test.pdf",
Title = "Текст заголовка", Title = "Текст заголовка",
Tables = [new[,] Tables = [new[,]
{ {
@ -38,5 +43,109 @@
}], }],
}); });
} }
private void button2_Click(object sender, EventArgs e)
{
customPdfTableWithGrouping1.SaveToPdf<TestModel>(new()
{
FilePath = @"C:\test\test.pdf",
Title = "Текст заголовка",
Columns = [
new() {
Header = "1",
PropertyName = nameof(TestModel.Height),
},
new() {
Header = "2",
PropertyName = nameof(TestModel.Name),
},
new() {
Header = "3",
PropertyName = nameof(TestModel.Id),
},
new() {
Header = "4",
PropertyName = nameof(TestModel.Age),
}
],
Rows = [
new() {
Value = new TestModel() {
Id = 1,
Name = "Vasya",
Age = 5,
Height = "low",
},
Height = 100f,
},
new() {
Value = new TestModel() {
Id = 2,
Name = "Masya",
Age = 8,
Height = "hi",
},
Height = 100f,
},
new() {
Value = new TestModel() {
Id = 3,
Name = "Kasya",
Age = 3,
Height = "low",
},
Height = 10f,
},
new() {
Value = new TestModel() {
Id = 4,
Name = "Asya",
Age = 100,
Height = "hi",
},
Height = 50f,
},
],
HeaderHeight = 100f,
});
}
private void button3_Click(object sender, EventArgs e)
{
customPdfHistogram1.SaveToPdf(new()
{
FilePath = @"C:\test\test.pdf",
DocumentTitle = "Текст заголовка",
HistogramTitle = "Заголовок диограммы",
Values = [
new(){
Name = "Линия1",
Points = [
new() { Name = "f3", Value = 2 },
new() { Name = "f4", Value = 3 },
new() { Name = "f5", Value = 5 },
new() { Name = "f6", Value = 8 },
new() { Name = "f7", Value = 13 },
new() { Name = "f8", Value = 21 },
new() { Name = "f9", Value = 34 },
new() { Name = "f10", Value = 55 },
new() { Name = "f11", Value = 89 },
],
},
new(){
Name = "Линия2",
Points = [
new() { Name = "r1", Value = 1 },
new() { Name = "r2", Value = 100 },
new() { Name = "r3", Value = 10 },
new() { Name = "r4", Value = 80 },
new() { Name = "r5", Value = 30 },
new() { Name = "r6", Value = 60 },
new() { Name = "r7", Value = 50 },
],
}
],
});
}
} }
} }

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<root> <root>
<!-- <!--
Microsoft ResX Schema Microsoft ResX Schema
Version 2.0 Version 2.0
@ -48,7 +48,7 @@
value : The object must be serialized with value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding. : and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64 mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
@ -120,4 +120,10 @@
<metadata name="customPdfTable1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"> <metadata name="customPdfTable1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>17, 17</value> <value>17, 17</value>
</metadata> </metadata>
<metadata name="customPdfTableWithGrouping1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>185, 17</value>
</metadata>
<metadata name="customPdfHistogram1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>446, 17</value>
</metadata>
</root> </root>

View File

@ -7,5 +7,7 @@
public string Name { get; set; } = string.Empty; public string Name { get; set; } = string.Empty;
public int Age { get; set; } public int Age { get; set; }
public string Height { get; set; } = string.Empty;
} }
} }