Compare commits

...

2 Commits

Author SHA1 Message Date
nikbel2004@outlook.com
96353d7682 Готовая лабораторная работа 1 2024-10-21 02:38:42 +04:00
nikbel2004@outlook.com
9b7acfad63 Готовая лабораторная работа 1 2024-10-21 02:08:06 +04:00
23 changed files with 1808 additions and 0 deletions

View File

@ -0,0 +1,31 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.9.34728.123
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MyCustomComponents", "MyCustomComponents\MyCustomComponents.csproj", "{33B0240F-25C6-4577-BE81-5381D939E230}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DesktopWithMyVisualComponents", "DesktopWithMyVisualComponents\DesktopWithMyVisualComponents.csproj", "{D784BAED-C48F-46F2-A6A0-BBD6CE599A59}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{33B0240F-25C6-4577-BE81-5381D939E230}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{33B0240F-25C6-4577-BE81-5381D939E230}.Debug|Any CPU.Build.0 = Debug|Any CPU
{33B0240F-25C6-4577-BE81-5381D939E230}.Release|Any CPU.ActiveCfg = Release|Any CPU
{33B0240F-25C6-4577-BE81-5381D939E230}.Release|Any CPU.Build.0 = Release|Any CPU
{D784BAED-C48F-46F2-A6A0-BBD6CE599A59}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D784BAED-C48F-46F2-A6A0-BBD6CE599A59}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D784BAED-C48F-46F2-A6A0-BBD6CE599A59}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D784BAED-C48F-46F2-A6A0-BBD6CE599A59}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {26502496-F9AE-4E02-986A-76FD9595E9AE}
EndGlobalSection
EndGlobal

View File

@ -0,0 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net6.0-windows</TargetFramework>
<Nullable>enable</Nullable>
<UseWindowsForms>true</UseWindowsForms>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\MyCustomComponents\MyCustomComponents.csproj" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,28 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace DesktopWithMyVisualComponents
{
public class Device
{
public int Id { get; set; }
public string DeviceType { get; set; }
public string Model { get; set; }
public string SerialNumber { get; set; }
public Device(string serialNumber, string deviceType, string model)
{
SerialNumber = serialNumber;
Model = model;
DeviceType = deviceType;
}
public Device() { }
}
}

View File

