DragAndDrop core
This commit is contained in:
37
Lattice.Serialization.Docking/DTO/AutoHidePanelDto.cs
Normal file
37
Lattice.Serialization.Docking/DTO/AutoHidePanelDto.cs
Normal file
@@ -0,0 +1,37 @@
|
||||
namespace Lattice.Serialization.Docking;
|
||||
|
||||
/// <summary>
|
||||
/// DTO для автоскрываемой панели (AutoHidePanel).
|
||||
/// </summary>
|
||||
public class AutoHidePanelDto
|
||||
{
|
||||
/// <summary>
|
||||
/// Уникальный идентификатор панели.
|
||||
/// </summary>
|
||||
public string Id { get; set; } = Guid.NewGuid().ToString();
|
||||
|
||||
/// <summary>
|
||||
/// Ссылка на контент панели.
|
||||
/// </summary>
|
||||
public ContentReferenceDto Content { get; set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// Сторона прикрепления в виде строки.
|
||||
/// </summary>
|
||||
public string Side { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Размер панели в пикселях.
|
||||
/// </summary>
|
||||
public double Size { get; set; } = 300;
|
||||
|
||||
/// <summary>
|
||||
/// Показывает, видима ли панель.
|
||||
/// </summary>
|
||||
public bool IsVisible { get; set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// Смещение для анимации (0.0 - 1.0).
|
||||
/// </summary>
|
||||
public double SlideOffset { get; set; } = 0.0;
|
||||
}
|
||||
32
Lattice.Serialization.Docking/DTO/ContentReferenceDto.cs
Normal file
32
Lattice.Serialization.Docking/DTO/ContentReferenceDto.cs
Normal file
@@ -0,0 +1,32 @@
|
||||
namespace Lattice.Serialization.Docking;
|
||||
|
||||
/// <summary>
|
||||
/// DTO для ссылки на контент без сериализации самого контента.
|
||||
/// </summary>
|
||||
public class ContentReferenceDto
|
||||
{
|
||||
/// <summary>
|
||||
/// Уникальный идентификатор контента.
|
||||
/// </summary>
|
||||
public string Id { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Тип контента (для восстановления через ContentRegistry).
|
||||
/// </summary>
|
||||
public string TypeId { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Отображаемое название контента.
|
||||
/// </summary>
|
||||
public string Title { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Показывает, можно ли закрыть контент.
|
||||
/// </summary>
|
||||
public bool CanClose { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Дополнительные свойства контента для восстановления состояния.
|
||||
/// </summary>
|
||||
public Dictionary<string, object?> Properties { get; set; } = new();
|
||||
}
|
||||
37
Lattice.Serialization.Docking/DTO/ElementDto.cs
Normal file
37
Lattice.Serialization.Docking/DTO/ElementDto.cs
Normal file
@@ -0,0 +1,37 @@
|
||||
namespace Lattice.Serialization.Docking;
|
||||
|
||||
/// <summary>
|
||||
/// Базовый DTO для элементов дерева компоновки.
|
||||
/// </summary>
|
||||
public abstract class ElementDto
|
||||
{
|
||||
/// <summary>
|
||||
/// Уникальный идентификатор элемента.
|
||||
/// </summary>
|
||||
public string Id { get; set; } = Guid.NewGuid().ToString();
|
||||
|
||||
/// <summary>
|
||||
/// Тип элемента (для десериализации).
|
||||
/// </summary>
|
||||
public string Type { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Ширина элемента.
|
||||
/// </summary>
|
||||
public double Width { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Высота элемента.
|
||||
/// </summary>
|
||||
public double Height { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Минимальная ширина элемента.
|
||||
/// </summary>
|
||||
public double MinWidth { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Минимальная высота элемента.
|
||||
/// </summary>
|
||||
public double MinHeight { get; set; }
|
||||
}
|
||||
27
Lattice.Serialization.Docking/DTO/GroupDto.cs
Normal file
27
Lattice.Serialization.Docking/DTO/GroupDto.cs
Normal file
@@ -0,0 +1,27 @@
|
||||
namespace Lattice.Serialization.Docking;
|
||||
|
||||
/// <summary>
|
||||
/// DTO для группы разделения (DockGroup).
|
||||
/// </summary>
|
||||
public class GroupDto : ElementDto
|
||||
{
|
||||
/// <summary>
|
||||
/// Первый дочерний элемент (левая или верхняя область).
|
||||
/// </summary>
|
||||
public ElementDto First { get; set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// Второй дочерний элемент (правая или нижняя область).
|
||||
/// </summary>
|
||||
public ElementDto Second { get; set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// Направление разделения в виде строки.
|
||||
/// </summary>
|
||||
public string Orientation { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Соотношение разделения между первым и вторым элементами (0.0 - 1.0).
|
||||
/// </summary>
|
||||
public double SplitRatio { get; set; } = 0.5;
|
||||
}
|
||||
47
Lattice.Serialization.Docking/DTO/LayoutDto.cs
Normal file
47
Lattice.Serialization.Docking/DTO/LayoutDto.cs
Normal file
@@ -0,0 +1,47 @@
|
||||
namespace Lattice.Serialization.Docking;
|
||||
|
||||
/// <summary>
|
||||
/// Data Transfer Object (DTO) для сериализации состояния макета док-системы.
|
||||
/// Содержит все необходимые данные для сохранения и восстановления состояния макета.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Этот DTO является независимым от формата сериализации (JSON, XML, Binary) и используется
|
||||
/// как промежуточное представление между объектной моделью и сериализованными данными.
|
||||
/// </remarks>
|
||||
public class LayoutDto
|
||||
{
|
||||
/// <summary>
|
||||
/// Версия формата DTO для контроля совместимости.
|
||||
/// </summary>
|
||||
public string Version { get; set; } = "1.0";
|
||||
|
||||
/// <summary>
|
||||
/// Дата и время создания DTO в UTC.
|
||||
/// </summary>
|
||||
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
||||
|
||||
/// <summary>
|
||||
/// Идентификатор приложения, создавшего DTO.
|
||||
/// </summary>
|
||||
public string? ApplicationId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Корневой элемент дерева компоновки.
|
||||
/// </summary>
|
||||
public ElementDto? Root { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Список плавающих окон.
|
||||
/// </summary>
|
||||
public List<WindowDto> FloatingWindows { get; set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// Список автоскрываемых панелей.
|
||||
/// </summary>
|
||||
public List<AutoHidePanelDto> AutoHidePanels { get; set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// Дополнительные метаданные, специфичные для приложения.
|
||||
/// </summary>
|
||||
public Dictionary<string, string> Metadata { get; set; } = new();
|
||||
}
|
||||
22
Lattice.Serialization.Docking/DTO/LeafDto.cs
Normal file
22
Lattice.Serialization.Docking/DTO/LeafDto.cs
Normal file
@@ -0,0 +1,22 @@
|
||||
namespace Lattice.Serialization.Docking;
|
||||
|
||||
/// <summary>
|
||||
/// DTO для контейнера вкладок (DockLeaf).
|
||||
/// </summary>
|
||||
public class LeafDto : ElementDto
|
||||
{
|
||||
/// <summary>
|
||||
/// Список ссылок на контент, содержащийся в листе.
|
||||
/// </summary>
|
||||
public List<ContentReferenceDto> Contents { get; set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// Идентификатор активного контента (если есть).
|
||||
/// </summary>
|
||||
public string? ActiveContentId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Расположение вкладок в виде строки.
|
||||
/// </summary>
|
||||
public string TabPlacement { get; set; } = "Bottom";
|
||||
}
|
||||
52
Lattice.Serialization.Docking/DTO/WindowDto.cs
Normal file
52
Lattice.Serialization.Docking/DTO/WindowDto.cs
Normal file
@@ -0,0 +1,52 @@
|
||||
namespace Lattice.Serialization.Docking;
|
||||
|
||||
/// <summary>
|
||||
/// DTO для плавающего окна (DockWindow).
|
||||
/// </summary>
|
||||
public class WindowDto
|
||||
{
|
||||
/// <summary>
|
||||
/// Уникальный идентификатор окна.
|
||||
/// </summary>
|
||||
public string Id { get; set; } = Guid.NewGuid().ToString();
|
||||
|
||||
/// <summary>
|
||||
/// Позиция X окна на экране.
|
||||
/// </summary>
|
||||
public double X { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Позиция Y окна на экране.
|
||||
/// </summary>
|
||||
public double Y { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Ширина окна.
|
||||
/// </summary>
|
||||
public double Width { get; set; } = 800;
|
||||
|
||||
/// <summary>
|
||||
/// Высота окна.
|
||||
/// </summary>
|
||||
public double Height { get; set; } = 600;
|
||||
|
||||
/// <summary>
|
||||
/// Заголовок окна.
|
||||
/// </summary>
|
||||
public string Title { get; set; } = "Lattice Tool Window";
|
||||
|
||||
/// <summary>
|
||||
/// Корневой элемент макета внутри окна.
|
||||
/// </summary>
|
||||
public ElementDto? Root { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Показывает, видимо ли окно.
|
||||
/// </summary>
|
||||
public bool IsVisible { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Показывает, сфокусировано ли окно.
|
||||
/// </summary>
|
||||
public bool IsFocused { get; set; } = false;
|
||||
}
|
||||
103
Lattice.Serialization.Docking/ILayoutSerializer.cs
Normal file
103
Lattice.Serialization.Docking/ILayoutSerializer.cs
Normal file
@@ -0,0 +1,103 @@
|
||||
using Lattice.Core.Docking.Abstractions;
|
||||
using Lattice.Core.Docking.Engine;
|
||||
|
||||
namespace Lattice.Serialization.Docking;
|
||||
|
||||
/// <summary>
|
||||
/// Определяет контракт для сериализации и десериализации состояния макета док-системы.
|
||||
/// Этот интерфейс позволяет реализовать различные форматы сериализации (JSON, XML, Binary)
|
||||
/// и различные хранилища (файлы, базы данных, облако) без изменения основной логики.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Реализации этого интерфейса должны преобразовывать объектную модель док-системы
|
||||
/// в промежуточный DTO (<see cref="LayoutDto"/>), который затем сериализуется в целевой формат.
|
||||
/// Это обеспечивает независимость формата сериализации от структуры DTO.
|
||||
/// </remarks>
|
||||
public interface ILayoutSerializer
|
||||
{
|
||||
/// <summary>
|
||||
/// Получает уникальный идентификатор формата сериализации.
|
||||
/// </summary>
|
||||
/// <value>Строковый идентификатор формата, например "json", "xml", "binary".</value>
|
||||
string FormatId { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Получает MIME-тип формата сериализации.
|
||||
/// </summary>
|
||||
/// <value>MIME-тип, например "application/json", "application/xml".</value>
|
||||
string MimeType { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Получает расширение файла по умолчанию для данного формата.
|
||||
/// </summary>
|
||||
/// <value>Расширение файла, например ".json", ".xml".</value>
|
||||
string DefaultFileExtension { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Сериализует состояние менеджера макета в массив байтов.
|
||||
/// </summary>
|
||||
/// <param name="manager">Менеджер макета для сериализации.</param>
|
||||
/// <returns>Массив байтов, содержащий сериализованное состояние макета.</returns>
|
||||
/// <exception cref="ArgumentNullException">Выбрасывается, если <paramref name="manager"/> равен null.</exception>
|
||||
byte[] Serialize(LayoutManager manager);
|
||||
|
||||
/// <summary>
|
||||
/// Сериализует состояние менеджера макета в строку.
|
||||
/// </summary>
|
||||
/// <param name="manager">Менеджер макета для сериализации.</param>
|
||||
/// <returns>Строковое представление состояния макета.</returns>
|
||||
/// <exception cref="ArgumentNullException">Выбрасывается, если <paramref name="manager"/> равен null.</exception>
|
||||
string SerializeToString(LayoutManager manager);
|
||||
|
||||
/// <summary>
|
||||
/// Сериализует состояние менеджера макета в поток.
|
||||
/// </summary>
|
||||
/// <param name="manager">Менеджер макета для сериализации.</param>
|
||||
/// <param name="stream">Поток для записи сериализованных данных.</param>
|
||||
/// <exception cref="ArgumentNullException">
|
||||
/// Выбрасывается, если <paramref name="manager"/> или <paramref name="stream"/> равен null.
|
||||
/// </exception>
|
||||
void SerializeToStream(LayoutManager manager, Stream stream);
|
||||
|
||||
/// <summary>
|
||||
/// Десериализует состояние макета из массива байтов и восстанавливает его в менеджере.
|
||||
/// </summary>
|
||||
/// <param name="manager">Менеджер макета для восстановления состояния.</param>
|
||||
/// <param name="data">Массив байтов с сериализованным состоянием макета.</param>
|
||||
/// <param name="contentResolver">
|
||||
/// Функция разрешения контента по идентификатору, используемая для восстановления
|
||||
/// ссылок на контент в десериализованном состоянии.
|
||||
/// </param>
|
||||
/// <exception cref="ArgumentNullException">
|
||||
/// Выбрасывается, если <paramref name="manager"/> или <paramref name="data"/> равен null.
|
||||
/// </exception>
|
||||
void Deserialize(LayoutManager manager, byte[] data, Func<string, IDockContent?> contentResolver);
|
||||
|
||||
/// <summary>
|
||||
/// Десериализует состояние макета из строки и восстанавливает его в менеджере.
|
||||
/// </summary>
|
||||
/// <param name="manager">Менеджер макета для восстановления состояния.</param>
|
||||
/// <param name="serializedData">Строка с сериализованным состоянием макета.</param>
|
||||
/// <param name="contentResolver">
|
||||
/// Функция разрешения контента по идентификатору, используемая для восстановления
|
||||
/// ссылок на контент в десериализованном состоянии.
|
||||
/// </param>
|
||||
/// <exception cref="ArgumentNullException">
|
||||
/// Выбрасывается, если <paramref name="manager"/> или <paramref name="serializedData"/> равен null.
|
||||
/// </exception>
|
||||
void DeserializeFromString(LayoutManager manager, string serializedData, Func<string, IDockContent?> contentResolver);
|
||||
|
||||
/// <summary>
|
||||
/// Десериализует состояние макета из потока и восстанавливает его в менеджере.
|
||||
/// </summary>
|
||||
/// <param name="manager">Менеджер макета для восстановления состояния.</param>
|
||||
/// <param name="stream">Поток с сериализованными данными.</param>
|
||||
/// <param name="contentResolver">
|
||||
/// Функция разрешения контента по идентификатору, используемая для восстановления
|
||||
/// ссылок на контент в десериализованном состоянии.
|
||||
/// </param>
|
||||
/// <exception cref="ArgumentNullException">
|
||||
/// Выбрасывается, если <paramref name="manager"/> или <paramref name="stream"/> равен null.
|
||||
/// </exception>
|
||||
void DeserializeFromStream(LayoutManager manager, Stream stream, Func<string, IDockContent?> contentResolver);
|
||||
}
|
||||
19
Lattice.Serialization.Docking/ISerializableContent.cs
Normal file
19
Lattice.Serialization.Docking/ISerializableContent.cs
Normal file
@@ -0,0 +1,19 @@
|
||||
namespace Lattice.Serialization.Docking;
|
||||
|
||||
/// <summary>
|
||||
/// Интерфейс для контента, поддерживающего сериализацию дополнительного состояния.
|
||||
/// </summary>
|
||||
public interface ISerializableContent
|
||||
{
|
||||
/// <summary>
|
||||
/// Получает состояние для сериализации.
|
||||
/// </summary>
|
||||
/// <returns>Словарь свойств и их значений.</returns>
|
||||
Dictionary<string, object?> GetSerializableState();
|
||||
|
||||
/// <summary>
|
||||
/// Восстанавливает состояние из десериализованных данных.
|
||||
/// </summary>
|
||||
/// <param name="state">Словарь свойств и их значений.</param>
|
||||
void RestoreFromState(Dictionary<string, object?> state);
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net8.0;net9.0;net10.0</TargetFrameworks>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Lattice.Core.Docking\Lattice.Core.Docking.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
317
Lattice.Serialization.Docking/LayoutConverter.cs
Normal file
317
Lattice.Serialization.Docking/LayoutConverter.cs
Normal file
@@ -0,0 +1,317 @@
|
||||
using Lattice.Core.Docking.Abstractions;
|
||||
using Lattice.Core.Docking.Engine;
|
||||
using Lattice.Core.Docking.Models;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Lattice.Serialization.Docking;
|
||||
|
||||
/// <summary>
|
||||
/// Предоставляет методы для преобразования между объектной моделью док-системы
|
||||
/// и Data Transfer Objects (DTO) для сериализации.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Этот класс является центральным местом для преобразования между различными представлениями
|
||||
/// данных макета. Он обеспечивает независимость формата сериализации от внутренней структуры
|
||||
/// данных и позволяет легко добавлять новые форматы без изменения логики преобразования.
|
||||
/// </remarks>
|
||||
public static class LayoutConverter
|
||||
{
|
||||
/// <summary>
|
||||
/// Преобразует менеджер макета в DTO для сериализации.
|
||||
/// </summary>
|
||||
/// <param name="manager">Менеджер макета для преобразования.</param>
|
||||
/// <returns>
|
||||
/// DTO, содержащий все данные, необходимые для восстановления состояния макета.
|
||||
/// </returns>
|
||||
/// <exception cref="ArgumentNullException">Выбрасывается, если <paramref name="manager"/> равен null.</exception>
|
||||
public static LayoutDto ConvertToDto(LayoutManager manager)
|
||||
{
|
||||
if (manager == null)
|
||||
throw new ArgumentNullException(nameof(manager));
|
||||
|
||||
var dto = new LayoutDto
|
||||
{
|
||||
ApplicationId = GetApplicationIdentifier()
|
||||
};
|
||||
|
||||
if (manager.Root != null)
|
||||
{
|
||||
dto.Root = ConvertElementToDto(manager.Root);
|
||||
}
|
||||
|
||||
dto.FloatingWindows = manager.FloatingWindows
|
||||
.Select(ConvertWindowToDto)
|
||||
.ToList();
|
||||
|
||||
dto.AutoHidePanels = manager.AutoHidePanels
|
||||
.Select(ConvertAutoHidePanelToDto)
|
||||
.ToList();
|
||||
|
||||
return dto;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Восстанавливает состояние менеджера макета из DTO.
|
||||
/// </summary>
|
||||
/// <param name="manager">Менеджер макета для восстановления состояния.</param>
|
||||
/// <param name="dto">DTO, содержащий данные состояния макета.</param>
|
||||
/// <param name="contentResolver">
|
||||
/// Функция разрешения контента по идентификатору.
|
||||
/// </param>
|
||||
/// <exception cref="ArgumentNullException">
|
||||
/// Выбрасывается, если <paramref name="manager"/> или <paramref name="dto"/> равен null.
|
||||
/// </exception>
|
||||
public static void RestoreFromDto(LayoutManager manager, LayoutDto dto,
|
||||
Func<string, IDockContent?> contentResolver)
|
||||
{
|
||||
if (manager == null)
|
||||
throw new ArgumentNullException(nameof(manager));
|
||||
if (dto == null)
|
||||
throw new ArgumentNullException(nameof(dto));
|
||||
|
||||
// Сбрасываем текущее состояние
|
||||
manager.Reset();
|
||||
|
||||
// Восстанавливаем корневой элемент
|
||||
if (dto.Root != null)
|
||||
{
|
||||
manager.Root = ConvertDtoToElement(dto.Root, contentResolver);
|
||||
}
|
||||
|
||||
// Восстанавливаем плавающие окна
|
||||
foreach (var windowDto in dto.FloatingWindows)
|
||||
{
|
||||
var window = new DockWindow
|
||||
{
|
||||
X = windowDto.X,
|
||||
Y = windowDto.Y,
|
||||
Width = windowDto.Width,
|
||||
Height = windowDto.Height,
|
||||
Title = windowDto.Title
|
||||
};
|
||||
|
||||
if (windowDto.Root != null)
|
||||
{
|
||||
window.Root = ConvertDtoToElement(windowDto.Root, contentResolver);
|
||||
}
|
||||
|
||||
manager.FloatingWindows.Add(window);
|
||||
}
|
||||
|
||||
// Восстанавливаем автоскрываемые панели
|
||||
foreach (var panelDto in dto.AutoHidePanels)
|
||||
{
|
||||
var content = contentResolver(panelDto.Content.Id);
|
||||
if (content != null)
|
||||
{
|
||||
if (Enum.TryParse<DockSide>(panelDto.Side, out var side))
|
||||
{
|
||||
var panel = manager.AddAutoHidePanel(content, side);
|
||||
panel.Size = panelDto.Size;
|
||||
panel.IsVisible = panelDto.IsVisible;
|
||||
panel.SlideOffset = panelDto.SlideOffset;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Преобразует элемент дерева компоновки в DTO.
|
||||
/// </summary>
|
||||
private static ElementDto ConvertElementToDto(IDockElement element)
|
||||
{
|
||||
if (element is DockGroup group)
|
||||
{
|
||||
return new GroupDto
|
||||
{
|
||||
Id = group.Id,
|
||||
Type = "group",
|
||||
Width = group.Width,
|
||||
Height = group.Height,
|
||||
MinWidth = group.MinWidth,
|
||||
MinHeight = group.MinHeight,
|
||||
First = ConvertElementToDto(group.First),
|
||||
Second = ConvertElementToDto(group.Second),
|
||||
Orientation = group.Orientation.ToString(),
|
||||
SplitRatio = group.SplitRatio
|
||||
};
|
||||
}
|
||||
else if (element is DockLeaf leaf)
|
||||
{
|
||||
return new LeafDto
|
||||
{
|
||||
Id = leaf.Id,
|
||||
Type = "leaf",
|
||||
Width = leaf.Width,
|
||||
Height = leaf.Height,
|
||||
MinWidth = leaf.MinWidth,
|
||||
MinHeight = leaf.MinHeight,
|
||||
Contents = leaf.Children.Select(ConvertContentToDto).ToList(),
|
||||
ActiveContentId = leaf.ActiveContent?.Id,
|
||||
TabPlacement = leaf.TabPlacement.ToString()
|
||||
};
|
||||
}
|
||||
|
||||
throw new NotSupportedException($"Element type {element.GetType().Name} is not supported for serialization");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Преобразует контент в DTO ссылки.
|
||||
/// </summary>
|
||||
private static ContentReferenceDto ConvertContentToDto(IDockContent content)
|
||||
{
|
||||
var dto = new ContentReferenceDto
|
||||
{
|
||||
Id = content.Id,
|
||||
TypeId = GetContentTypeId(content),
|
||||
Title = content.Title,
|
||||
CanClose = content.CanClose
|
||||
};
|
||||
|
||||
// Сохраняем дополнительные свойства, если контент поддерживает сериализацию состояния
|
||||
if (content is ISerializableContent serializable)
|
||||
{
|
||||
dto.Properties = serializable.GetSerializableState();
|
||||
}
|
||||
|
||||
return dto;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Преобразует окно в DTO.
|
||||
/// </summary>
|
||||
private static WindowDto ConvertWindowToDto(DockWindow window)
|
||||
{
|
||||
return new WindowDto
|
||||
{
|
||||
Id = window.Id,
|
||||
X = window.X,
|
||||
Y = window.Y,
|
||||
Width = window.Width,
|
||||
Height = window.Height,
|
||||
Title = window.Title,
|
||||
Root = window.Root != null ? ConvertElementToDto(window.Root) : null
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Преобразует автоскрываемую панель в DTO.
|
||||
/// </summary>
|
||||
private static AutoHidePanelDto ConvertAutoHidePanelToDto(AutoHidePanel panel)
|
||||
{
|
||||
return new AutoHidePanelDto
|
||||
{
|
||||
Id = panel.Id,
|
||||
Content = ConvertContentToDto(panel.Content),
|
||||
Side = panel.Side.ToString(),
|
||||
Size = panel.Size,
|
||||
IsVisible = panel.IsVisible,
|
||||
SlideOffset = panel.SlideOffset
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Преобразует DTO обратно в элемент дерева.
|
||||
/// </summary>
|
||||
private static IDockElement ConvertDtoToElement(ElementDto dto, Func<string, IDockContent?> contentResolver)
|
||||
{
|
||||
return dto switch
|
||||
{
|
||||
GroupDto groupDto => ConvertGroupDtoToElement(groupDto, contentResolver),
|
||||
LeafDto leafDto => ConvertLeafDtoToElement(leafDto, contentResolver),
|
||||
_ => throw new NotSupportedException($"Unsupported DTO type: {dto.Type}")
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Преобразует DTO группы в элемент.
|
||||
/// </summary>
|
||||
private static DockGroup ConvertGroupDtoToElement(GroupDto dto, Func<string, IDockContent?> contentResolver)
|
||||
{
|
||||
var group = new DockGroup(
|
||||
ConvertDtoToElement(dto.First, contentResolver),
|
||||
ConvertDtoToElement(dto.Second, contentResolver),
|
||||
Enum.Parse<SplitDirection>(dto.Orientation))
|
||||
{
|
||||
Width = dto.Width,
|
||||
Height = dto.Height,
|
||||
SplitRatio = dto.SplitRatio
|
||||
};
|
||||
|
||||
// Восстанавливаем ID
|
||||
SetElementId(group, dto.Id);
|
||||
|
||||
return group;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Преобразует DTO листа в элемент.
|
||||
/// </summary>
|
||||
private static DockLeaf ConvertLeafDtoToElement(LeafDto dto, Func<string, IDockContent?> contentResolver)
|
||||
{
|
||||
var leaf = new DockLeaf
|
||||
{
|
||||
Width = dto.Width,
|
||||
Height = dto.Height,
|
||||
MinWidth = dto.MinWidth,
|
||||
MinHeight = dto.MinHeight,
|
||||
TabPlacement = Enum.Parse<TabPlacement>(dto.TabPlacement)
|
||||
};
|
||||
|
||||
// Восстанавливаем ID
|
||||
SetElementId(leaf, dto.Id);
|
||||
|
||||
// Восстанавливаем контент
|
||||
foreach (var contentRef in dto.Contents)
|
||||
{
|
||||
var content = contentResolver(contentRef.Id);
|
||||
if (content != null)
|
||||
{
|
||||
// Восстанавливаем состояние контента, если он поддерживает десериализацию
|
||||
if (content is ISerializableContent serializable)
|
||||
{
|
||||
serializable.RestoreFromState(contentRef.Properties);
|
||||
}
|
||||
|
||||
leaf.AddContent(content);
|
||||
}
|
||||
}
|
||||
|
||||
// Восстанавливаем активный контент
|
||||
if (dto.ActiveContentId != null)
|
||||
{
|
||||
leaf.ActiveContent = leaf.Children.FirstOrDefault(c => c.Id == dto.ActiveContentId);
|
||||
}
|
||||
|
||||
return leaf;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Получает идентификатор типа контента.
|
||||
/// </summary>
|
||||
private static string GetContentTypeId(IDockContent content)
|
||||
{
|
||||
// По умолчанию используем имя типа
|
||||
return content.GetType().Name;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Устанавливает ID элемента через рефлексию.
|
||||
/// </summary>
|
||||
private static void SetElementId(object element, string id)
|
||||
{
|
||||
var property = element.GetType().GetProperty("Id");
|
||||
if (property != null && property.CanWrite && property.PropertyType == typeof(string))
|
||||
{
|
||||
property.SetValue(element, id);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Получает идентификатор приложения.
|
||||
/// </summary>
|
||||
private static string GetApplicationIdentifier()
|
||||
{
|
||||
return Assembly.GetEntryAssembly()?.GetName().Name ?? "UnknownApp";
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user