Доработан Docking

This commit is contained in:
2026-01-27 05:17:35 +03:00
parent 33abd94f6e
commit 584df249f6
99 changed files with 2270 additions and 12792 deletions

View File

@@ -2,7 +2,7 @@
/// <summary>
/// Реестр типов содержимого, который позволяет создавать экземпляры контента по типу.
/// Этот сервис является центральным для динамического создания панелей инструментов и документов в IDE.
/// Этот сервис является центральным для динамического создания панелей инструментов и документов.
/// </summary>
/// <remarks>
/// Реализует шаблон "Фабрика" для создания экземпляров <see cref="Abstractions.IDockContent"/>.
@@ -16,12 +16,19 @@ public class ContentRegistry
/// <summary>
/// Регистрирует фабричный метод для создания контента указанного типа.
/// </summary>
/// <typeparam name="T">Тип контента, реализующий <see cref="Abstractions.IDockContent"/>.</typeparam>
/// <typeparam name="T">
/// Тип контента, реализующий <see cref="Abstractions.IDockContent"/>.
/// </typeparam>
/// <param name="contentTypeId">Уникальный идентификатор типа контента.</param>
/// <param name="factory">Фабричный метод для создания экземпляров контента.</param>
/// <param name="metadata">Метаданные типа контента (опционально).</param>
/// <exception cref="ArgumentNullException">Выбрасывается, если contentTypeId или factory равны null.</exception>
/// <exception cref="ArgumentException">Выбрасывается, если contentTypeId уже зарегистрирован.</exception>
/// <exception cref="ArgumentNullException">
/// Выбрасывается, если <paramref name="contentTypeId"/> или <paramref name="factory"/>
/// равны null.
/// </exception>
/// <exception cref="ArgumentException">
/// Выбрасывается, если <paramref name="contentTypeId"/> уже зарегистрирован.
/// </exception>
public void Register<T>(string contentTypeId, Func<T> factory, ContentMetadata? metadata = null)
where T : Abstractions.IDockContent
{
@@ -31,7 +38,7 @@ public class ContentRegistry
throw new ArgumentNullException(nameof(factory));
if (_contentTypes.ContainsKey(contentTypeId))
throw new ArgumentException($"Content type '{contentTypeId}' is already registered.");
throw new ArgumentException($"Тип контента '{contentTypeId}' уже зарегистрирован.");
_contentTypes[contentTypeId] = new ContentDescriptor(
typeof(T),
@@ -45,14 +52,25 @@ public class ContentRegistry
/// </summary>
/// <param name="contentTypeId">Идентификатор типа контента.</param>
/// <param name="id">Уникальный идентификатор для создаваемого экземпляра контента.</param>
/// <returns>Новый экземпляр контента.</returns>
/// <exception cref="KeyNotFoundException">Выбрасывается, если тип контента не зарегистрирован.</exception>
/// <returns>
/// Новый экземпляр контента.
/// </returns>
/// <exception cref="ArgumentNullException">
/// Выбрасывается, если <paramref name="contentTypeId"/> равен null или пустой строке.
/// </exception>
/// <exception cref="KeyNotFoundException">
/// Выбрасывается, если тип контента не зарегистрирован.
/// </exception>
public Abstractions.IDockContent CreateContent(string contentTypeId, string id)
{
if (string.IsNullOrWhiteSpace(contentTypeId))
throw new ArgumentNullException(nameof(contentTypeId));
if (!_contentTypes.TryGetValue(contentTypeId, out var descriptor))
throw new KeyNotFoundException($"Content type '{contentTypeId}' is not registered.");
throw new KeyNotFoundException($"Тип контента '{contentTypeId}' не зарегистрирован.");
var content = descriptor.Factory();
// Устанавливаем ID через рефлексию, если есть свойство Id
var property = content.GetType().GetProperty("Id");
if (property != null && property.CanWrite)
@@ -67,9 +85,14 @@ public class ContentRegistry
/// Получает метаданные для указанного типа контента.
/// </summary>
/// <param name="contentTypeId">Идентификатор типа контента.</param>
/// <returns>Метаданные типа контента или null, если тип не найден.</returns>
/// <returns>
/// Метаданные типа контента или null, если тип не найден.
/// </returns>
public ContentMetadata? GetMetadata(string contentTypeId)
{
if (string.IsNullOrWhiteSpace(contentTypeId))
return null;
return _contentTypes.TryGetValue(contentTypeId, out var descriptor)
? descriptor.Metadata
: null;
@@ -78,24 +101,54 @@ public class ContentRegistry
/// <summary>
/// Получает все зарегистрированные типы контента.
/// </summary>
/// <returns>Коллекция идентификаторов зарегистрированных типов контента.</returns>
/// <returns>
/// Коллекция идентификаторов зарегистрированных типов контента.
/// </returns>
public IEnumerable<string> GetRegisteredTypes() => _contentTypes.Keys;
/// <summary>
/// Проверяет, зарегистрирован ли указанный тип контента.
/// </summary>
public bool IsRegistered(string contentTypeId) => _contentTypes.ContainsKey(contentTypeId);
/// <param name="contentTypeId">Идентификатор типа контента.</param>
/// <returns>
/// true, если тип контента зарегистрирован; в противном случае false.
/// </returns>
public bool IsRegistered(string contentTypeId)
{
if (string.IsNullOrWhiteSpace(contentTypeId))
return false;
return _contentTypes.ContainsKey(contentTypeId);
}
/// <summary>
/// Дескриптор типа контента, содержащий информацию о фабричном методе и метаданных.
/// Представляет дескриптор типа контента, содержащий информацию о фабричном методе и метаданных.
/// </summary>
private class ContentDescriptor
{
/// <summary>
/// Получает тип контента.
/// </summary>
public Type ContentType { get; }
/// <summary>
/// Получает фабричный метод для создания экземпляров контента.
/// </summary>
public Func<Abstractions.IDockContent> Factory { get; }
/// <summary>
/// Получает метаданные типа контента.
/// </summary>
public ContentMetadata Metadata { get; }
public ContentDescriptor(Type contentType, Func<Abstractions.IDockContent> factory, ContentMetadata metadata)
/// <summary>
/// Инициализирует новый экземпляр класса <see cref="ContentDescriptor"/>.
/// </summary>
/// <param name="contentType">Тип контента.</param>
/// <param name="factory">Фабричный метод.</param>
/// <param name="metadata">Метаданные.</param>
public ContentDescriptor(Type contentType, Func<Abstractions.IDockContent> factory,
ContentMetadata metadata)
{
ContentType = contentType;
Factory = factory;
@@ -105,54 +158,80 @@ public class ContentRegistry
}
/// <summary>
/// Метаданные типа контента, предоставляющие дополнительную информацию для отображения в UI.
/// Представляет метаданные типа контента, предоставляющие дополнительную информацию для отображения в UI.
/// </summary>
public class ContentMetadata
{
/// <summary>
/// Идентификатор типа контента.
/// Получает идентификатор типа контента.
/// </summary>
/// <value>
/// Уникальный строковый идентификатор типа контента.
/// </value>
public string ContentTypeId { get; }
/// <summary>
/// Отображаемое имя типа контента.
/// Получает или задает отображаемое имя типа контента.
/// </summary>
/// <value>
/// Имя типа контента, отображаемое пользователю.
/// </value>
public string DisplayName { get; set; }
/// <summary>
/// Описание типа контента.
/// Получает или задает описание типа контента.
/// </summary>
/// <value>
/// Текстовое описание функциональности контента.
/// </value>
public string Description { get; set; }
/// <summary>
/// Имя ресурса для иконки (опционально).
/// Получает или задает имя ресурса для иконки типа контента.
/// </summary>
/// <value>
/// Имя ресурса иконки или null, если иконка не определена.
/// </value>
public string? IconResource { get; set; }
/// <summary>
/// Признак того, что контент является документом (а не инструментальной панелью).
/// Получает или задает значение, указывающее, является ли контент документом
/// (а не инструментальной панелью).
/// </summary>
/// <value>
/// true, если контент является документом; в противном случае false.
/// </value>
public bool IsDocument { get; set; }
/// <summary>
/// Минимальная ширина контента в пикселях.
/// Получает или задает ширину контента по умолчанию.
/// </summary>
/// <value>
/// Ширина контента в пикселях. Значение по умолчанию: 300.
/// </value>
public double DefaultWidth { get; set; } = 300;
/// <summary>
/// Минимальная высота контента в пикселях.
/// Получает или задает высоту контента по умолчанию.
/// </summary>
/// <value>
/// Высота контента в пикселях. Значение по умолчанию: 200.
/// </value>
public double DefaultHeight { get; set; } = 200;
/// <summary>
/// Инициализирует новый экземпляр метаданных контента.
/// Инициализирует новый экземпляр класса <see cref="ContentMetadata"/>.
/// </summary>
/// <param name="contentTypeId">Идентификатор типа контента.</param>
/// <param name="displayName">Отображаемое имя.</param>
/// <param name="displayName">Отображаемое имя типа контента.</param>
/// <exception cref="ArgumentNullException">
/// Выбрасывается, если <paramref name="contentTypeId"/> или <paramref name="displayName"/>
/// равны null.
/// </exception>
public ContentMetadata(string contentTypeId, string displayName)
{
ContentTypeId = contentTypeId;
DisplayName = displayName;
ContentTypeId = contentTypeId ?? throw new ArgumentNullException(nameof(contentTypeId));
DisplayName = displayName ?? throw new ArgumentNullException(nameof(displayName));
Description = string.Empty;
}
}