@ -0,0 +1,380 @@
namespace DesktopWithMyVisualComponents
{
partial class FormMain
{
/// <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()
{
customInputRangeNumber = new MyCustomComponents.CustomInputRangeNumber();
buttonCheck = new Button();
labelCheckValue = new Label();
textBoxMin = new TextBox();
textBoxMax = new TextBox();
buttonSetBorders = new Button();
labelMin = new Label();
labelMax = new Label();
labelRange = new Label();
groupBoxInput = new GroupBox();
groupBoxSelected = new GroupBox();
buttonGetSelected = new Button();
labelSelectedValue = new Label();
buttonClear = new Button();
buttonAdd = new Button();
textBoxAdd = new TextBox();
customSelectedCheckedListBoxProperty = new MyCustomComponents.CustomSelectedCheckedListBoxProperty();
groupBoxData = new GroupBox();
labelDeviceType = new Label();
labelModel = new Label();
labelSerialNum = new Label();
buttonGetFromTree = new Button();
buttonAddToTree = new Button();
comboBoxDeviceType = new ComboBox();
textBoxModel = new TextBox();
textBoxSerialNumber = new TextBox();
customTreeView = new MyCustomComponents.CustomTreeView();
groupBoxInput.SuspendLayout();
groupBoxSelected.SuspendLayout();
groupBoxData.SuspendLayout();
SuspendLayout();
//
// customInputRangeNumber
//
customInputRangeNumber.AutoValidate = AutoValidate.Disable;
customInputRangeNumber.CausesValidation = false;
customInputRangeNumber.Location = new Point(30, 19);
customInputRangeNumber.Margin = new Padding(3, 2, 3, 2);
customInputRangeNumber.MaxValue = new decimal(new int[] { -1, -1, -1, 0 });
customInputRangeNumber.MinValue = new decimal(new int[] { -1, -1, -1, int.MinValue });
customInputRangeNumber.Name = "customInputRangeNumber";
customInputRangeNumber.Size = new Size(134, 30);
customInputRangeNumber.TabIndex = 0;
customInputRangeNumber.Value = new decimal(new int[] { 0, 0, 0, 0 });
//
// buttonCheck
//
buttonCheck.Location = new Point(171, 22);
buttonCheck.Name = "buttonCheck";
buttonCheck.Size = new Size(126, 23);
buttonCheck.TabIndex = 1;
buttonCheck.Text = "Check";
buttonCheck.UseVisualStyleBackColor = true;
buttonCheck.Click += buttonCheck_Click;
//
// labelCheckValue
//
labelCheckValue.AutoSize = true;
labelCheckValue.Location = new Point(30, 111);
labelCheckValue.Name = "labelCheckValue";
labelCheckValue.Size = new Size(65, 15);
labelCheckValue.TabIndex = 2;
labelCheckValue.Text = "Enter value";
//
// textBoxMin
//
textBoxMin.Location = new Point(30, 71);
textBoxMin.Name = "textBoxMin";
textBoxMin.Size = new Size(55, 23);
textBoxMin.TabIndex = 3;
textBoxMin.KeyPress += textBoxMin_KeyPress;
//
// textBoxMax
//
textBoxMax.Location = new Point(103, 71);
textBoxMax.Name = "textBoxMax";
textBoxMax.Size = new Size(53, 23);
textBoxMax.TabIndex = 4;
textBoxMax.KeyPress += textBoxMax_KeyPress;
//
// buttonSetBorders
//
buttonSetBorders.Location = new Point(171, 71);
buttonSetBorders.Name = "buttonSetBorders";
buttonSetBorders.Size = new Size(126, 23);
buttonSetBorders.TabIndex = 5;
buttonSetBorders.Text = "Set Borders";
buttonSetBorders.UseVisualStyleBackColor = true;
buttonSetBorders.Click += buttonSetBorders_Click;
//
// labelMin
//
labelMin.AutoSize = true;
labelMin.Location = new Point(29, 54);
labelMin.Name = "labelMin";
labelMin.Size = new Size(56, 15);
labelMin.TabIndex = 6;
labelMin.Text = "MinValue";
//
// labelMax
//
labelMax.AutoSize = true;
labelMax.Location = new Point(101, 54);
labelMax.Name = "labelMax";
labelMax.Size = new Size(58, 15);
labelMax.TabIndex = 7;
labelMax.Text = "MaxValue";
//
// labelRange
//
labelRange.AutoSize = true;
labelRange.Location = new Point(88, 75);
labelRange.Name = "labelRange";
labelRange.Size = new Size(12, 15);
labelRange.TabIndex = 8;
labelRange.Text = "-";
//
// groupBoxInput
//
groupBoxInput.Controls.Add(customInputRangeNumber);
groupBoxInput.Controls.Add(labelCheckValue);
groupBoxInput.Controls.Add(labelRange);
groupBoxInput.Controls.Add(buttonCheck);
groupBoxInput.Controls.Add(labelMax);
groupBoxInput.Controls.Add(textBoxMin);
groupBoxInput.Controls.Add(labelMin);
groupBoxInput.Controls.Add(textBoxMax);
groupBoxInput.Controls.Add(buttonSetBorders);
groupBoxInput.Location = new Point(12, 252);
groupBoxInput.Name = "groupBoxInput";
groupBoxInput.Size = new Size(311, 190);
groupBoxInput.TabIndex = 9;
groupBoxInput.TabStop = false;
groupBoxInput.Text = "Input";
//
// groupBoxSelected
//
groupBoxSelected.Controls.Add(buttonGetSelected);
groupBoxSelected.Controls.Add(labelSelectedValue);
groupBoxSelected.Controls.Add(buttonClear);
groupBoxSelected.Controls.Add(buttonAdd);
groupBoxSelected.Controls.Add(textBoxAdd);
groupBoxSelected.Controls.Add(customSelectedCheckedListBoxProperty);
groupBoxSelected.Location = new Point(340, 253);
groupBoxSelected.Name = "groupBoxSelected";
groupBoxSelected.Size = new Size(311, 190);
groupBoxSelected.TabIndex = 10;
groupBoxSelected.TabStop = false;
groupBoxSelected.Text = "Selected";
//
// buttonGetSelected
//
buttonGetSelected.Location = new Point(192, 144);
buttonGetSelected.Name = "buttonGetSelected";
buttonGetSelected.Size = new Size(100, 23);
buttonGetSelected.TabIndex = 14;
buttonGetSelected.Text = "Get Selected";
buttonGetSelected.UseVisualStyleBackColor = true;
buttonGetSelected.Click += buttonGetSelected_Click;
//
// labelSelectedValue
//
labelSelectedValue.AutoSize = true;
labelSelectedValue.Location = new Point(192, 111);
labelSelectedValue.Name = "labelSelectedValue";
labelSelectedValue.Size = new Size(82, 15);
labelSelectedValue.TabIndex = 11;
labelSelectedValue.Text = "Selected value";
//
// buttonClear
//
buttonClear.Location = new Point(192, 81);
buttonClear.Name = "buttonClear";
buttonClear.Size = new Size(100, 23);
buttonClear.TabIndex = 13;
buttonClear.Text = "Clear";
buttonClear.UseVisualStyleBackColor = true;
buttonClear.Click += buttonClear_Click;
//
// buttonAdd
//
buttonAdd.Location = new Point(192, 52);
buttonAdd.Name = "buttonAdd";
buttonAdd.Size = new Size(100, 23);
buttonAdd.TabIndex = 12;
buttonAdd.Text = "Add or Select";
buttonAdd.UseVisualStyleBackColor = true;
buttonAdd.Click += buttonAdd_Click;
//
// textBoxAdd
//
textBoxAdd.Location = new Point(192, 23);
textBoxAdd.Name = "textBoxAdd";
textBoxAdd.Size = new Size(100, 23);
textBoxAdd.TabIndex = 11;
//
// customSelectedCheckedListBoxProperty
//
customSelectedCheckedListBoxProperty.Location = new Point(36, 19);
customSelectedCheckedListBoxProperty.Margin = new Padding(3, 2, 3, 2);
customSelectedCheckedListBoxProperty.Name = "customSelectedCheckedListBoxProperty";
customSelectedCheckedListBoxProperty.SelectedElement = "";
customSelectedCheckedListBoxProperty.Size = new Size(150, 157);
customSelectedCheckedListBoxProperty.TabIndex = 0;
//
// groupBoxData
//
groupBoxData.Controls.Add(labelDeviceType);
groupBoxData.Controls.Add(labelModel);
groupBoxData.Controls.Add(labelSerialNum);
groupBoxData.Controls.Add(buttonGetFromTree);
groupBoxData.Controls.Add(buttonAddToTree);
groupBoxData.Controls.Add(comboBoxDeviceType);
groupBoxData.Controls.Add(textBoxModel);
groupBoxData.Controls.Add(textBoxSerialNumber);
groupBoxData.Controls.Add(customTreeView);
groupBoxData.Location = new Point(12, 12);
groupBoxData.Name = "groupBoxData";
groupBoxData.Size = new Size(639, 230);
groupBoxData.TabIndex = 11;
groupBoxData.TabStop = false;
groupBoxData.Text = "Data";
//
// labelDeviceType
//
labelDeviceType.AutoSize = true;
labelDeviceType.Location = new Point(432, 116);
labelDeviceType.Name = "labelDeviceType";
labelDeviceType.Size = new Size(91, 15);
labelDeviceType.TabIndex = 8;
labelDeviceType.Text = "Тип устройства";
//
// labelModel
//
labelModel.AutoSize = true;
labelModel.Location = new Point(432, 72);
labelModel.Name = "labelModel";
labelModel.Size = new Size(50, 15);
labelModel.TabIndex = 7;
labelModel.Text = "Модель";
//
// labelSerialNum
//
labelSerialNum.AutoSize = true;
labelSerialNum.Location = new Point(432, 28);
labelSerialNum.Name = "labelSerialNum";
labelSerialNum.Size = new Size(105, 15);
labelSerialNum.TabIndex = 6;
labelSerialNum.Text = "Серия устройства";
//
// buttonGetFromTree
//
buttonGetFromTree.Location = new Point(432, 195);
buttonGetFromTree.Name = "buttonGetFromTree";
buttonGetFromTree.Size = new Size(188, 23);
buttonGetFromTree.TabIndex = 5;
buttonGetFromTree.Text = "Get Selected";
buttonGetFromTree.UseVisualStyleBackColor = true;
buttonGetFromTree.Click += buttonGetFromTree_Click;
//
// buttonAddToTree
//
buttonAddToTree.Location = new Point(432, 166);
buttonAddToTree.Name = "buttonAddToTree";
buttonAddToTree.Size = new Size(188, 23);
buttonAddToTree.TabIndex = 4;
buttonAddToTree.Text = "Add";
buttonAddToTree.UseVisualStyleBackColor = true;
buttonAddToTree.Click += buttonAddToTree_Click;
//
// comboBoxDeviceType
//
comboBoxDeviceType.FormattingEnabled = true;
comboBoxDeviceType.Location = new Point(432, 134);
comboBoxDeviceType.Name = "comboBoxDeviceType";
comboBoxDeviceType.Size = new Size(188, 23);
comboBoxDeviceType.TabIndex = 3;
//
// textBoxModel
//
textBoxModel.Location = new Point(432, 90);
textBoxModel.Name = "textBoxModel";
textBoxModel.Size = new Size(188, 23);
textBoxModel.TabIndex = 2;
//
// textBoxSerialNumber
//
textBoxSerialNumber.Location = new Point(432, 46);
textBoxSerialNumber.Name = "textBoxSerialNumber";
textBoxSerialNumber.Size = new Size(188, 23);
textBoxSerialNumber.TabIndex = 1;
//
// customTreeView
//
customTreeView.Location = new Point(15, 22);
customTreeView.Margin = new Padding(3, 2, 3, 2);
customTreeView.Name = "customTreeView";
customTreeView.Size = new Size(398, 202);
customTreeView.TabIndex = 0;
//
// FormMain
//
AutoScaleDimensions = new SizeF(7F, 15F);
AutoScaleMode = AutoScaleMode.Font;
ClientSize = new Size(667, 450);
Controls.Add(groupBoxData);
Controls.Add(groupBoxSelected);
Controls.Add(groupBoxInput);
Name = "FormMain";
Text = "FormMain";
groupBoxInput.ResumeLayout(false);
groupBoxInput.PerformLayout();
groupBoxSelected.ResumeLayout(false);
groupBoxSelected.PerformLayout();
groupBoxData.ResumeLayout(false);
groupBoxData.PerformLayout();
ResumeLayout(false);
}
#endregion
private MyCustomComponents.CustomInputRangeNumber customInputRangeNumber;
private Button buttonCheck;
private Label labelCheckValue;
private TextBox textBoxMin;
private TextBox textBoxMax;
private Button buttonSetBorders;
private Label labelMin;
private Label labelMax;
private Label labelRange;
private GroupBox groupBoxInput;
private GroupBox groupBoxSelected;
private Button buttonGetSelected;
private Label labelSelectedValue;
private Button buttonClear;
private Button buttonAdd;
private TextBox textBoxAdd;
private MyCustomComponents.CustomSelectedCheckedListBoxProperty customSelectedCheckedListBoxProperty;
private GroupBox groupBoxData;
private MyCustomComponents.CustomTreeView customTreeView;
private Button buttonGetFromTree;
private Button buttonAddToTree;
private ComboBox comboBoxDeviceType;
private TextBox textBoxModel;
private TextBox textBoxSerialNumber;
private Label labelDeviceType;
private Label labelModel;
private Label labelSerialNum;
}
}

