Files
Lattice/Lattice.Core.Docking/Services/ContentRegistry.cs
2026-02-01 09:26:13 +03:00

235 lines
11 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
namespace Lattice.Core.Docking.Services;
/// <summary>
/// Реестр типов содержимого, который позволяет создавать экземпляры контента по типу.
/// Этот сервис является центральным для динамического создания панелей инструментов и документов.
/// </summary>
/// <remarks>
/// Реализует шаблон "Фабрика" для создания экземпляров <see cref="Abstractions.IDockContent"/>.
/// Позволяет регистрировать фабричные методы для различных типов контента, что обеспечивает
/// позднее связывание и возможность плагинной архитектуры.
/// </remarks>
public class ContentRegistry
{
private readonly Dictionary<string, ContentDescriptor> _contentTypes = new();
/// <summary>
/// Регистрирует фабричный метод для создания контента указанного типа.
/// </summary>
/// <typeparam name="T">
/// Тип контента, реализующий <see cref="Abstractions.IDockContent"/>.
/// </typeparam>
/// <param name="contentTypeId">Уникальный идентификатор типа контента.</param>
/// <param name="factory">Фабричный метод для создания экземпляров контента.</param>
/// <param name="metadata">Метаданные типа контента (опционально).</param>
/// <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
{
if (string.IsNullOrWhiteSpace(contentTypeId))
throw new ArgumentNullException(nameof(contentTypeId));
if (factory == null)
throw new ArgumentNullException(nameof(factory));
// Дополнительная проверка на пустую строку
if (string.IsNullOrEmpty(contentTypeId.Trim()))
throw new ArgumentException("Идентификатор типа контента не может быть пустой строкой.", nameof(contentTypeId));
if (_contentTypes.ContainsKey(contentTypeId))
throw new ArgumentException($"Тип контента '{contentTypeId}' уже зарегистрирован.");
_contentTypes[contentTypeId] = new ContentDescriptor(
typeof(T),
() => factory(),
metadata ?? new ContentMetadata(contentTypeId, typeof(T).Name)
);
}
/// <summary>
/// Создает новый экземпляр контента указанного типа с заданным идентификатором.
/// </summary>
/// <param name="contentTypeId">Идентификатор типа контента.</param>
/// <param name="id">Уникальный идентификатор для создаваемого экземпляра контента.</param>
/// <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($"Тип контента '{contentTypeId}' не зарегистрирован.");
var content = descriptor.Factory();
content.SetId(id);
return content;
}
/// <summary>
/// Получает метаданные для указанного типа контента.
/// </summary>
/// <param name="contentTypeId">Идентификатор типа контента.</param>
/// <returns>
/// Метаданные типа контента или null, если тип не найден.
/// </returns>
public ContentMetadata? GetMetadata(string contentTypeId)
{
if (string.IsNullOrWhiteSpace(contentTypeId))
return null;
return _contentTypes.TryGetValue(contentTypeId, out var descriptor)
? descriptor.Metadata
: null;
}
/// <summary>
/// Получает все зарегистрированные типы контента.
/// </summary>
/// <returns>
/// Коллекция идентификаторов зарегистрированных типов контента.
/// </returns>
public IEnumerable<string> GetRegisteredTypes() => _contentTypes.Keys;
/// <summary>
/// Проверяет, зарегистрирован ли указанный тип контента.
/// </summary>
/// <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; }
/// <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;
Metadata = metadata;
}
}
}
/// <summary>
/// Представляет метаданные типа контента, предоставляющие дополнительную информацию для отображения в 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>
/// <exception cref="ArgumentNullException">
/// Выбрасывается, если <paramref name="contentTypeId"/> или <paramref name="displayName"/>
/// равны null.
/// </exception>
public ContentMetadata(string contentTypeId, string displayName)
{
ContentTypeId = contentTypeId ?? throw new ArgumentNullException(nameof(contentTypeId));
DisplayName = displayName ?? throw new ArgumentNullException(nameof(displayName));
Description = string.Empty;
}
}