diff --git a/Pf2eViewer/CoreBindingNavigator.cs b/Pf2eViewer/CoreBindingNavigator.cs
index c00659b..dc1f2e2 100644
--- a/Pf2eViewer/CoreBindingNavigator.cs
+++ b/Pf2eViewer/CoreBindingNavigator.cs
@@ -1,5 +1,8 @@
namespace Pf2eViewer;
+///
+/// Элемент управления прокрутки страниц
+///
internal sealed class CoreBindingNavigator : BindingNavigator
{
public CoreBindingNavigator()
diff --git a/Pf2eViewer/DatabaseViewer/DatabaseQueryPerformer.cs b/Pf2eViewer/DatabaseViewer/DatabaseQueryPerformer.cs
index e9cca11..3623ce1 100644
--- a/Pf2eViewer/DatabaseViewer/DatabaseQueryPerformer.cs
+++ b/Pf2eViewer/DatabaseViewer/DatabaseQueryPerformer.cs
@@ -5,33 +5,63 @@ using Pf2eModel.Model.Enum;
namespace Pf2eViewer.DatabaseViewer;
+///
+/// Класс, выполняющий страничные запросы к базе данных.
+///
internal class DatabaseQueryPerformer
{
+ ///
+ /// Тип сущностей, которую необходимо получить.
+ ///
public required EntityType Type;
+ ///
+ /// Номер страницы, которую необходимо получить.
+ ///
public required int Page;
+ ///
+ /// Количество элементов на странице.
+ ///
public required int PerPage;
+ ///
+ /// Поисковая строка, по которой будет производиться фильтрация.
+ ///
public required string Search = "";
+ ///
+ /// Поток, в котором будет выполняться запрос к базе данных.
+ ///
private readonly Thread _queryThread;
+ ///
+ /// Конструктор класса, который инициализирует поток для выполнения запроса к базе данных.
+ ///
public DatabaseQueryPerformer()
{
_queryThread = new(PerformQuery);
}
+ ///
+ /// Запускает поток для выполнения запроса к базе данных.
+ ///
public void Query()
{
_queryThread.Start();
}
+ ///
+ /// Прерывает выполнение потока, если он ещё не завершён.
+ ///
public void Abort()
{
_queryThread.Interrupt();
}
+ ///
+ /// Выполняет запрос к базе данных и обрабатывает результаты.
+ ///
private void PerformQuery()
{
try
@@ -65,16 +95,36 @@ internal class DatabaseQueryPerformer
}
}
+ ///
+ /// Делегат, который будет вызван по завершении выполнения запроса к базе данных.
+ ///
+ /// Объект класса, выполневшего запрос.
+ /// Результат запроса.
public delegate void DatabaseQueryPerformerDelegate(
DatabaseQueryPerformer sender,
DatabaseQueryResult result
);
+
+ ///
+ /// Событие, которое будет вызвано по завершении выполнения запроса к базе данных.
+ ///
public event DatabaseQueryPerformerDelegate? OnQueryCompleted;
}
+///
+/// Класс, который содержит результат выполнения запроса к базе данных.
+///
+/// Общее количество найденных сущностей.
+/// Набор найденных сущностей.
internal class DatabaseQueryResult(int count, ICollection entities)
{
+ ///
+ /// Общее количество найденных сущностей.
+ ///
public int Count { get; } = count;
+ ///
+ /// Набор найденных сущностей.
+ ///
public ConcurrentBag Entities { get; } = new(entities);
}
diff --git a/Pf2eViewer/DatabaseViewer/DatabaseViewerControl.cs b/Pf2eViewer/DatabaseViewer/DatabaseViewerControl.cs
index e589ee5..d056a03 100644
--- a/Pf2eViewer/DatabaseViewer/DatabaseViewerControl.cs
+++ b/Pf2eViewer/DatabaseViewer/DatabaseViewerControl.cs
@@ -8,10 +8,21 @@ using Timer = System.Windows.Forms.Timer;
namespace Pf2eViewer.DatabaseViewer;
+///
+/// Класс элемента управления, который отображает сущности в виде таблицы.
+///
public partial class DatabaseViewerControl : UserControl
{
+ #region "Поля и свойства"
+
+ ///
+ /// Контекст базы данных, который используется для получения сущностей.
+ ///
public readonly Pf2eDbContext Context = new();
+ ///
+ /// Количество элементов на странице.
+ ///
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public int PerPage
{
@@ -24,6 +35,9 @@ public partial class DatabaseViewerControl : UserControl
}
private int _perPage = 30;
+ ///
+ /// Номер страницы, которую необходимо отобразить.
+ ///
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public int Page
{
@@ -36,6 +50,9 @@ public partial class DatabaseViewerControl : UserControl
}
private int _page = 1;
+ ///
+ /// Тип сущности, которую необходимо отобразить.
+ ///
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public EntityType Type
{
@@ -49,23 +66,49 @@ public partial class DatabaseViewerControl : UserControl
}
private EntityType _type;
+ ///
+ /// Количество элементов, которые были получены из базы данных.
+ ///
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public int Count { get; private set; }
+ ///
+ /// Номер последней страницы, которая может быть отображена.
+ ///
public int MaxPage => Convert.ToInt32(Math.Ceiling(1.0 * Count / PerPage));
+ ///
+ /// Выбранная в элементе управления сущность.
+ ///
[AllowNull]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public EntityView CurrentEntity { get; private set; }
+ ///
+ /// Флаг, указывающий, что элемент управления занят выполнением запроса к базе данных.
+ ///
private bool _isBusy = true;
+ ///
+ /// Объект, который выполняет запрос к базе данных.
+ ///
private DatabaseQueryPerformer? _performer;
+ ///
+ /// Таймер, который используется для задержки обработки события изменения текста в поле поиска.
+ ///
private Timer? _typingTimer;
+ ///
+ /// Строка, которая хранит последнее значение, введенное в поле поиска.
+ ///
private string _lastSearch = "";
+ #endregion
+
+ ///
+ /// Конструктор класса, который инициализирует элемент управления и связывает его с источником данных.
+ ///
public DatabaseViewerControl()
{
InitializeComponent();
@@ -77,8 +120,13 @@ public partial class DatabaseViewerControl : UserControl
_isBusy = false;
}
- // Control events
+ #region "События элемента управления"
+ ///
+ /// Обработчик события, который вызывается при изменении текущей страницы в элементе управления.
+ ///
+ /// Источник события.
+ /// Объект, содержащий данные события.
private void BindingNavigator_RefreshItems(object sender, EventArgs e)
{
if (_isBusy)
@@ -90,6 +138,11 @@ public partial class DatabaseViewerControl : UserControl
UpdateView();
}
+ ///
+ /// Обработчик события, который вызывается при изменении выделенной строки в элементе управления DataGridView.
+ ///
+ /// Источник события.
+ /// Объект, содержащий данные события.
private void DataGridView_RowStateChanged(object sender, DataGridViewRowStateChangedEventArgs e)
{
if (_isBusy)
@@ -100,6 +153,11 @@ public partial class DatabaseViewerControl : UserControl
UpdateEntity();
}
+ ///
+ /// Обработчик события, который вызывается когда поле поиска теряет фокус.
+ ///
+ /// Источник события.
+ /// Объект, содержащий данные события.
private void SearchTextBox_Leave(object sender, EventArgs e)
{
if (_isBusy || SearchTextBox.Text == _lastSearch)
@@ -110,6 +168,11 @@ public partial class DatabaseViewerControl : UserControl
UpdateView();
}
+ ///
+ /// Обработчик события, который вызывается при изменении текста в поле поиска.
+ ///
+ /// Источник события.
+ /// Объект, содержащий данные события.
private void SearchTextBox_TextChanged(object sender, EventArgs e)
{
if (_isBusy)
@@ -128,6 +191,11 @@ public partial class DatabaseViewerControl : UserControl
_typingTimer.Start();
}
+ ///
+ /// Обработчик события, который вызывается при истечении времени таймера ввода текста.
+ ///
+ /// Источник события.
+ /// Объект, содержащий данные события.
private void HandleTypingTimerTimeout(object? sender, EventArgs e)
{
if (_isBusy || sender is not Timer timer)
@@ -139,8 +207,14 @@ public partial class DatabaseViewerControl : UserControl
timer.Stop();
}
- // Updater
+ #endregion
+ #region "Обновление данных"
+
+ ///
+ /// Устанавливает статус занятости элемента управления.
+ ///
+ /// Новый статус занятости.
private void SetBusyStatus(bool busy)
{
if (InvokeRequired)
@@ -160,6 +234,10 @@ public partial class DatabaseViewerControl : UserControl
: DataGridViewColumnHeadersHeightSizeMode.DisableResizing;
}
+ ///
+ /// Обновляет данные в элементе управления.
+ ///
+ /// Проигнорировать флаг занятости.
public void UpdateView(bool force = false)
{
if (_isBusy && !force)
@@ -184,6 +262,11 @@ public partial class DatabaseViewerControl : UserControl
_performer.Query();
}
+ ///
+ /// Обработчик события, который вызывается по завершении выполнения запроса к базе данных.
+ ///
+ /// Источник события.
+ /// Результат выполнения события
private void HandleQueryCompleted(DatabaseQueryPerformer sender, DatabaseQueryResult result)
{
if (sender != _performer)
@@ -223,6 +306,12 @@ public partial class DatabaseViewerControl : UserControl
);
}
+ ///
+ /// Вызывает действие на указанном элементе управления в нужном потоке , если это необходимо.
+ ///
+ ///
+ ///
+ ///
private void InvokeControl(T control, Action action)
where T : Control
{
@@ -236,6 +325,9 @@ public partial class DatabaseViewerControl : UserControl
}
}
+ ///
+ /// Обновить сущность, которая была выбрана в элементе управления DataGridView.
+ ///
private void UpdateEntity()
{
if (DataGridView.SelectedRows.Count == 0)
@@ -257,18 +349,42 @@ public partial class DatabaseViewerControl : UserControl
EntityChange?.Invoke(CurrentEntity);
}
- // Output events
+ #endregion
+ #region Output events
+
+ ///
+ /// Делегат, который будет вызван при изменении выбранной сущности.
+ ///
+ /// Новая выбранная сущность.
public delegate void EntityChangeHandler(EntityView newEntity);
+ ///
+ /// Событие, которое вызывается при изменении выбранной сущности.
+ ///
public event EntityChangeHandler? EntityChange;
- internal class EntityGridRow(Entity e)
+ #endregion
+
+ ///
+ /// Класс, который представляет строку в таблице сущностей.
+ ///
+ /// Сущность, связанная со строкой
+ internal class EntityGridRow(Entity entity)
{
- public int Id { get; set; } = e.Id;
+ ///
+ /// Идентификатор сущности.
+ ///
+ public int Id { get; set; } = entity.Id;
- public string Name { get; set; } = e.Name[LocaleType.English];
+ ///
+ /// Название сущности.
+ ///
+ public string Name { get; set; } = entity.Name[LocaleType.English];
- public EntityRarity Rarity { get; set; } = e.Rarity;
+ ///
+ /// Редкость сущности.
+ ///
+ public EntityRarity Rarity { get; set; } = entity.Rarity;
}
}
diff --git a/Pf2eViewer/EntityTreeViewer/EntityTreeViewerControl.cs b/Pf2eViewer/EntityTreeViewer/EntityTreeViewerControl.cs
index 1e6d234..daef077 100644
--- a/Pf2eViewer/EntityTreeViewer/EntityTreeViewerControl.cs
+++ b/Pf2eViewer/EntityTreeViewer/EntityTreeViewerControl.cs
@@ -1,10 +1,17 @@
using System.ComponentModel;
-using System.Diagnostics;
namespace Pf2eViewer.EntityTreeViewer;
+///
+/// Элемент управления для отображения структуры сущности.
+///
public sealed partial class EntityTreeViewerControl : UserControl
{
+ #region "Поля элемента управления"
+
+ ///
+ /// Высота строки поля структуры.
+ ///
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
[Category("Entity Render")]
[Description("The height of each item in the tree view.")]
@@ -19,6 +26,9 @@ public sealed partial class EntityTreeViewerControl : UserControl
}
private int _itemHeight = 20;
+ ///
+ /// Сущность, которая будет отображаться в элементе управления.
+ ///
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
[Browsable(false)]
[EditorBrowsable(EditorBrowsableState.Never)]
@@ -33,6 +43,9 @@ public sealed partial class EntityTreeViewerControl : UserControl
}
private EntityView? _entityView;
+ ///
+ /// Ширина отступа глубины.
+ ///
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
[Category("Entity Render")]
[Description("The size of tab.")]
@@ -47,6 +60,9 @@ public sealed partial class EntityTreeViewerControl : UserControl
}
private int _depthSize = 20;
+ ///
+ /// Размер шрифта типа поля сущности.
+ ///
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
[Category("Entity Render")]
[Description("The size of tab.")]
@@ -61,6 +77,9 @@ public sealed partial class EntityTreeViewerControl : UserControl
}
private float _typeFontSize = 9.5f;
+ ///
+ /// Размер кнопки плюс/минус.
+ ///
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
[Category("Entity Render")]
[Description("The size of plus/minus button.")]
@@ -75,11 +94,17 @@ public sealed partial class EntityTreeViewerControl : UserControl
}
private int _buttonSize = 20;
+ ///
+ /// Ширина имени поля сущности.
+ ///
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
[Browsable(false)]
[EditorBrowsable(EditorBrowsableState.Never)]
public int NameWidth { get; private set; } = 200;
+ ///
+ /// Цвет фона строки поля при наведении на элемент управления.
+ ///
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
[Category("Entity Render")]
[Description("Color on item hover.")]
@@ -94,21 +119,50 @@ public sealed partial class EntityTreeViewerControl : UserControl
}
private Color _hoverColor = SystemColors.ControlDark;
- [Browsable(false)]
- [EditorBrowsable(EditorBrowsableState.Never)]
- public int ContentWidth => Width - SystemInformation.VerticalScrollBarWidth - 2;
-
- private readonly List _rows = [];
-
- private List _viewerRows = [];
-
- private bool _rendering;
-
+ ///
+ /// Изображение, используемое для отображения содержимого элемента управления.
+ ///
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
[Browsable(false)]
[EditorBrowsable(EditorBrowsableState.Never)]
public Bitmap ContentImage { get; set; }
+ #endregion
+
+ #region "Вычисляемые поля"
+
+ ///
+ /// Ширина области содержимого элемента управления.
+ ///
+ [Browsable(false)]
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public int ContentWidth => Width - SystemInformation.VerticalScrollBarWidth - 2;
+
+ #endregion
+
+ #region "Приватные поля"
+
+
+ ///
+ /// Коллекция полей структуры.
+ ///
+ private readonly List _rows = [];
+
+ ///
+ /// Коллекция строк полей структуры, которые будут отображаться в элементе управления.
+ ///
+ private List _viewerRows = [];
+
+ ///
+ /// Флаг, указывающий, что элемент управления в данный момент перерисовывается.
+ ///
+ private bool _rendering;
+
+ #endregion
+
+ ///
+ /// Конструктор класса .
+ ///
public EntityTreeViewerControl()
{
InitializeComponent();
@@ -120,6 +174,11 @@ public sealed partial class EntityTreeViewerControl : UserControl
ContentPanel.MouseWheel += ContentPanel_MouseWheel;
}
+ #region "Методы обновления"
+
+ ///
+ /// Обновление данных в элементе управления.
+ ///
public void UpdateData()
{
if (EntityView?.Entity is null)
@@ -148,6 +207,9 @@ public sealed partial class EntityTreeViewerControl : UserControl
UpdateRenders();
}
+ ///
+ /// Обновление отрисовок в элементе управления.
+ ///
public void UpdateRenders()
{
int maxNameWidth = -1;
@@ -171,6 +233,9 @@ public sealed partial class EntityTreeViewerControl : UserControl
UpdateView();
}
+ ///
+ /// Обновление отображения в элементе управления.
+ ///
public void UpdateView()
{
_viewerRows.ForEach(row => row.UpdateButton());
@@ -203,6 +268,14 @@ public sealed partial class EntityTreeViewerControl : UserControl
_rendering = false;
}
+ #endregion
+
+ #region "Вспомогательные методы"
+
+ ///
+ /// Построение лестницы для строк полей структуры.
+ ///
+ /// Набор строк полей структуры.
private void BuildLadder(List rows)
{
List lastLadder = [];
@@ -248,8 +321,15 @@ public sealed partial class EntityTreeViewerControl : UserControl
}
}
- // Events
+ #endregion
+ #region "События элемента управления"
+
+ ///
+ /// Обработчик события изменения размера элемента управления.
+ ///
+ /// Источник события.
+ /// Объект события.
private void EntityTreeViewerControl_Resize(object sender, EventArgs e)
{
if (_rendering)
@@ -262,6 +342,11 @@ public sealed partial class EntityTreeViewerControl : UserControl
UpdateRenders();
}
+ ///
+ /// Обработчик события прокрутки элемента управления.
+ ///
+ /// Источник события.
+ /// Объект события.
private void ContentPanel_MouseWheel(object? sender, MouseEventArgs e)
{
/*
@@ -281,6 +366,11 @@ public sealed partial class EntityTreeViewerControl : UserControl
*/
}
+ ///
+ /// Обработчик события движения мыши над элементом управления.
+ ///
+ /// Источник события.
+ /// Объект события.
private void ContentPanel_MouseMove(object sender, MouseEventArgs e)
{
PointF p = new(e.X, e.Y);
@@ -299,4 +389,6 @@ public sealed partial class EntityTreeViewerControl : UserControl
row.Hovered = contained;
}
}
+
+ #endregion
}
diff --git a/Pf2eViewer/EntityTreeViewer/EntityTreeViewerControlRow.cs b/Pf2eViewer/EntityTreeViewer/EntityTreeViewerControlRow.cs
index 08460f8..1baecfb 100644
--- a/Pf2eViewer/EntityTreeViewer/EntityTreeViewerControlRow.cs
+++ b/Pf2eViewer/EntityTreeViewer/EntityTreeViewerControlRow.cs
@@ -5,13 +5,26 @@ using Image = System.Drawing.Image;
namespace Pf2eViewer.EntityTreeViewer;
+///
+/// Элемент управления для отображения строки поля сущности.
+///
internal class EntityTreeViewerControlRow
{
+ ///
+ /// Элемент управления, которому принадлежит эта строка.
+ ///
public readonly EntityTreeViewerControl ParentControl;
+ ///
+ /// Отображаемая строка поля сущности.
+ ///
public readonly StructureField Item;
- // Local fields
+ #region "Поля строки"
+
+ ///
+ /// Кэш изображения строки.
+ ///
public Image Cache
{
get
@@ -26,6 +39,9 @@ internal class EntityTreeViewerControlRow
}
private Image? _cache;
+ ///
+ /// Кэш изображения строки при наведении.
+ ///
public Image HoveredCache
{
get
@@ -40,6 +56,9 @@ internal class EntityTreeViewerControlRow
}
private Image? _hoveredCache;
+ ///
+ /// Список типов лестницы.
+ ///
public List Ladder
{
get => _ladder;
@@ -51,6 +70,9 @@ internal class EntityTreeViewerControlRow
}
private List _ladder = [];
+ ///
+ /// Проверяет, находится ли курсор над строкой.
+ ///
public bool Hovered
{
get => _hovered;
@@ -65,59 +87,103 @@ internal class EntityTreeViewerControlRow
}
private bool _hovered = false;
+ ///
+ /// Индекс строки в представлении.
+ ///
public int ViewIndex { get; set; } // = -1;
+ ///
+ /// Кнопка раскрытия/сворачивания.
+ ///
public IconButton? Button { get; set; } = null;
- // Inherited fields
+ #endregion
+ #region "Поля для рендеринга"
+
+ ///
public Font Font
{
get => ParentControl.Font;
set => ParentControl.Font = value;
}
+
+ ///
public Color ForeColor
{
get => ParentControl.ForeColor;
set => ParentControl.ForeColor = value;
}
+
+ ///
public Color BackColor
{
get => ParentControl.BackColor;
set => ParentControl.BackColor = value;
}
+
+ ///
public Color HoverColor
{
get => ParentControl.HoverColor;
set => ParentControl.HoverColor = value;
}
+
+ ///
public int ItemHeight
{
get => ParentControl.ItemHeight;
set => ParentControl.ItemHeight = value;
}
+
+ ///
public int DepthSize
{
get => ParentControl.DepthSize;
set => ParentControl.DepthSize = value;
}
+
+ ///
public int ButtonSize
{
get => ParentControl.ButtonSize;
set => ParentControl.ButtonSize = value;
}
+
+ ///
public float TypeFontSize
{
get => ParentControl.TypeFontSize;
set => ParentControl.TypeFontSize = value;
}
- // ValueText functions
+ #endregion
+ #region "Вычисляемые поля"
+
+ ///
+ /// Ширина строки.
+ ///
public int Width => ParentControl.ContentWidth;
+
+ ///
+ /// Высота строки.
+ ///
public int Height => ItemHeight;
+
+ ///
+ /// Флаг наличия кнопки.
+ ///
public bool HasButton => Item.FieldType == StructureFieldType.Complex;
+
+ ///
+ /// Глубина отображения строки.
+ ///
public int Depth => Item.Depth;
+
+ ///
+ /// Ширина имени в строке.
+ ///
public int NameWidth
{
get
@@ -127,6 +193,9 @@ internal class EntityTreeViewerControlRow
}
}
+ ///
+ /// Строковое представление типа поля.
+ ///
public string TypeText
{
get
@@ -140,10 +209,19 @@ internal class EntityTreeViewerControlRow
}
}
- // public Font TypeFont => new(Font.FontFamily, TypeFontSize, FontStyle.Italic);
+ ///
+ /// Шрифт для отображения типа поля.
+ ///
public Font TypeFont => new(Font.FontFamily, TypeFontSize);
+ ///
+ /// Строковое представление значения поля.
+ ///
public string ValueText => Item.GetValueString();
+
+ ///
+ /// Шрифт для отображения значения поля.
+ ///
public Font ValueFont =>
new(
Font.FontFamily,
@@ -151,16 +229,31 @@ internal class EntityTreeViewerControlRow
Item.FieldType == StructureFieldType.Complex ? FontStyle.Italic : FontStyle.Regular
);
+ ///
+ /// Прямоугольник, занимаемый строкой в представлении.
+ ///
public RectangleF Target => new(0, ViewIndex * Height, Width, Height);
+
+ ///
+ /// Прямоугольник, занимаемый строкой в родительском представлении.
+ ///
public RectangleF RealTarget =>
new(0, ViewIndex * Height - ParentControl.VerticalScroll.Value, Width, Height);
- // Simple fields
+ #endregion
+ ///
+ /// Список данных для рендеринга строк.
+ ///
private readonly List _renderData = [];
- // Main methods
+ #region "Основные методы"
+ ///
+ /// Создание элемента управления строки поля сущности.
+ ///
+ /// Родительский элемент управления.
+ /// Строка поля структуры объекта.
public EntityTreeViewerControlRow(EntityTreeViewerControl parent, StructureField item)
{
ParentControl = parent;
@@ -171,6 +264,9 @@ internal class EntityTreeViewerControlRow
UpdateButton();
}
+ ///
+ /// Применение свойств к элементу управления.
+ ///
private void ApplyItem()
{
_renderData.Clear();
@@ -200,12 +296,20 @@ internal class EntityTreeViewerControlRow
}
}
+ ///
+ /// Обновление кэша изображения строки.
+ ///
public void UpdateCache()
{
Cache = RenderCache(BackColor);
HoveredCache = RenderCache(HoverColor);
}
+ ///
+ /// Создание кэша изображения строки.
+ ///
+ /// Цвет заднего фона.
+ /// Кеш изображения строки.
private Image RenderCache(Color backColor)
{
Image cache = new Bitmap(Width, Height);
@@ -220,6 +324,10 @@ internal class EntityTreeViewerControlRow
return cache;
}
+ ///
+ /// Запрос на перерисовку строки.
+ ///
+ /// Графический объект.
public void Invalidate(Graphics? g = null)
{
g ??= Graphics.FromImage(ParentControl.ContentImage);
@@ -229,8 +337,13 @@ internal class EntityTreeViewerControlRow
UpdateButton();
}
- // Button
+ #endregion
+ #region "Кнопка"
+
+ ///
+ /// Обновление кнопки раскрытия/сворачивания.
+ ///
public void UpdateButton()
{
if (Button is null)
@@ -250,6 +363,11 @@ internal class EntityTreeViewerControlRow
Button.Visible = Item.Visible;
}
+ ///
+ /// Событие нажатия кнопки раскрытия/сворачивания.
+ ///
+ /// Источник события.
+ /// Событие нажатие кнопки
private void Button_Click(object? sender, EventArgs e)
{
if (Item is StructureFieldComplex complex)
@@ -259,8 +377,14 @@ internal class EntityTreeViewerControlRow
}
}
- // Render
+ #endregion
+ #region "Отрисовка"
+
+ ///
+ /// Отрисовка лестницы.
+ ///
+ /// Графический объект.
private void RenderLadder(Graphics g)
{
int n = Depth;
@@ -301,6 +425,10 @@ internal class EntityTreeViewerControlRow
}
}
+ ///
+ /// Отрисовка содержимого строки.
+ ///
+ /// Графический объект.
private void RenderContent(Graphics g)
{
float[] xs =
@@ -326,13 +454,28 @@ internal class EntityTreeViewerControlRow
}
}
- // Helper functions
+ #endregion
+ #region Функции-помощники в отрисовке
+
+ ///
+ /// Смешивание цвета с цветом текста.
+ ///
+ /// Цвет оттенка.
+ /// Степень смешения.
+ /// Смешенный цвет.
private Color BlendColor(Color color, double rate = 0.5)
{
return BlendColor(ForeColor, color, rate);
}
+ ///
+ /// Смешивание цветов.
+ ///
+ /// Первый смешиваемый цвет.
+ /// Второй смешиваемый цвет.
+ /// Степень смешения.
+ /// Смешенный цвет.
private Color BlendColor(Color color1, Color color2, double rate)
{
return Color.FromArgb(
@@ -345,6 +488,10 @@ internal class EntityTreeViewerControlRow
int Mix(int a, int b) => Convert.ToInt32(Math.Round(a * rate + b * (1 - rate)));
}
+ ///
+ /// Получение цвета текста по типу значения поля.
+ ///
+ /// Цвет текста.
public Color GetValueColor()
{
return Item switch
@@ -363,19 +510,59 @@ internal class EntityTreeViewerControlRow
};
}
+ #endregion
+
+ ///
+ /// Класс для хранения данных рендеринга строки.
+ ///
+ /// Текст отображаемой строки.
+ /// Шрифт отображаемой строки.
+ /// Цвет отображаемой строки.
private class StringRenderData(string text, Font font, Color color)
{
+ ///
+ /// Текст отображаемой строки.
+ ///
public string Text { get; } = text;
+
+ ///
+ /// Шрифт отображаемой строки.
+ ///
public Font Font { get; } = font;
+
+ ///
+ /// Цвет отображаемой строки.
+ ///
public Color Color { get; } = color;
+ ///
+ /// Левый край отображаемой строки.
+ ///
public float Left { get; set; }
+
+ ///
+ /// Ширина отображаемой строки.
+ ///
public float Width { get; set; }
+
+ ///
+ /// Высота отображаемой строки.
+ ///
public float Height { get; set; }
+ ///
+ /// Прямоугольник, занимаемый строкой.
+ ///
public RectangleF Rect => new(Left, 0, Width, Height);
- private string Fit(Graphics g, RectangleF rect)
+ ///
+ /// Уместить строку в занимаемый прямоугольник.
+ ///
+ /// Элемент графики.
+ /// Занимаемый прямоугольник
+ /// Строка, на которую будет заканчиваться значнеие при переполнении.
+ ///
+ private string Fit(Graphics g, RectangleF rect, string filler = "…")
{
if (Text.Length == 0 || g.MeasureString(Text, Font).Width <= rect.Width)
{
@@ -386,16 +573,20 @@ internal class EntityTreeViewerControlRow
int i = txt.Length;
- while (g.MeasureString(txt + "...", Font).Width > rect.Width)
+ while (g.MeasureString(txt + filler, Font).Width > rect.Width)
{
txt = txt[..--i];
if (i == 0)
break;
}
- return txt + "...";
+ return txt + filler;
}
+ ///
+ /// Отрисовка строки.
+ ///
+ /// Элемент графики.
public void Draw(Graphics g)
{
g.DrawString(
diff --git a/Pf2eViewer/EntityTreeViewer/StructureBuilder.cs b/Pf2eViewer/EntityTreeViewer/StructureBuilder.cs
index 2af955f..0dfea73 100644
--- a/Pf2eViewer/EntityTreeViewer/StructureBuilder.cs
+++ b/Pf2eViewer/EntityTreeViewer/StructureBuilder.cs
@@ -5,10 +5,23 @@ using Pf2eModel.Model.Common.I18n;
namespace Pf2eViewer.EntityTreeViewer;
+///
+/// Класс для построения структуры данных класса.
+///
internal class StructureBuilder
{
+ ///
+ /// Список обработанных объектов.
+ ///
private readonly List _processedObjects = [];
+ ///
+ /// Создание поля структуры.
+ ///
+ /// Название поля.
+ /// Значение поля.
+ /// Глубина, на котором поле отображается.
+ /// Поле структуры.
public StructureFieldComplex Create(string name, object value, int depth = 0)
{
_ = RegisterProcessed(value);
@@ -20,6 +33,11 @@ internal class StructureBuilder
return field;
}
+ ///
+ /// Регистрация обработанного объекта.
+ ///
+ /// Регистрируемый объект.
+ /// `true`, если объект был добавлен; `false`, если объект был добавлен ранее.
public bool RegisterProcessed(object value)
{
if (_processedObjects.Any(p => p.Value == value))
@@ -34,6 +52,11 @@ internal class StructureBuilder
return true;
}
+ ///
+ /// Дополнить обработанный объект.
+ ///
+ /// Значение.
+ /// Поле объекта.
public void CompleteProcessed(object value, StructureFieldComplex field)
{
StructureProcessedObject? q = _processedObjects.FirstOrDefault(p => p.Value == value);
@@ -46,6 +69,13 @@ internal class StructureBuilder
q.Field = field;
}
+ ///
+ /// Выбор поля структуры по значению.
+ ///
+ /// Название поля.
+ /// Значение поля.
+ /// Глубина, на котором поле отображается.
+ ///
public StructureField Resolve(string name, object? value, int depth)
{
switch (GetFieldType(value))
@@ -87,6 +117,11 @@ internal class StructureBuilder
throw new("Impossible");
}
+ ///
+ /// Получить тип поля структуры по значению.
+ ///
+ /// Значение поля.
+ /// Тип поля структуры.
public static StructureFieldType GetFieldType(object? value)
{
if (value is null)
@@ -110,6 +145,10 @@ internal class StructureBuilder
}
}
+///
+/// Класс для хранения обработанного объекта.
+///
+/// Объект.
internal class StructureProcessedObject(object value)
{
public readonly object Value = value;
@@ -142,38 +181,109 @@ internal class StructureProcessedObject(object value)
}
}
+///
+/// Тип поля структуры.
+///
internal enum StructureFieldType
{
+ ///
+ /// Пустое поле.
+ ///
Null,
+
+ ///
+ /// Простое поле.
+ ///
Primitive,
+
+ ///
+ /// Поле с перечислением.
+ ///
Enum,
+
+ ///
+ /// Поле с объектом.
+ ///
Complex,
+
+ ///
+ /// Поле с ссылкой на объект.
+ ///
Reference,
}
+///
+/// Тип лестницы глубины.
+///
internal enum LadderType
{
+ ///
+ /// Пустой элемент: [ ].
+ ///
Empty,
+
+ ///
+ /// Последний элемент: [└].
+ ///
Last,
+
+ ///
+ /// Проходной элемент: [├] или [│].
+ ///
Through,
}
+///
+/// Базовый класс для полей структуры.
+///
+/// Класс построения структуры данных.
+/// Название поля.
+/// Глубина поля.
internal abstract class StructureField(StructureBuilder builder, string name, int depth)
{
+ ///
+ /// Название поля.
+ ///
public readonly string Name = name;
+ ///
+ /// Тип поля структуры.
+ ///
public abstract StructureFieldType FieldType { get; }
+ ///
+ /// Класс построения структуры данных.
+ ///
public readonly StructureBuilder Builder = builder;
+ ///
+ /// Флаг, указывающий, отображается ли поле.
+ ///
public virtual bool Visible { get; set; } = true;
+ ///
+ /// Глубина поля структуры.
+ ///
public readonly int Depth = depth;
+ ///
+ /// Получить строку, представляющую тип поля.
+ ///
+ /// Строка, представляющая тип поля.
public abstract string GetTypeString();
+ ///
+ /// Получить строку, представляющую значение поля.
+ ///
+ /// Строка, представляющая значение поля.
public abstract string GetValueString();
+ ///
+ /// Получить набор строк, используя результат функция.
+ ///
+ /// Тип результирующей строки.
+ /// Функция, оборажения поля в результирующую строку.
+ /// Коллекция, содержащая набор строк.
public virtual ICollection GetRows(Func func)
{
StructureField[] q = [this];
@@ -181,16 +291,32 @@ internal abstract class StructureField(StructureBuilder builder, string name, in
}
}
+///
+/// Пустое поле структуры.
+///
+/// Класс построения структуры данных.
+/// Название поля.
+/// Глубина поля.
internal class StructureFieldNull(StructureBuilder builder, string name, int depth)
: StructureField(builder, name, depth)
{
+ ///
public override StructureFieldType FieldType => StructureFieldType.Null;
+ ///
public override string GetTypeString() => "null";
+ ///
public override string GetValueString() => "";
}
+///
+/// Простое поле структуры.
+///
+/// Класс построения структуры данных.
+/// Значение поля.
+/// Название поля.
+/// Глубина поля.
internal class StructureFieldPrimitive(
StructureBuilder builder,
object value,
@@ -198,12 +324,20 @@ internal class StructureFieldPrimitive(
int depth
) : StructureField(builder, name, depth)
{
+ ///
public override StructureFieldType FieldType => StructureFieldType.Primitive;
+ ///
+ /// Значение поля.
+ ///
public readonly object Value = value;
+ ///
+ /// Тип значения поля.
+ ///
public readonly Type Type = value.GetType();
+ ///
public override string GetTypeString()
{
return Type.GetTypeCode(Type) switch
@@ -216,6 +350,7 @@ internal class StructureFieldPrimitive(
};
}
+ ///
public override string GetValueString()
{
if (Type == typeof(string))
@@ -227,28 +362,57 @@ internal class StructureFieldPrimitive(
}
}
+///
+/// Поле структуры с перечислением.
+///
+/// Класс построения структуры данных.
+/// Значение поля.
+/// Название поля.
+/// Глубина поля.
internal class StructureFieldEnum(StructureBuilder builder, object value, string name, int depth)
: StructureField(builder, name, depth)
{
+ ///
public override StructureFieldType FieldType => StructureFieldType.Enum;
+ ///
+ /// Значение поля.
+ ///
public readonly object Value = value;
+ ///
+ /// Тип значения поля.
+ ///
public readonly Type Type = value.GetType();
+ ///
public override string GetTypeString() => "enum";
+ ///
public override string GetValueString() => $"{Type.Name}.{Value}";
}
+///
+/// Поле стркутуры с объектом.
+///
internal class StructureFieldComplex : StructureField
{
+ ///
public override StructureFieldType FieldType => StructureFieldType.Complex;
+ ///
+ /// Набор полей объекта.
+ ///
public readonly List Fields = [];
+ ///
+ /// Тип значения поля.
+ ///
public readonly Type Type;
+ ///
+ /// Флаг, указывающий, отображается ли поля объекта.
+ ///
public bool Collapsed
{
get => _collapsed;
@@ -260,6 +424,7 @@ internal class StructureFieldComplex : StructureField
}
private bool _collapsed = false;
+ ///
public override bool Visible
{
get => _visible;
@@ -271,6 +436,13 @@ internal class StructureFieldComplex : StructureField
}
private bool _visible = true;
+ ///
+ /// Конструктор класса поля структуры с объектом.
+ ///
+ /// Класс построения структуры данных.
+ /// Значение поля.
+ /// Название поля.
+ /// Глубина поля.
public StructureFieldComplex(StructureBuilder builder, object value, string name, int depth)
: base(builder, name, depth)
{
@@ -279,6 +451,9 @@ internal class StructureFieldComplex : StructureField
SetFields((dynamic)value);
}
+ ///
+ /// Обновить видимость полей структуры.
+ ///
private void UpdateFieldVisibility()
{
foreach (StructureField field in Fields)
@@ -287,6 +462,10 @@ internal class StructureFieldComplex : StructureField
}
}
+ ///
+ /// Установить поля структуры `LocaleString`.
+ ///
+ /// Объект строки локализации.
private void SetFields(LocaleString localeString)
{
SetFieldsFromRecords(
@@ -296,6 +475,10 @@ internal class StructureFieldComplex : StructureField
);
}
+ ///
+ /// Установить поля структуры `IEnumerable`.
+ ///
+ /// Объект-перечисление.
private void SetFields(IEnumerable enumerable)
{
int i = 0;
@@ -310,6 +493,10 @@ internal class StructureFieldComplex : StructureField
SetFieldsFromRecords(records);
}
+ ///
+ /// Установить поля структуры общего объекта.
+ ///
+ /// Объект.
private void SetFields(object value)
{
Type type = value.GetType();
@@ -325,6 +512,10 @@ internal class StructureFieldComplex : StructureField
);
}
+ ///
+ /// Установить поля структуры из списка записей.
+ ///
+ ///
private void SetFieldsFromRecords(List records)
{
foreach (
@@ -346,22 +537,35 @@ internal class StructureFieldComplex : StructureField
}
}
- public override ICollection GetRows(Func func)
- {
- return [.. base.GetRows(func), .. Fields.SelectMany(f => f.GetRows(func)).ToList()];
- }
-
+ ///
+ /// Запись поля структуры.
+ ///
+ /// Название поля.
+ /// Значение поля.
private class FieldRecord(string name, object? value)
{
+ ///
+ /// Название поля.
+ ///
public readonly string Name = name;
+ ///
+ /// Значение поля.
+ ///
public readonly object? Value = value;
+ ///
+ /// Тип поля структуры.
+ ///
public StructureFieldType FieldType => StructureBuilder.GetFieldType(Value);
+ ///
+ /// Флаг, указывающий, использовать ли поле.
+ ///
public bool Allowed = true;
}
+ ///
public override string GetTypeString()
{
if (Type.IsGenericEnumerableType())
@@ -376,6 +580,7 @@ internal class StructureFieldComplex : StructureField
}
}
+ ///
public override string GetValueString()
{
if (Type.IsGenericEnumerableType())
@@ -384,8 +589,21 @@ internal class StructureFieldComplex : StructureField
}
return "";
}
+
+ ///
+ public override ICollection GetRows(Func func)
+ {
+ return [.. base.GetRows(func), .. Fields.SelectMany(f => f.GetRows(func)).ToList()];
+ }
}
+///
+/// Поле структуры с ссылкой на объект.
+///
+/// Класс построения структуры данных.
+/// Название поля.
+/// Ссылка на объект.
+/// Глубина поля.
internal class StructureFieldReference(
StructureBuilder builder,
string name,
@@ -393,11 +611,17 @@ internal class StructureFieldReference(
int depth
) : StructureField(builder, name, depth)
{
+ ///
+ /// Ссылка на объект.
+ ///
public StructureFieldComplex? Reference = reference;
+ ///
public override StructureFieldType FieldType => StructureFieldType.Reference;
+ ///
public override string GetTypeString() => Reference?.GetTypeString() ?? "???";
+ ///
public override string GetValueString() => "";
}
diff --git a/Pf2eViewer/EntityView.cs b/Pf2eViewer/EntityView.cs
index 8ee8b7e..cd440b2 100644
--- a/Pf2eViewer/EntityView.cs
+++ b/Pf2eViewer/EntityView.cs
@@ -7,11 +7,27 @@ using Pf2eModel.Model.Structure;
namespace Pf2eViewer;
+///
+/// Класс, обрабатывающий запросы к сущностям и подключающий необходимые данные.
+///
public class EntityView
{
- public Entity? Entity;
- public IEntity? SubEntity;
+ ///
+ /// Корневая сущность.
+ ///
+ public Entity? Entity { get; }
+ ///
+ /// Основная сущность.
+ ///
+ public IEntity? SubEntity { get; }
+
+ ///
+ /// Конструктор класса, который получает сущность из базы данных и заполняет её данными.
+ ///
+ /// Контекст базы данных.
+ /// Тип запрашиваемой сущности.
+ /// Идентификатор запрашиваемой сущности.
public EntityView(Pf2eDbContext context, EntityType type, int id)
{
Getter? getter = _getters.FirstOrDefault(g => g.Type == type);
@@ -37,6 +53,9 @@ public class EntityView
SubEntity = subEntity;
}
+ ///
+ /// Словарь, содержащий методы получения сущностей из базы данных.
+ ///
private readonly ICollection _getters =
[
new Getter(
@@ -90,6 +109,10 @@ public class EntityView
),
];
+ ///
+ /// Абстрактный класс, который определяет метод получения сущности из базы данных.
+ ///
+ /// Связанный тип сущности.
private abstract class Getter(EntityType type)
{
public readonly EntityType Type = type;
@@ -97,6 +120,12 @@ public class EntityView
public abstract IEntity? Get(int id, Pf2eDbContext context);
}
+ ///
+ /// Класс, определяющий метод получения сущности из базы данных.
+ ///
+ /// Тип данных основной сущности.
+ /// Связанный тип сущности.
+ /// Метод получения сущности из базы данных.
private class Getter(EntityType type, Func> method)
: Getter(type)
where T : IEntity
diff --git a/Pf2eViewer/MainViewForm.cs b/Pf2eViewer/MainViewForm.cs
index 4fe78e7..0979588 100644
--- a/Pf2eViewer/MainViewForm.cs
+++ b/Pf2eViewer/MainViewForm.cs
@@ -4,10 +4,19 @@ using Pf2eModel.Model.Enum;
namespace Pf2eViewer;
+///
+/// Основная форма приложения.
+///
public partial class MainViewForm : Form
{
+ ///
+ /// Контекст базы данных.
+ ///
public Pf2eDbContext Context = new();
+ ///
+ /// Элемент управления для отображения дерева сущностей.
+ ///
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public EntityView? CurrentView
{
@@ -19,6 +28,9 @@ public partial class MainViewForm : Form
}
}
+ ///
+ /// Конструктор класса .
+ ///
public MainViewForm()
{
InitializeComponent();
@@ -28,6 +40,9 @@ public partial class MainViewForm : Form
Reset();
}
+ ///
+ /// Сброс состояния формы.
+ ///
public void Reset()
{
Context.Dispose();
@@ -41,6 +56,11 @@ public partial class MainViewForm : Form
TypesListBox.SelectedIndex = 0;
}
+ ///
+ /// Обработчик события изменения выбранного элемента в списке типов сущностей.
+ ///
+ /// Источник события.
+ /// Данные события.
private void TypesListBox_SelectedIndexChanged(object sender, EventArgs _)
{
if (TypesListBox.SelectedItem is null)
@@ -53,6 +73,10 @@ public partial class MainViewForm : Form
DatabaseViewer.Type = (EntityType)TypesListBox.SelectedItem;
}
+ ///
+ /// Обработчик события изменения выбранной сущности в дереве сущностей.
+ ///
+ /// Новая выбранная сущность.
private void DatabaseViewer_EntityChange(EntityView newEntity)
{
CurrentView = newEntity;