View File

@ -0,0 +1,186 @@
using MyCustomComponents;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace DesktopWithMyVisualComponents
{
public partial class FormMain : Form
{
readonly List<Device> devices = new()
{
new Device { Id = 1, SerialNumber = "SM-12345", DeviceType = "Мобильный телефон", Model = "IPhone 13" },
new Device { Id = 2, SerialNumber = "SM-G3412", DeviceType = "Мобильный телефон", Model = "Samsung Galaxy S24" },
new Device { Id = 3, SerialNumber = "CO2UD8471", DeviceType = "Ноутбук", Model = "MacBook Pro" },
new Device { Id = 4, SerialNumber = "R80NZD8812", DeviceType = "Умные часы", Model = "Galaxy Watch 4" },
new Device { Id = 5, SerialNumber = "FN738214", DeviceType = "Умные часы", Model = "Apple Watch 3", },
};
public FormMain()
{
InitializeComponent();
// Пример для компонента
var list = new List<string>() { "Значение 1", "Значение 2", "Значение 3", "Значение 4", "Значение 5" };
customSelectedCheckedListBoxProperty.Items.AddRange(list.ToArray());
comboBoxDeviceType.Items.Add("Мобильный телефон");
comboBoxDeviceType.Items.Add("Ноутбук");
comboBoxDeviceType.Items.Add("Умные часы");
// Загрузка дерева с девайсами
LoadTree();
// Присоединить обработчик события при изменении значения
customInputRangeNumber.ChangeEvent += CustomInputRangeNumber_ChangeEvent;
}
// Загрузка дерево с иерархией устройств на основе типа устройства, модели и серийного номера
public void LoadTree()
{
// Очистите существующие узлы перед загрузкой новых
customTreeView.Clear();
customTreeView.hierarchy = new List<string> { "DeviceType", "Model", "SerialNumber" };
foreach (Device device in devices)
{
customTreeView.AddNode(device, "SerialNumber");
}
}
// Вынесенная логика проверки значения
private void UpdateLabelWithValue()
{
labelCheckValue.Text = customInputRangeNumber.Value.ToString();
if (string.IsNullOrEmpty(labelCheckValue.Text))
{
labelCheckValue.Text = customInputRangeNumber.Error;
}
}
// Добавляем метод для обработки изменения значения
private void CustomInputRangeNumber_ChangeEvent(object sender, EventArgs e)
{
UpdateLabelWithValue();
}
// Метод проверки значения в Input
private void buttonCheck_Click(object sender, EventArgs e)
{
UpdateLabelWithValue();
}
// Установка границ
private void buttonSetBorders_Click(object sender, EventArgs e)
{
if (decimal.TryParse(textBoxMin.Text, out decimal minValue) && decimal.TryParse(textBoxMax.Text, out decimal maxValue))
{
// Проверка: MaxValue должно быть больше MinValue
if (maxValue <= minValue)
{
labelCheckValue.Text = "Ошибка: MaxValue должно быть больше MinValue.";
return;
}
// Устанавливаем границы
customInputRangeNumber.MinValue = minValue;
customInputRangeNumber.MaxValue = maxValue;
labelCheckValue.Text = "Границы установлены";
// Проверим текущее значение компонента на соответствие новому диапазону
/*try
{
var currentValue = customInputRangeNumber.Value;
// Если значение в пределах, выводим сообщение об успехе
labelCheckValue.Text = "Границы установлены. Текущее значение в пределах диапазона.";
}
catch (ArgumentOutOfRangeException ex)
{
// Если текущее значение вне диапазона, выводим ошибку
labelCheckValue.Text = $"Ошибка: {ex.Message}";
}*/
}
else
{
labelCheckValue.Text = "Ошибка: неверные значения границ";
}
}
private void textBoxMin_KeyPress(object sender, KeyPressEventArgs e)
{
char ch = e.KeyChar;
if (!Char.IsDigit(ch) && ch != 8 && ch != 45)
{
e.Handled = true;
}
}
private void textBoxMax_KeyPress(object sender, KeyPressEventArgs e)
{
char ch = e.KeyChar;
if (!Char.IsDigit(ch) && ch != 8 && ch != 45)
{
e.Handled = true;
}
}
private void buttonAdd_Click(object sender, EventArgs e)
{
if (textBoxAdd.Text != "" && !customSelectedCheckedListBoxProperty.Items.Contains(textBoxAdd.Text))
customSelectedCheckedListBoxProperty.Items.Add(textBoxAdd.Text);
else if (customSelectedCheckedListBoxProperty.Items.Contains(textBoxAdd.Text))
customSelectedCheckedListBoxProperty.SelectedElement = textBoxAdd.Text;
}
private void buttonClear_Click(object sender, EventArgs e)
{
customSelectedCheckedListBoxProperty.Clear();
}
private void buttonGetSelected_Click(object sender, EventArgs e)
{
labelSelectedValue.Text = customSelectedCheckedListBoxProperty.SelectedElement;
if (string.IsNullOrEmpty(labelSelectedValue.Text))
{
labelSelectedValue.Text = "Значение \nне выбрано";
}
}
// Добавление нового узла в дерево
private void buttonAddToTree_Click(object sender, EventArgs e)
{
Device device = new Device
{
SerialNumber = textBoxSerialNumber.Text,
Model = textBoxModel.Text,
DeviceType = comboBoxDeviceType.SelectedItem?.ToString()
};
customTreeView.AddNode(device, "SerialNumber");
}
// Получение данных выбранного узла из дерева
private void buttonGetFromTree_Click(object sender, EventArgs e)
{
Device? device = customTreeView.GetSelectedNode<Device>();
if (device == null)
{
return;
}
textBoxSerialNumber.Text = device.SerialNumber;
textBoxModel.Text = device.Model;
comboBoxDeviceType.SelectedItem = device.DeviceType;
}
}
}

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,17 @@
namespace DesktopWithMyVisualComponents
{
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();
Application.Run(new FormMain());
}
}
}

View File

@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MyCustomComponents.Attributes
{
public class AlwaysCreateAttribute : Attribute
{
public AlwaysCreateAttribute() { }
}
}

View File

@ -0,0 +1,59 @@
namespace MyCustomComponents
{
partial class CustomInputRangeNumber
{
/// <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()
{
numericUpDown = new NumericUpDown();
((System.ComponentModel.ISupportInitialize)numericUpDown).BeginInit();
SuspendLayout();
//
// numericUpDown
//
numericUpDown.Location = new Point(3, 2);
numericUpDown.Margin = new Padding(3, 2, 3, 2);
numericUpDown.Name = "numericUpDown";
numericUpDown.Size = new Size(131, 23);
numericUpDown.TabIndex = 0;
//
// CustomInputRangeNumber
//
AutoScaleDimensions = new SizeF(7F, 15F);
AutoScaleMode = AutoScaleMode.Font;
Controls.Add(numericUpDown);
Margin = new Padding(3, 2, 3, 2);
Name = "CustomInputRangeNumber";
Size = new Size(141, 27);
((System.ComponentModel.ISupportInitialize)numericUpDown).EndInit();
ResumeLayout(false);
}
#endregion
private NumericUpDown numericUpDown;
}
}

View File

@ -0,0 +1,122 @@
using MyCustomComponents.Extensions;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace MyCustomComponents
{
public partial class CustomInputRangeNumber : UserControl
{
// Диапазон
private string example = "Введите значение от " + decimal.MinValue + " до " + decimal.MaxValue;
// В случае ошибки
public string Error { get; protected set; } = string.Empty;
// Создам 2 публичных поля для настройки границ диапазона
public decimal? MinValue { get; set; } = null;
public decimal? MaxValue { get; set; } = null;
/* Публичное свойство для установки и получения введённого значения (set, get). При получении проводится проверка,
Если введённое значение не входит в диапозон, возвращать значение null, а в отдельное поле выводить текст ошибки.
При установке должна проводиться проверка, если передаваемое значение не входит в диапозон, то не заполнять поле компонента.
*/
public decimal? Value
{
get
{
// Как мы проверим что программист не забыл задать минимальное, максимальное значение?
// Проверка, что MinValue и MaxValue задан
if (!MinValue.HasValue || !MaxValue.HasValue)
{
throw new RangeException("MinValue и MaxValue должны быть заданы.");
}
// Проверяем, что max не может быть min
if (MaxValue.Value <= MinValue.Value)
{
throw new RangeException("MaxValue должно быть больше MinValue");
}
decimal currentValue = numericUpDown.Value;
// Проверка, что значение в диапазоне
if (currentValue < MinValue.Value || currentValue > MaxValue.Value)
{
throw new RangeException($"Введённое значение {currentValue} лежит вне диапазона {MinValue.Value} - {MaxValue.Value}.");
}
return currentValue;
}
set
{
// Проверка, что MinValue и MaxValue заданы
if (!MinValue.HasValue || !MaxValue.HasValue)
{
Error = "MinValue и MaxValue должны быть заданы.";
return;
}
// Проверка, что MinValue меньше MaxValue
if (MinValue.Value >= MaxValue.Value)
{
Error = "Диапазон неверен. MinValue должен быть меньше, чем MaxValue.";
return;
}
// Если число верно в диапозоне, то значение устанавливается
if (value.HasValue && value.Value >= MinValue.Value && value.Value <= MaxValue.Value)
{
numericUpDown.Value = value.Value;
// Очистить ошибку, если значение установлено успешно
Error = null;
}
else
{
Error = $"Значение должно быть в диапазоне от {MinValue.Value} до {MaxValue.Value}.";
}
}
}
public CustomInputRangeNumber()
{
InitializeComponent();
}
private void numericUpDown_Enter(object sender, EventArgs e)
{
ToolTip toolTip = new ToolTip();
toolTip.Show(example, numericUpDown, 30, -20, 1000);
}
private void numericUpDown_ValueChanged(object sender, EventArgs e)
{
_changeEvent?.Invoke(sender, e);
}
private EventHandler _changeEvent;
// Событие, вызываемое при смене значения
public event EventHandler ChangeEvent
{
add
{
_changeEvent += value;
}
remove
{
_changeEvent -= value;
}
}
}
}

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,56 @@
namespace MyCustomComponents
{
partial class CustomSelectedCheckedListBoxProperty
{
/// <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()
{
checkedListBox = new CheckedListBox();
SuspendLayout();
//
// checkedListBox
//
checkedListBox.FormattingEnabled = true;
checkedListBox.Location = new Point(4, 4);
checkedListBox.Name = "checkedListBox";
checkedListBox.Size = new Size(164, 180);
checkedListBox.TabIndex = 0;
//
// CustomSelectedCheckedListBoxProperty
//
AutoScaleDimensions = new SizeF(8F, 20F);
AutoScaleMode = AutoScaleMode.Font;
Controls.Add(checkedListBox);
Name = "CustomSelectedCheckedListBoxProperty";
Size = new Size(171, 209);
ResumeLayout(false);
}
#endregion
private CheckedListBox checkedListBox;
}
}

View File

@ -0,0 +1,89 @@
using MyCustomComponents.Extensions;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace MyCustomComponents
{
public partial class CustomSelectedCheckedListBoxProperty : UserControl
{
/* Публичное свойство, которое передаёт ссылку на свойство Items
* элемента ComboBox, через которое и идёт заполнение */
public CheckedListBox.ObjectCollection Items => checkedListBox.Items;
public CustomSelectedCheckedListBoxProperty()
{
InitializeComponent();
}
// Отдельный публичный метод очистки списка
public void Clear()
{
checkedListBox.Items.Clear();
}
private EventHandler _changeEvent;
private void checkedListBox_SelectedIndexChanged(object sender, EventArgs e)
{
_changeEvent?.Invoke(sender, e);
}
// Событие, вызываемое при смене значения в CheckedListBox
public event EventHandler Changed
{
add
{
_changeEvent += value;
}
remove
{
_changeEvent -= value;
}
}
// Публичное свойство (set, get) для установки и получения выбранного значения (возвращает пустую строку, если нет выбранного значения)
public string SelectedElement
{
get
{
return (checkedListBox.SelectedIndex > -1 && checkedListBox.GetItemChecked(checkedListBox.SelectedIndex)) ? checkedListBox.SelectedIndex.ToString() : string.Empty;
}
set
{
if (!string.IsNullOrEmpty(value))
{
int index = checkedListBox.Items.IndexOf(value);
// Если попытаться установить несуществующий элемент
if (index == -1)
{
// Выбрасываем исключение, если элемент не найден в списке
throw new InvalidSelectedElementException($"Элемент '{value}' не найден в списке.");
}
checkedListBox.SelectedItem = value;
checkedListBox.SetItemCheckState(checkedListBox.SelectedIndex, CheckState.Checked);
}
}
}
private void checkedListBox_ItemCheck(object sender, ItemCheckEventArgs e)
{
if (e.NewValue == CheckState.Checked && checkedListBox.CheckedItems.Count > 0)
{
checkedListBox.ItemCheck -= checkedListBox_ItemCheck;
checkedListBox.SetItemChecked(checkedListBox.CheckedIndices[0], value: false);
checkedListBox.ItemCheck += checkedListBox_ItemCheck;
}
}
}
}

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,55 @@
namespace MyCustomComponents
{
partial class CustomTreeView
{
/// <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()
{
treeView = new TreeView();
SuspendLayout();
//
// treeView
//
treeView.Location = new Point(3, 3);
treeView.Name = "treeView";
treeView.Size = new Size(517, 259);
treeView.TabIndex = 0;
//
// CustomTreeCell
//
AutoScaleDimensions = new SizeF(8F, 20F);
AutoScaleMode = AutoScaleMode.Font;
Controls.Add(treeView);
Name = "CustomTreeCell";
Size = new Size(525, 269);
ResumeLayout(false);
}
#endregion
private TreeView treeView;
}
}

View File

@ -0,0 +1,168 @@
using MyCustomComponents.Extensions;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using static System.Windows.Forms.VisualStyles.VisualStyleElement.Button;
using static System.Windows.Forms.VisualStyles.VisualStyleElement;
using MyCustomComponents.Attributes;
using System.Xml.Linq;
namespace MyCustomComponents
{
public partial class CustomTreeView : UserControl
{
public CustomTreeView()
{
InitializeComponent();
}
// Свойство для получения и установки выбранного узла в TreeView
public int SelectedTreeNode
{
get
{
if (treeView.SelectedNode != null)
{
return treeView.SelectedNode.Index;
}
return -1;
}
set
{
if (treeView.Nodes.Count > 0 && value >= 0 && value < treeView.Nodes.Count)
{
treeView.SelectedNode = treeView.Nodes[value];
}
else
{
throw new ArgumentOutOfRangeException(nameof(value), $"Индекс {value} выходит за пределы допустимого диапазона. Допустимый диапазон: от 0 до {treeView.Nodes.Count - 1}.");
}
}
}
public List<string>? hierarchy { get; set; }
private Dictionary<string, bool> newBranch { get; set; } = new Dictionary<string, bool>();
// Отдельный публичный метод очистки всех узлов дерева
public void Clear()
{
treeView.Nodes.Clear();
}
// Публичный метод для получения выбранной записи из древовидной структуры
public T GetSelectedNode<T>() where T : class, new()
{
if (hierarchy == null)
{
throw new HierarchyNullException("Hierarchy is null");
}
if (treeView.SelectedNode == null)
{
throw new InvalidSelectedElementException("TreeView null");
}
// Проверка, является ли выбранный узел корневым
if (treeView.SelectedNode.Parent == null)
{
// Выбран корневой элемент — возвращаем пустой объект
throw new InvalidSelectedElementException("Parent is null");
}
// Если узел выбран и существует, вызываем приватный метод для получения данных узла
return _getNode<T>();
}
// Приватный метод, идущий по узлам вверх (по иерархии)
private T _getNode<T>() where T : new()
{
TreeNode? node = treeView.SelectedNode;
var obj = new T();
int level = hierarchy.Count - 1;
// Проходим по иерархии от нижнего уровня к верхнему
while (node != null && level >= 0)
{
var property = hierarchy[level];
obj.GetType().GetProperty(property)?.SetValue(obj, node.Text);
node = node.Parent;
level--;
}
return obj;
}
/* Параметризированный метод, у которого в передаваемых параметрах
* идёт объект какого-то класса и имя свойства/поля, до которого согласно
* иерархии будет следовать формирование ветви
*/
public void AddNode<T>(T obj, string propertyName)
{
if (hierarchy == null)
{
throw new HierarchyNullException("Hierarchy is null");
}
if (obj == null)
{
throw new ArgumentNullException("Added object is null");
}
// Получаем первый узел в дереве
TreeNode currentNode = null;
// Проходимся по иерархии
foreach (var property in hierarchy)
{
// Получаем значение свойства
var value = obj.GetType().GetProperty(property)?.GetValue(obj, null)?.ToString();
bool createNewBranch = newBranch != null && newBranch.ContainsKey(propertyName) && newBranch[propertyName];
if (currentNode == null)
{
currentNode = treeView.Nodes.Cast<TreeNode>().FirstOrDefault(n => n.Text == value)
?? treeView.Nodes.Add(value);
}
else
{
var childNode = currentNode.Nodes.Cast<TreeNode>().FirstOrDefault(n => n.Text == value);
// Проверка нужно ли нам создавать дочерний узел
if (childNode == null || createNewBranch)
{
childNode = currentNode.Nodes.Add(value);
}
// Переходим на уровень этого дочернего узла
currentNode = childNode;
}
if (property == propertyName)
{
break;
}
}
}
public void SetHierarchy(List<string> hierarchy, Dictionary<string, bool> newBranch)
{
this.hierarchy = hierarchy;
this.newBranch = newBranch;
}
}
}

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,18 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MyCustomComponents.Extensions
{
public class HierarchyNullException : Exception
{
public HierarchyNullException() { }
public HierarchyNullException(string message) : base(message) { }
public HierarchyNullException(string message, Exception inner) : base(message, inner) { }
}
}

View File

@ -0,0 +1,22 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MyCustomComponents.Extensions
{
// Исключение, если нет выбранного и отмеченного элемента
public class InvalidSelectedElementException : Exception
{
public InvalidSelectedElementException() { }
public InvalidSelectedElementException(string message) : base(message)
{
}
public InvalidSelectedElementException(string message, Exception inner) : base(message, inner)
{
}
}
}

View File

@ -0,0 +1,17 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MyCustomComponents.Extensions
{
public class PropertyNullException : Exception
{
public PropertyNullException() { }
public PropertyNullException(string message) : base(message) { }
public PropertyNullException(string message, Exception inner) : base(message, inner) { }
}
}

View File

@ -0,0 +1,20 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MyCustomComponents.Extensions
{
internal class RangeException : Exception
{
public RangeException() { }
public RangeException(string message) : base(message)
{ }
public RangeException(string message, Exception inner) : base(message, inner)
{
}
}
}

View File

@ -0,0 +1,22 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MyCustomComponents.Extensions
{
// Ошибка неверного значения, когда значение выходит за границы диапазона
public class ValueOutOfRangeException : Exception
{
public ValueOutOfRangeException() { }
public ValueOutOfRangeException(string message) : base(message)
{
}
public ValueOutOfRangeException(string message, Exception inner) : base(message, inner)
{
}
}
}

View File

@ -0,0 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0-windows</TargetFramework>
<Nullable>enable</Nullable>
<UseWindowsForms>true</UseWindowsForms>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
</Project>