diff --git a/Lattice.Core.Docking/Abstractions/IDockCommand.cs b/Lattice.Core.Docking/Abstractions/IDockCommand.cs
index e7b064f..bcbfaf2 100644
--- a/Lattice.Core.Docking/Abstractions/IDockCommand.cs
+++ b/Lattice.Core.Docking/Abstractions/IDockCommand.cs
@@ -1,8 +1,23 @@
namespace Lattice.Core.Docking.Abstractions;
+///
+/// Определяет контракт для команды в системе докинга.
+/// Команды представляют действия, которые могут быть выполнены над элементами док-системы.
+///
public interface IDockCommand : System.Windows.Input.ICommand
{
+ ///
+ /// Получает отображаемое имя команды.
+ ///
string Name { get; }
+
+ ///
+ /// Получает идентификатор ресурса для иконки команды.
+ ///
string Icon { get; }
+
+ ///
+ /// Получает текстовое представление жеста (горячей клавиши) для команды.
+ ///
string GestureText { get; }
-}
+}
\ No newline at end of file
diff --git a/Lattice.Core.Docking/Abstractions/IDockContainer.cs b/Lattice.Core.Docking/Abstractions/IDockContainer.cs
index 404924d..803d73a 100644
--- a/Lattice.Core.Docking/Abstractions/IDockContainer.cs
+++ b/Lattice.Core.Docking/Abstractions/IDockContainer.cs
@@ -3,22 +3,36 @@
namespace Lattice.Core.Docking.Abstractions;
///
-/// Интерфейс для элементов (листьев дерева), которые физически содержат внутри себя коллекцию вкладок.
+/// Определяет контракт для контейнеров, содержащих коллекцию вкладок.
+/// Контейнеры являются листьями дерева компоновки и непосредственно отображают содержимое.
///
public interface IDockContainer : IDockElement
{
- /// Список вкладок, находящихся в данном контейнере.
+ ///
+ /// Получает список вкладок, находящихся в данном контейнере.
+ ///
IList Children { get; }
- /// Ссылка на текущую выбранную и отображаемую вкладку.
+ ///
+ /// Получает или задает текущую активную (выбранную) вкладку.
+ ///
IDockContent? ActiveContent { get; set; }
- /// Добавляет контент в контейнер и делает его активным.
+ ///
+ /// Добавляет контент в контейнер и делает его активным.
+ ///
+ /// Контент для добавления.
void AddContent(IDockContent content);
- /// Удаляет контент. Если Children становится пустым, контейнер может быть удален из дерева макета.
+ ///
+ /// Удаляет контент из контейнера. Если коллекция становится пустой,
+ /// контейнер может быть удален из дерева макета.
+ ///
+ /// Контент для удаления.
void RemoveContent(IDockContent content);
- /// Положение вкладок в интерфейсе.
+ ///
+ /// Получает или задает положение панели вкладок в интерфейсе.
+ ///
TabPlacement TabPlacement { get; set; }
-}
+}
\ No newline at end of file
diff --git a/Lattice.Core.Docking/Abstractions/IDockContent.cs b/Lattice.Core.Docking/Abstractions/IDockContent.cs
index cd719d5..35a2446 100644
--- a/Lattice.Core.Docking/Abstractions/IDockContent.cs
+++ b/Lattice.Core.Docking/Abstractions/IDockContent.cs
@@ -1,25 +1,37 @@
namespace Lattice.Core.Docking.Abstractions;
///
-/// Описывает объект содержимого (вкладку), который может быть размещен внутри IDockContainer.
+/// Определяет контракт для содержимого (вкладки), которое может быть размещено внутри контейнера.
///
public interface IDockContent
{
- /// Уникальный идентификатор контента (например, путь к файлу или ID инструмента).
+ ///
+ /// Получает уникальный идентификатор контента.
+ /// Используется для идентификации вкладки в системе.
+ ///
string Id { get; }
- /// Заголовок, отображаемый пользователю в интерфейсе (на вкладке).
+ ///
+ /// Получает заголовок, отображаемый пользователю на вкладке.
+ ///
string Title { get; }
- ///
- /// Сам визуальный элемент (например, Microsoft.UI.Xaml.UIElement).
- /// Lattice просто отображает этот объект в теле вкладки.
+ ///
+ /// Получает или задает визуальный элемент для отображения в теле вкладки.
///
object View { get; set; }
- /// Флаг, определяющий доступность кнопки закрытия для пользователя.
+ ///
+ /// Получает значение, указывающее, можно ли закрыть вкладку.
+ ///
bool CanClose { get; }
- /// Вызывается системой при попытке закрытия контента. Возвращает true, если закрытие разрешено.
+ ///
+ /// Вызывается системой при попытке закрытия контента.
+ /// Позволяет выполнить дополнительные проверки или сохранить состояние.
+ ///
+ ///
+ /// true, если закрытие разрешено; в противном случае false.
+ ///
bool OnClosing();
-}
+}
\ No newline at end of file
diff --git a/Lattice.Core.Docking/Abstractions/IDockElement.cs b/Lattice.Core.Docking/Abstractions/IDockElement.cs
index 9131a85..39b49aa 100644
--- a/Lattice.Core.Docking/Abstractions/IDockElement.cs
+++ b/Lattice.Core.Docking/Abstractions/IDockElement.cs
@@ -1,25 +1,91 @@
namespace Lattice.Core.Docking.Abstractions;
///
-/// Базовый интерфейс для любого элемента, который может быть частью дерева компоновки Lattice.
+/// Базовый интерфейс для любого элемента, являющегося частью дерева компоновки.
+/// Определяет общие свойства и методы для всех элементов док-системы.
///
+///
+/// Элементы док-системы образуют древовидную структуру, где каждый элемент может иметь
+/// родителя и дочерние элементы. Эта иерархия используется для организации пространства
+/// главного окна и плавающих окон в IDE-подобных приложениях.
+///
public interface IDockElement
{
- /// Уникальный идентификатор элемента.
+ ///
+ /// Получает уникальный идентификатор элемента.
+ /// Используется для поиска элементов, сериализации состояния и отслеживания изменений.
+ ///
+ ///
+ /// Строковый идентификатор, гарантированно уникальный в пределах дерева компоновки.
+ /// Обычно представляет собой GUID в строковом формате.
+ ///
string Id { get; }
- /// Родительский элемент в иерархии. Если null — элемент является корневым.
+ ///
+ /// Получает или задает родительский элемент в иерархии дерева компоновки.
+ ///
+ ///
+ /// Родительский элемент или null, если элемент является корневым.
+ /// Это свойство управляется системой компоновки при добавлении или удалении элементов.
+ ///
+ ///
+ /// Изменение этого свойства вручную может привести к нарушению целостности дерева.
+ /// Для манипуляции структурой дерева следует использовать методы .
+ ///
IDockElement? Parent { get; set; }
- /// Желаемая ширина элемента в относительных или абсолютных единицах.
+ ///
+ /// Получает или задает желаемую ширину элемента.
+ ///
+ ///
+ /// Ширина элемента в пикселях или относительных единицах.
+ /// Может быть выражена как абсолютное значение (в пикселях) или как пропорция
+ /// (например, 0.5 для 50% доступного пространства).
+ ///
+ ///
+ /// Фактическая ширина элемента определяется родительским контейнером с учетом
+ /// минимальных размеров и соотношений разделения.
+ ///
double Width { get; set; }
- /// Желаемая высота элемента в относительных или абсолютных единицах.
+ ///
+ /// Получает или задает желаемую высоту элемента.
+ ///
+ ///
+ /// Высота элемента в пикселях или относительных единицах.
+ /// Может быть выражена как абсолютное значение (в пикселях) или как пропорция.
+ ///
+ ///
+ /// Фактическая высота элемента определяется родительским контейнером с учетом
+ /// минимальных размеров и соотношений разделения.
+ ///
double Height { get; set; }
- /// Минимально допустимая ширина, при которой элемент сохраняет функциональность.
+ ///
+ /// Получает минимально допустимую ширину элемента.
+ ///
+ ///
+ /// Минимальная ширина элемента в пикселях, при которой элемент сохраняет
+ /// базовую функциональность и читаемость содержимого.
+ ///
+ ///
+ /// Система компоновки не позволит уменьшить элемент ниже этого значения.
+ /// Для групп разделения минимальная ширина вычисляется рекурсивно на основе
+ /// минимальных размеров дочерних элементов.
+ ///
double MinWidth { get; }
- /// Минимально допустимая высота, при которой элемент сохраняет функциональность.
+ ///
+ /// Получает минимально допустимую высоту элемента.
+ ///
+ ///
+ /// Минимальная высота элемента в пикселях, при которой элемент сохраняет
+ /// базовую функциональность и читаемость содержимого.
+ ///
+ ///
+ /// Система компоновки не позволит уменьшить элемент ниже этого значения.
+ /// Для групп разделения минимальная высота вычисляется рекурсивно на основе
+ /// минимальных размеров дочерних элементов.
+ ///
double MinHeight { get; }
-}
+}
\ No newline at end of file
diff --git a/Lattice.Core.Docking/Abstractions/IDockElementDragSource.cs b/Lattice.Core.Docking/Abstractions/IDockElementDragSource.cs
deleted file mode 100644
index 42ac214..0000000
--- a/Lattice.Core.Docking/Abstractions/IDockElementDragSource.cs
+++ /dev/null
@@ -1,19 +0,0 @@
-using Lattice.Core.DragDrop.Abstractions;
-
-namespace Lattice.Core.Docking.Abstractions;
-
-///
-/// Расширяет интерфейс элемента док-системы для поддержки операций перетаскивания.
-///
-public interface IDockElementDragSource : IDockElement, IDragSource
-{
- ///
- /// Получает или устанавливает признак того, что элемент можно перетаскивать.
- ///
- bool CanDrag { get; set; }
-
- ///
- /// Получает тип данных для перетаскивания этого элемента.
- ///
- string DragDataType { get; }
-}
diff --git a/Lattice.Core.Docking/Abstractions/IDockElementDropTarget.cs b/Lattice.Core.Docking/Abstractions/IDockElementDropTarget.cs
deleted file mode 100644
index 642a810..0000000
--- a/Lattice.Core.Docking/Abstractions/IDockElementDropTarget.cs
+++ /dev/null
@@ -1,19 +0,0 @@
-using Lattice.Core.DragDrop.Abstractions;
-
-namespace Lattice.Core.Docking.Abstractions;
-
-///
-/// Расширяет интерфейс элемента док-системы для возможности быть целью сброса.
-///
-public interface IDockElementDropTarget : IDockElement, IDropTarget
-{
- ///
- /// Получает или устанавливает признак того, что элемент может принимать сброс.
- ///
- bool CanDrop { get; set; }
-
- ///
- /// Получает типы данных, которые может принимать элемент.
- ///
- IEnumerable AcceptableDropTypes { get; }
-}
\ No newline at end of file
diff --git a/Lattice.Core.Docking/Abstractions/IDragService.cs b/Lattice.Core.Docking/Abstractions/IDragService.cs
deleted file mode 100644
index 1fbc4bc..0000000
--- a/Lattice.Core.Docking/Abstractions/IDragService.cs
+++ /dev/null
@@ -1,76 +0,0 @@
-using Lattice.Core.Docking.Models;
-using Lattice.Core.Geometry;
-
-namespace Lattice.Core.Docking.Abstractions;
-
-///
-/// Предоставляет абстракцию для операции перетаскивания в док-системе.
-/// Эта абстракция позволяет отделить логику перетаскивания от конкретной UI-платформы.
-///
-public interface IDragService
-{
- ///
- /// Начинает операцию перетаскивания указанного элемента.
- ///
- /// Элемент для перетаскивания.
- /// Визуальная обратная связь (зависит от платформы).
- void StartDrag(IDockElement element, object? visualFeedback = null);
-
- ///
- /// Обновляет позицию перетаскивания.
- ///
- /// Координата X.
- /// Координата Y.
- void UpdateDrag(double x, double y);
-
- ///
- /// Завершает операцию перетаскивания.
- ///
- /// Координата X завершения.
- /// Координата Y завершения.
- void EndDrag(double x, double y);
-
- ///
- /// Отменяет операцию перетаскивания.
- ///
- void CancelDrag();
-}
-
-///
-/// Представляет область для сброса при операции перетаскивания.
-///
-public class DropArea
-{
- ///
- /// Целевой элемент для сброса.
- ///
- public IDockElement Target { get; set; }
-
- ///
- /// Позиция сброса относительно цели.
- ///
- public DockPosition Position { get; set; }
-
- ///
- /// Границы области в экранных координатах.
- ///
- public Rect Bounds { get; set; }
-
- ///
- /// Видимость области (для анимации).
- ///
- public double Visibility { get; set; } = 0.0;
-
- ///
- /// Инициализирует новый экземпляр области сброса.
- ///
- /// Целевой элемент.
- /// Позиция сброса.
- /// Границы области.
- public DropArea(IDockElement target, DockPosition position, Rect bounds)
- {
- Target = target;
- Position = position;
- Bounds = bounds;
- }
-}
\ No newline at end of file
diff --git a/Lattice.Core.Docking/Engine/DockOperations.cs b/Lattice.Core.Docking/Engine/DockOperations.cs
index 4c6a5c5..b3c5bf6 100644
--- a/Lattice.Core.Docking/Engine/DockOperations.cs
+++ b/Lattice.Core.Docking/Engine/DockOperations.cs
@@ -4,20 +4,30 @@ using Lattice.Core.Docking.Models;
namespace Lattice.Core.Docking.Engine;
///
-/// Статический движок для манипуляции иерархией дерева компоновки.
-/// Содержит чистые алгоритмы трансформации графа.
+/// Предоставляет статические методы для манипуляции иерархией дерева компоновки.
+/// Содержит чистые алгоритмы трансформации графа без зависимости от UI.
///
public static class DockOperations
{
///
- /// Извлекает элемент из дерева. Если родительская группа остается с одним ребенком,
- /// она удаляется, а ребенок занимает её место.
+ /// Извлекает элемент из дерева компоновки.
+ /// Если родительская группа остается с одним ребенком, она удаляется,
+ /// а оставшийся ребенок занимает её место в иерархии.
///
- /// Элемент для удаления.
- /// Текущий корень дерева.
- /// Новый корень дерева после оптимизации.
+ /// Элемент для удаления из дерева.
+ /// Текущий корневой элемент дерева.
+ ///
+ /// Новый корневой элемент дерева после удаления и оптимизации структуры.
+ /// Возвращает null, если дерево становится пустым.
+ ///
+ ///
+ /// Выбрасывается, когда равен null.
+ ///
public static IDockElement? Remove(IDockElement element, IDockElement root)
{
+ if (element == null) throw new ArgumentNullException(nameof(element));
+ if (root == null) throw new ArgumentNullException(nameof(root));
+
if (element == root) return null;
var parent = element.Parent as DockGroup;
@@ -43,15 +53,36 @@ public static class DockOperations
}
///
- /// Вставляет элемент в дерево, создавая новую группу разделения или объединяя контент.
+ /// Вставляет элемент в дерево компоновки относительно целевого элемента.
+ /// Создает новую группу разделения или объединяет контент в зависимости от позиции.
///
- public static IDockElement Insert(IDockElement target, IDockElement source, DockPosition pos, IDockElement root)
+ /// Целевой элемент, относительно которого выполняется вставка.
+ /// Вставляемый элемент.
+ /// Позиция вставки относительно целевого элемента.
+ /// Текущий корневой элемент дерева.
+ ///
+ /// Новый корневой элемент дерева после вставки и оптимизации структуры.
+ ///
+ ///
+ /// Выбрасывается, когда ,
+ /// или равны null.
+ ///
+ ///
+ /// Выбрасывается, когда имеет недопустимое значение.
+ ///
+ public static IDockElement Insert(IDockElement target, IDockElement source,
+ DockPosition pos, IDockElement root)
{
+ if (target == null) throw new ArgumentNullException(nameof(target));
+ if (source == null) throw new ArgumentNullException(nameof(source));
+ if (root == null) throw new ArgumentNullException(nameof(root));
+
// Случай 1: Объединение вкладок в центре
if (pos == DockPosition.Center)
{
if (target is IDockContainer targetContainer && source is IDockContainer sourceContainer)
{
+ // Переносим все вкладки из источника в целевой контейнер
var items = new List(sourceContainer.Children);
foreach (var item in items)
{
diff --git a/Lattice.Core.Docking/Engine/LayoutManager.cs b/Lattice.Core.Docking/Engine/LayoutManager.cs
index f652021..64c33d7 100644
--- a/Lattice.Core.Docking/Engine/LayoutManager.cs
+++ b/Lattice.Core.Docking/Engine/LayoutManager.cs
@@ -8,54 +8,87 @@ using System.Runtime.CompilerServices;
namespace Lattice.Core.Docking.Engine;
///
-/// Расширенный менеджер макета, поддерживающий автоскрываемые панели, группы документов
-/// и расширенные операции управления макетом.
+/// Центральный менеджер макета, управляющий всей структурой док-системы.
+/// Координирует дерево компоновки, плавающие окна, автоскрываемые панели
+/// и предоставляет API для манипуляции макетом.
///
///
-/// Этот класс является центральным координатором всей док-системы, управляя деревом компоновки,
-/// плавающими окнами, автоскрываемыми панелями и предоставляя API для манипуляции макетом.
+/// Этот класс является основным координатором док-системы. Он управляет:
+///
+/// - Деревом компоновки главного окна
+/// - Коллекцией плавающих окон
+/// - Коллекцией автоскрываемых панелей
+/// - Реестром типов контента
+///
+/// Все изменения в структуре макета должны выполняться через методы этого класса.
///
public class LayoutManager
{
private readonly ObservableCollection _autoHidePanels = new();
+ private IDockElement? _root;
///
- /// Корневой элемент главного окна IDE.
+ /// Получает или задает корневой элемент дерева компоновки главного окна.
///
- public IDockElement? Root { get; internal set; }
+ ///
+ /// Корневой элемент или null, если макет пуст.
+ ///
+ ///
+ /// При изменении этого свойства генерируется событие .
+ ///
+ public IDockElement? Root
+ {
+ get => _root;
+ internal set
+ {
+ if (_root != value)
+ {
+ _root = value;
+ LayoutUpdated?.Invoke();
+ }
+ }
+ }
///
- /// Список активных плавающих окон.
+ /// Получает список активных плавающих окон.
///
+ ///
+ /// Коллекция объектов , представляющих плавающие окна.
+ ///
public List FloatingWindows { get; } = new();
///
- /// Коллекция автоскрываемых панелей.
+ /// Получает коллекцию автоскрываемых панелей.
///
+ ///
+ /// Доступная только для чтения коллекция объектов .
+ ///
public ReadOnlyObservableCollection AutoHidePanels { get; }
///
- /// Реестр типов контента (опционально).
+ /// Получает или задает реестр типов контента.
///
+ ///
+ /// Реестр типов контента или null, если реестр не установлен.
+ ///
public Services.ContentRegistry? ContentRegistry { get; set; }
///
- /// Уведомляет UI, что структура дерева изменилась.
+ /// Происходит при изменении структуры дерева компоновки.
///
+ ///
+ /// Событие генерируется при любых изменениях в дереве компоновки,
+ /// включая добавление, удаление или перемещение элементов.
+ ///
public event Action? LayoutUpdated;
///
- /// Уведомляет об изменении в коллекции автоскрываемых панелей.
+ /// Происходит при изменении коллекции автоскрываемых панелей.
///
public event EventHandler? AutoHidePanelsChanged;
///
- /// Событие, возникающее при операции перетаскивания элемента.
- ///
- public event EventHandler? DragDropOperation;
-
- ///
- /// Инициализирует новый экземпляр менеджера макета.
+ /// Инициализирует новый экземпляр класса .
///
public LayoutManager()
{
@@ -63,13 +96,20 @@ public class LayoutManager
}
///
- /// Добавляет автоскрываемую панель.
+ /// Добавляет автоскрываемую панель с указанным содержимым к заданной стороне окна.
///
/// Содержимое панели.
- /// Сторона для прикрепления.
- /// Созданная автоскрываемая панель.
+ /// Сторона окна для прикрепления панели.
+ ///
+ /// Созданная автоскрываемая панель.
+ ///
+ ///
+ /// Выбрасывается, когда равен null.
+ ///
public AutoHidePanel AddAutoHidePanel(IDockContent content, DockSide side)
{
+ if (content == null) throw new ArgumentNullException(nameof(content));
+
var panel = new AutoHidePanel(content, side);
_autoHidePanels.Add(panel);
AutoHidePanelsChanged?.Invoke(this, EventArgs.Empty);
@@ -77,23 +117,36 @@ public class LayoutManager
}
///
- /// Удаляет автоскрываемую панель.
+ /// Удаляет автоскрываемую панель из коллекции.
///
/// Панель для удаления.
- public void RemoveAutoHidePanel(AutoHidePanel panel)
+ ///
+ /// true, если панель была успешно удалена; в противном случае false.
+ ///
+ ///
+ /// Выбрасывается, когда равен null.
+ ///
+ public bool RemoveAutoHidePanel(AutoHidePanel panel)
{
+ if (panel == null) throw new ArgumentNullException(nameof(panel));
+
if (_autoHidePanels.Remove(panel))
{
AutoHidePanelsChanged?.Invoke(this, EventArgs.Empty);
+ return true;
}
+ return false;
}
///
- /// Создает документ из зарегистрированного типа контента.
+ /// Создает документ указанного типа контента с заданным идентификатором.
///
/// Идентификатор типа контента.
/// Уникальный идентификатор документа.
- /// Созданный контент или null, если ContentRegistry не установлен.
+ ///
+ /// Созданный контент или null, если ContentRegistry не установлен
+ /// или тип контента не зарегистрирован.
+ ///
public IDockContent? CreateDocument(string contentTypeId, string id)
{
if (ContentRegistry == null || !ContentRegistry.IsRegistered(contentTypeId))
@@ -103,16 +156,31 @@ public class LayoutManager
}
///
- /// Основной метод перемещения элементов в макете.
+ /// Выполняет перемещение элемента в макете относительно целевого элемента.
///
- /// Что перетаскиваем.
- /// Куда приземляем.
- /// Позиция относительно цели.
+ /// Перемещаемый элемент.
+ /// Целевой элемент, относительно которого выполняется перемещение.
+ /// Позиция перемещения относительно цели.
///
/// Если true, контент будет добавлен как документ в центральную область.
+ /// В текущей реализации этот параметр не используется.
///
- public void Move(IDockElement source, IDockElement? target, DockPosition position, bool asDocument = false)
+ ///
+ /// Выбрасывается, когда равен null.
+ ///
+ ///
+ /// Метод выполняет следующие шаги:
+ ///
+ /// - Удаляет источник из текущего местоположения
+ /// - Вставляет источник в новое местоположение относительно цели
+ /// - Обновляет структуру дерева компоновки
+ ///
+ /// Если равен null, элемент помещается в новое плавающее окно.
+ ///
+ public void Move(IDockElement source, IDockElement? target,
+ DockPosition position, bool asDocument = false)
{
+ if (source == null) throw new ArgumentNullException(nameof(source));
if (source == target) return;
// 1. Удаляем источник из текущего местоположения
@@ -145,13 +213,14 @@ public class LayoutManager
// 2. Вставляем в цель
if (target == null)
{
- FloatingWindows.Add(new DockWindow { Root = source as IDockElement });
+ // Создаем новое плавающее окно
+ FloatingWindows.Add(new DockWindow { Root = source });
}
else
{
- if (IsDescendantOf(target, Root))
+ if (Root != null && IsDescendantOf(target, Root))
{
- Root = DockOperations.Insert(target, source, position, Root!);
+ Root = DockOperations.Insert(target, source, position, Root);
}
else
{
@@ -162,6 +231,13 @@ public class LayoutManager
LayoutUpdated?.Invoke();
}
+ ///
+ /// Удаляет элемент из всех плавающих окон.
+ ///
+ /// Элемент для удаления.
+ ///
+ /// true, если элемент был найден и удален; в противном случае false.
+ ///
private bool RemoveFromFloatingWindows(IDockElement element)
{
foreach (var win in FloatingWindows.ToArray())
@@ -177,7 +253,14 @@ public class LayoutManager
return false;
}
- private void InsertIntoFloatingWindow(IDockElement target, IDockElement source, DockPosition position)
+ ///
+ /// Вставляет элемент в плавающее окно, содержащее целевой элемент.
+ ///
+ /// Целевой элемент в плавающем окне.
+ /// Вставляемый элемент.
+ /// Позиция вставки.
+ private void InsertIntoFloatingWindow(IDockElement target, IDockElement source,
+ DockPosition position)
{
foreach (var win in FloatingWindows)
{
@@ -189,6 +272,14 @@ public class LayoutManager
}
}
+ ///
+ /// Определяет, является ли элемент потомком указанного предка.
+ ///
+ /// Проверяемый элемент.
+ /// Предполагаемый предок.
+ ///
+ /// true, если элемент является потомком предка; в противном случае false.
+ ///
private bool IsDescendantOf(IDockElement element, IDockElement ancestor)
{
if (element == ancestor) return true;
@@ -197,9 +288,17 @@ public class LayoutManager
return false;
}
- /// Поиск элемента по ID во всех окнах.
+ ///
+ /// Находит элемент по его идентификатору во всех окнах (главном и плавающих).
+ ///
+ /// Идентификатор элемента для поиска.
+ ///
+ /// Найденный элемент или null, если элемент с таким идентификатором не найден.
+ ///
public IDockElement? FindById(string id)
{
+ if (string.IsNullOrEmpty(id)) return null;
+
var found = FindRecursive(Root, id);
if (found != null) return found;
@@ -211,16 +310,37 @@ public class LayoutManager
return null;
}
+ ///
+ /// Рекурсивно ищет элемент по идентификатору в поддереве.
+ ///
+ /// Корневой узел поддерева для поиска.
+ /// Идентификатор элемента для поиска.
+ ///
+ /// Найденный элемент или null, если элемент не найден.
+ ///
private IDockElement? FindRecursive(IDockElement? node, string id)
{
- if (node == null || node.Id == id) return node;
- if (node is DockGroup g) return FindRecursive(g.First, id) ?? FindRecursive(g.Second, id);
+ if (node == null) return null;
+ if (node.Id == id) return node;
+
+ if (node is DockGroup g)
+ return FindRecursive(g.First, id) ?? FindRecursive(g.Second, id);
+
return null;
}
///
/// Сбрасывает макет к состоянию по умолчанию.
///
+ ///
+ /// Метод выполняет следующие действия:
+ ///
+ /// - Очищает корневой элемент
+ /// - Закрывает все плавающие окна
+ /// - Удаляет все автоскрываемые панели
+ /// - Генерирует соответствующие события
+ ///
+ ///
public void Reset()
{
Root = null;
@@ -231,86 +351,12 @@ public class LayoutManager
}
///
- /// Обрабатывает операцию перетаскивания между элементами.
- ///
- /// Источник перетаскивания.
- /// Цель сброса.
- /// Позиция сброса относительно цели.
- /// Данные перетаскивания.
- /// true, если операция успешно выполнена; иначе false.
- public bool HandleDragDrop(IDockElement source, IDockElement target,
- DockPosition position, object data)
- {
- try
- {
- if (source == target)
- return false;
-
- // Определяем тип операции на основе данных
- if (data is ContentDragData contentData)
- {
- return HandleContentDragDrop(contentData, target, position);
- }
- else if (data is DockElementDragData elementData)
- {
- return HandleElementDragDrop(elementData, target, position);
- }
-
- return false;
- }
- catch (Exception ex)
- {
- DragDropOperation?.Invoke(this, new DragDropEventArgs(
- source, target, position, false, ex.Message));
- return false;
- }
- }
-
- private bool HandleContentDragDrop(ContentDragData data, IDockElement target, DockPosition position)
- {
- // Находим исходный контейнер с контентом
- var sourceContainer = FindElementById(data.ElementId) as IDockContainer;
- if (sourceContainer == null)
- return false;
-
- // Находим контент
- var content = sourceContainer.Children.FirstOrDefault(c => c.Id == data.ContentId);
- if (content == null)
- return false;
-
- if (target is IDockContainer targetContainer && position == DockPosition.Center)
- {
- // Объединение вкладок
- sourceContainer.RemoveContent(content);
- targetContainer.AddContent(content);
-
- DragDropOperation?.Invoke(this, new DragDropEventArgs(
- sourceContainer as IDockElement ?? sourceContainer as IDockElement,
- target, position, true, "Content merged"));
- return true;
- }
-
- return false;
- }
-
- private bool HandleElementDragDrop(DockElementDragData data, IDockElement target, DockPosition position)
- {
- // Находим перетаскиваемый элемент
- var sourceElement = FindElementById(data.ElementId);
- if (sourceElement == null)
- return false;
-
- // Выполняем перемещение
- Move(sourceElement, target, position);
-
- DragDropOperation?.Invoke(this, new DragDropEventArgs(
- sourceElement, target, position, true, "Element moved"));
- return true;
- }
-
- ///
- /// Находит элемент по идентификатору.
+ /// Находит элемент по идентификатору в дереве компоновки.
///
+ /// Идентификатор элемента для поиска.
+ ///
+ /// Найденный элемент или null, если элемент с таким идентификатором не найден.
+ ///
public IDockElement? FindElementById(string id)
{
return FindElementByIdRecursive(Root, id) ??
@@ -318,6 +364,14 @@ public class LayoutManager
.FirstOrDefault(result => result != null);
}
+ ///
+ /// Рекурсивно ищет элемент по идентификатору в поддереве.
+ ///
+ /// Корневой элемент поддерева для поиска.
+ /// Идентификатор элемента для поиска.
+ ///
+ /// Найденный элемент или null, если элемент не найден.
+ ///
private IDockElement? FindElementByIdRecursive(IDockElement? element, string id)
{
if (element == null) return null;
@@ -331,39 +385,4 @@ public class LayoutManager
return null;
}
-}
-
-///
-/// Аргументы события операции перетаскивания.
-///
-public class DragDropEventArgs : EventArgs
-{
- /// Источник перетаскивания.
- public IDockElement Source { get; }
-
- /// Цель сброса.
- public IDockElement Target { get; }
-
- /// Позиция сброса.
- public DockPosition Position { get; }
-
- /// Показывает, была ли операция успешной.
- public bool Success { get; }
-
- /// Сообщение о результате операции.
- public string Message { get; }
-
- /// Время выполнения операции.
- public DateTime Timestamp { get; }
-
- public DragDropEventArgs(IDockElement source, IDockElement target,
- DockPosition position, bool success, string message)
- {
- Source = source;
- Target = target;
- Position = position;
- Success = success;
- Message = message;
- Timestamp = DateTime.UtcNow;
- }
}
\ No newline at end of file
diff --git a/Lattice.Core.Docking/Lattice.Core.Docking.csproj b/Lattice.Core.Docking/Lattice.Core.Docking.csproj
index a05bde6..a571f74 100644
--- a/Lattice.Core.Docking/Lattice.Core.Docking.csproj
+++ b/Lattice.Core.Docking/Lattice.Core.Docking.csproj
@@ -8,6 +8,5 @@
-
diff --git a/Lattice.Core.Docking/Models/AutoHidePanel.cs b/Lattice.Core.Docking/Models/AutoHidePanel.cs
index 03df5ed..fe2a0d3 100644
--- a/Lattice.Core.Docking/Models/AutoHidePanel.cs
+++ b/Lattice.Core.Docking/Models/AutoHidePanel.cs
@@ -5,44 +5,81 @@ namespace Lattice.Core.Docking.Models;
///
/// Представляет автоскрываемую панель, которая может быть прикреплена к одной из сторон окна.
-/// Автоскрываемые панели скрываются, оставляя только заголовок, и появляются при наведении курсора.
+/// Автоскрываемые панели скрываются, оставляя видимой только полоску-заголовок,
+/// и разворачиваются при наведении курсора или клике.
///
///
-/// Автоскрываемые панели являются ключевым элементом интерфейса современных IDE,
+/// Автоскрываемые панели являются важным элементом современных IDE-подобных приложений,
/// позволяя экономить пространство экрана при сохранении быстрого доступа к инструментам.
///
public class AutoHidePanel : INotifyPropertyChanged
{
+ ///
+ /// Происходит при изменении значения свойства.
+ ///
public event PropertyChangedEventHandler? PropertyChanged;
- protected void OnPropertyChanged([CallerMemberName] string? name = null) =>
- PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
private bool _isVisible = false;
private double _slideOffset = 0;
///
- /// Уникальный идентификатор автоскрываемой панели.
+ /// Получает уникальный идентификатор автоскрываемой панели.
///
+ ///
+ /// Строковый идентификатор, сгенерированный с помощью GUID.
+ ///
public string Id { get; } = Guid.NewGuid().ToString();
///
- /// Содержимое панели.
+ /// Получает или задает содержимое панели.
///
- public Abstractions.IDockContent Content { get; set; }
+ ///
+ /// Объект, реализующий .
+ ///
+ ///
+ /// Выбрасывается при попытке установить значение null.
+ ///
+ public Abstractions.IDockContent Content
+ {
+ get => _content;
+ set
+ {
+ if (_content != value)
+ {
+ _content = value ?? throw new ArgumentNullException(nameof(value));
+ OnPropertyChanged();
+ OnPropertyChanged(nameof(Title));
+ }
+ }
+ }
+ private Abstractions.IDockContent _content;
///
- /// Сторона окна, к которой прикреплена панель.
+ /// Получает или задает сторону окна, к которой прикреплена панель.
///
+ ///
+ /// Значение перечисления , указывающее сторону прикрепления.
+ ///
public DockSide Side { get; set; }
///
- /// Ширина панели (для левой/правой сторон) или высота (для верхней/нижней сторон).
+ /// Получает или задает ширину панели (для левой/правой сторон)
+ /// или высоту (для верхней/нижней сторон).
///
+ ///
+ /// Размер панели в пикселях. Значение по умолчанию: 300.
+ ///
public double Size { get; set; } = 300;
///
- /// Признак видимости панели.
+ /// Получает или задает признак видимости панели.
///
+ ///
+ /// true, если панель развернута и видима; в противном случае false.
+ ///
+ ///
+ /// При изменении этого свойства генерируется событие .
+ ///
public bool IsVisible
{
get => _isVisible;
@@ -57,8 +94,14 @@ public class AutoHidePanel : INotifyPropertyChanged
}
///
- /// Смещение для анимации выезда/заезда панели (0-1).
+ /// Получает или задает смещение для анимации выезда/заезда панели.
///
+ ///
+ /// Значение от 0.0 до 1.0, где 0.0 - полностью скрыта, 1.0 - полностью развернута.
+ ///
+ ///
+ /// Используется для плавной анимации отображения/скрытия панели.
+ ///
public double SlideOffset
{
get => _slideOffset;
@@ -73,15 +116,22 @@ public class AutoHidePanel : INotifyPropertyChanged
}
///
- /// Заголовок панели (обычно берется из содержимого).
+ /// Получает заголовок панели.
///
+ ///
+ /// Заголовок, взятый из содержимого панели.
+ /// Если содержимое не установлено, возвращает "Auto-hide Panel".
+ ///
public string Title => Content?.Title ?? "Auto-hide Panel";
///
- /// Инициализирует новый экземпляр автоскрываемой панели.
+ /// Инициализирует новый экземпляр класса .
///
/// Содержимое панели.
/// Сторона окна для прикрепления.
+ ///
+ /// Выбрасывается, когда равен null.
+ ///
public AutoHidePanel(Abstractions.IDockContent content, DockSide side)
{
Content = content ?? throw new ArgumentNullException(nameof(content));
@@ -91,6 +141,9 @@ public class AutoHidePanel : INotifyPropertyChanged
///
/// Переключает видимость панели.
///
+ ///
+ /// Если панель была видимой, становится скрытой, и наоборот.
+ ///
public void Toggle()
{
IsVisible = !IsVisible;
@@ -111,4 +164,15 @@ public class AutoHidePanel : INotifyPropertyChanged
{
IsVisible = false;
}
-}
+
+ ///
+ /// Вызывает событие .
+ ///
+ ///
+ /// Имя изменившегося свойства. Если не указано, определяется автоматически.
+ ///
+ protected void OnPropertyChanged([CallerMemberName] string? name = null)
+ {
+ PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
+ }
+}
\ No newline at end of file
diff --git a/Lattice.Core.Docking/Models/DockGroup.cs b/Lattice.Core.Docking/Models/DockGroup.cs
index 08bc6ff..c6745cd 100644
--- a/Lattice.Core.Docking/Models/DockGroup.cs
+++ b/Lattice.Core.Docking/Models/DockGroup.cs
@@ -1,8 +1,4 @@
using Lattice.Core.Docking.Abstractions;
-using Lattice.Core.DragDrop.Abstractions;
-using Lattice.Core.DragDrop.Enums;
-using Lattice.Core.DragDrop.Models;
-using Lattice.Core.Geometry;
using System.ComponentModel;
using System.Runtime.CompilerServices;
@@ -14,32 +10,25 @@ namespace Lattice.Core.Docking.Models;
/// элементом для создания сложных макетов с разделителями.
///
///
-///
-/// реализует как (для
-/// возможности перетаскивания всей группы), так и
-/// (для возможности сброса на группу), что делает его полностью интегрированным
-/// в систему перетаскивания док-системы.
-///
-///
-/// Каждая группа содержит два дочерних элемента ( и
-/// ), которые могут быть либо другими группами (для
-/// создания вложенной структуры), либо листами ( )
-/// с контентом. Направление разделения определяется свойством
-/// .
-///
+/// Каждая группа содержит два дочерних элемента ( и ),
+/// которые могут быть либо другими группами (для создания вложенной структуры),
+/// либо листами ( ) с контентом.
+/// Направление разделения определяется свойством .
///
-public class DockGroup : IDockElement, IDragSource, IDropTarget, INotifyPropertyChanged
+public class DockGroup : IDockElement, INotifyPropertyChanged
{
///
- /// Событие, возникающее при изменении значения свойства.
+ /// Происходит при изменении значения свойства.
///
public event PropertyChangedEventHandler? PropertyChanged;
private double _splitRatio = 0.5;
private string _id;
+ private IDockElement _first;
+ private IDockElement _second;
///
- /// Получает уникальный идентификатор группы.
+ /// Получает или задает уникальный идентификатор группы.
///
///
/// Строковый идентификатор, уникальный в пределах дерева компоновки.
@@ -86,7 +75,19 @@ public class DockGroup : IDockElement, IDragSource, IDropTarget, INotifyProperty
/// При установке нового значения автоматически обновляется свойство
/// у дочернего элемента.
///
- public IDockElement First { get; set; }
+ public IDockElement First
+ {
+ get => _first;
+ set
+ {
+ if (_first != value)
+ {
+ _first = value ?? throw new ArgumentNullException(nameof(value));
+ _first.Parent = this;
+ OnPropertyChanged();
+ }
+ }
+ }
///
/// Получает или задает второй дочерний элемент (правую или нижнюю область).
@@ -101,7 +102,19 @@ public class DockGroup : IDockElement, IDragSource, IDropTarget, INotifyProperty
/// При установке нового значения автоматически обновляется свойство
/// у дочернего элемента.
///
- public IDockElement Second { get; set; }
+ public IDockElement Second
+ {
+ get => _second;
+ set
+ {
+ if (_second != value)
+ {
+ _second = value ?? throw new ArgumentNullException(nameof(value));
+ _second.Parent = this;
+ OnPropertyChanged();
+ }
+ }
+ }
///
/// Получает или задает направление разделения данной группы.
@@ -111,12 +124,10 @@ public class DockGroup : IDockElement, IDragSource, IDropTarget, INotifyProperty
/// как разделена область: горизонтально или вертикально.
///
///
- ///
- /// создает левую и правую области.
- ///
- ///
- /// создает верхнюю и нижнюю области.
- ///
+ ///
+ /// создает левую и правую области
+ /// создает верхнюю и нижнюю области
+ ///
///
public SplitDirection Orientation { get; set; }
@@ -218,7 +229,8 @@ public class DockGroup : IDockElement, IDragSource, IDropTarget, INotifyProperty
/// у дочерних элементов на текущую группу и генерирует уникальный идентификатор,
/// если он не был предоставлен.
///
- public DockGroup(IDockElement first, IDockElement second, SplitDirection orientation, string? id = null)
+ public DockGroup(IDockElement first, IDockElement second,
+ SplitDirection orientation, string? id = null)
{
First = first ?? throw new ArgumentNullException(nameof(first));
Second = second ?? throw new ArgumentNullException(nameof(second));
@@ -239,206 +251,4 @@ public class DockGroup : IDockElement, IDragSource, IDropTarget, INotifyProperty
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
-
- #region Реализация IDragSource
-
- ///
- /// Определяет, может ли группа начать операцию перетаскивания.
- ///
- ///
- /// При успешном возврате содержит информацию о перетаскивании;
- /// в противном случае — null.
- ///
- ///
- /// true, если группа может начать перетаскивание; в противном случае — false.
- ///
- ///
- ///
- /// Группа может быть перетащена только если она не является корневым
- /// элементом дерева (имеет родителя).
- ///
- ///
- /// При успешной проверке метод заполняет
- /// данными типа .
- ///
- ///
- public bool CanStartDrag(out DragInfo? dragInfo)
- {
- dragInfo = null;
-
- // DockGroup можно перетаскивать только если он не является корневым элементом
- if (Parent == null)
- return false;
-
- // Создаем данные для перетаскивания
- var data = new DockElementDragData
- {
- ElementId = Id,
- ElementType = GetType().Name,
- IsGroup = true
- };
-
- dragInfo = new DragInfo(data, DragDropEffects.Move, Point.Zero, this);
- return true;
- }
-
- ///
- /// Начинает операцию перетаскивания для группы.
- ///
- ///
- /// Информация о перетаскивании, полученная из .
- ///
- ///
- /// Всегда возвращает true, так как группа не требует специальной подготовки
- /// для начала перетаскивания.
- ///
- ///
- /// Для этот метод не выполняет дополнительных действий,
- /// так как все необходимые данные уже содержатся в .
- ///
- public bool StartDrag(DragInfo dragInfo)
- {
- // DockGroup не требует дополнительной подготовки для перетаскивания
- return true;
- }
-
- ///
- /// Вызывается при завершении операции перетаскивания.
- ///
- ///
- /// Исходная информация о перетаскивании.
- ///
- ///
- /// Эффекты, которые были применены при сбросе.
- ///
- ///
- ///
- /// Этот метод вызывается после того, как операция перетаскивания была
- /// завершена (успешно или неуспешно).
- ///
- ///
- /// Для этот метод не выполняет действий, так как
- /// все изменения в структуре дерева уже обработаны .
- ///
- ///
- public void DragCompleted(DragInfo dragInfo, DragDropEffects effects)
- {
- // Если группа была перемещена, ничего не делаем - LayoutManager уже обработал изменение
- }
-
- ///
- /// Вызывается при отмене операции перетаскивания.
- ///
- ///
- /// Исходная информация о перетаскивании.
- ///
- ///
- /// Для отмена перетаскивания не требует специальных
- /// действий, так как структура дерева не была изменена.
- ///
- public void DragCancelled(DragInfo dragInfo)
- {
- // Отмена перетаскивания не требует действий
- }
-
- #endregion
-
- #region Реализация IDropTarget
-
- ///
- /// Определяет, может ли группа принять сбрасываемые данные.
- ///
- ///
- /// Информация о потенциальном сбросе.
- ///
- ///
- /// true, если группа может принять данные; в противном случае — false.
- ///
- ///
- ///
- /// Группа может принимать только данные типа
- /// для элементов док-системы ( или ).
- ///
- ///
- /// Группа не может принять сброс самой себя (проверяется по идентификатору).
- ///
- ///
- public bool CanAcceptDrop(DropInfo dropInfo)
- {
- if (dropInfo.Data is not DockElementDragData dragData)
- return false;
-
- // Нельзя сбросить элемент на самого себя
- if (dragData.ElementId == Id)
- return false;
-
- // Можно принимать только элементы док-системы
- return dragData.ElementType == nameof(DockGroup) || dragData.ElementType == nameof(DockLeaf);
- }
-
- ///
- /// Вызывается, когда перетаскиваемый объект находится над группой.
- ///
- ///
- /// Информация о текущем положении перетаскивания.
- ///
- ///
- ///
- /// Этот метод вызывается постоянно, пока пользователь перемещает объект
- /// над целью. Для группы он устанавливает предлагаемые эффекты в
- /// .
- ///
- ///
- /// Если группа может принять сброс, предлагается эффект перемещения;
- /// в противном случае эффекты не предлагаются.
- ///
- ///
- public void DragOver(DropInfo dropInfo)
- {
- if (CanAcceptDrop(dropInfo))
- {
- dropInfo.SuggestedEffects = DragDropEffects.Move;
- }
- else
- {
- dropInfo.SuggestedEffects = DragDropEffects.None;
- }
- }
-
- ///
- /// Вызывается, когда пользователь сбрасывает данные на группу.
- ///
- ///
- /// Информация о сбросе.
- ///
- ///
- ///
- /// Для обработка сброса делегируется
- /// , поэтому метод просто помечает операцию
- /// как обработанную.
- ///
- ///
- /// Фактическое изменение структуры дерева выполняется менеджером макета
- /// на основе данных из .
- ///
- ///
- public void Drop(DropInfo dropInfo)
- {
- // Обработка сброса делегируется LayoutManager
- dropInfo.MarkAsHandled();
- }
-
- ///
- /// Вызывается, когда перетаскиваемый объект покидает область группы.
- ///
- ///
- /// Для группы этот метод не выполняет действий, так как очистка визуальной
- /// обратной связи выполняется в UI-слое.
- ///
- public void DragLeave()
- {
- // Очистка визуальной обратной связи (будет выполнена в UI слое)
- }
-
- #endregion
}
\ No newline at end of file
diff --git a/Lattice.Core.Docking/Models/DockLeaf.cs b/Lattice.Core.Docking/Models/DockLeaf.cs
index f8a7537..793c79e 100644
--- a/Lattice.Core.Docking/Models/DockLeaf.cs
+++ b/Lattice.Core.Docking/Models/DockLeaf.cs
@@ -1,8 +1,4 @@
using Lattice.Core.Docking.Abstractions;
-using Lattice.Core.DragDrop.Abstractions;
-using Lattice.Core.DragDrop.Enums;
-using Lattice.Core.DragDrop.Models;
-using Lattice.Core.Geometry;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Runtime.CompilerServices;
@@ -15,34 +11,24 @@ namespace Lattice.Core.Docking.Models;
/// отображаемого пользователю содержимого.
///
///
-///
-/// реализует интерфейсы ,
-/// и , что позволяет ему:
-///
-///
-/// - Управлять коллекцией вкладок
-/// - Быть источником перетаскивания (как всего листа, так и отдельных вкладок)
-/// - Принимать сброс других элементов или вкладок
-///
-///
/// Лист является основным элементом, с которым взаимодействует пользователь
/// при работе с документами или инструментальными панелями в IDE-подобных
/// приложениях.
-///
///
-public class DockLeaf : IDockContainer, INotifyPropertyChanged, IDragSource, IDropTarget
+public class DockLeaf : IDockContainer, INotifyPropertyChanged
{
///
- /// Событие, возникающее при изменении значения свойства.
+ /// Происходит при изменении значения свойства.
///
public event PropertyChangedEventHandler? PropertyChanged;
private readonly ObservableCollection _items = new();
private IDockContent? _activeContent;
private string _id;
+ private TabPlacement _tabPlacement = TabPlacement.Bottom;
///
- /// Получает уникальный идентификатор листа.
+ /// Получает или задает уникальный идентификатор листа.
///
///
/// Строковый идентификатор, уникальный в пределах дерева компоновки.
@@ -88,13 +74,9 @@ public class DockLeaf : IDockContainer, INotifyPropertyChanged, IDragSource, IDr
/// Активная вкладка или null, если в контейнере нет вкладок.
///
///
- ///
/// При установке нового значения проверяется, что вкладка действительно
/// содержится в коллекции .
- ///
- ///
/// Изменение этого свойства вызывает событие .
- ///
///
public IDockContent? ActiveContent
{
@@ -152,7 +134,18 @@ public class DockLeaf : IDockContainer, INotifyPropertyChanged, IDragSource, IDr
///
/// Поддерживаются все четыре стороны: верх, низ, лево, право.
///
- public TabPlacement TabPlacement { get; set; } = TabPlacement.Bottom;
+ public TabPlacement TabPlacement
+ {
+ get => _tabPlacement;
+ set
+ {
+ if (_tabPlacement != value)
+ {
+ _tabPlacement = value;
+ OnPropertyChanged();
+ }
+ }
+ }
///
/// Инициализирует новый экземпляр класса .
@@ -187,17 +180,15 @@ public class DockLeaf : IDockContainer, INotifyPropertyChanged, IDragSource, IDr
/// Контент для добавления.
///
///
- ///
/// Если контент уже содержится в коллекции, он не добавляется повторно,
/// но становится активным.
- ///
- ///
/// Этот метод обновляет свойство и вызывает
/// соответствующее событие изменения свойства.
- ///
///
public void AddContent(IDockContent content)
{
+ if (content == null) return;
+
if (!_items.Contains(content))
{
_items.Add(content);
@@ -212,18 +203,16 @@ public class DockLeaf : IDockContainer, INotifyPropertyChanged, IDragSource, IDr
/// Контент для удаления.
///
///
- ///
/// Если удаляемый контент является активным, автоматически выбирается
/// новая активная вкладка (следующая в списке или предыдущая, если удалена
/// последняя).
- ///
- ///
/// Если после удаления контейнер становится пустым, он может быть удален
/// из дерева макета системой компоновки.
- ///
///
public void RemoveContent(IDockContent content)
{
+ if (content == null) return;
+
int index = _items.IndexOf(content);
if (index == -1) return;
@@ -237,344 +226,4 @@ public class DockLeaf : IDockContainer, INotifyPropertyChanged, IDragSource, IDr
ActiveContent = null;
}
}
-
- #region Реализация IDragSource
-
- ///
- /// Определяет, может ли лист начать операцию перетаскивания.
- ///
- ///
- /// При успешном возврате содержит информацию о перетаскивании;
- /// в противном случае — null.
- ///
- ///
- /// true, если лист может начать перетаскивание; в противном случае — false.
- ///
- ///
- ///
- /// Лист может быть перетащен, если:
- ///
- ///
- /// - Он имеет родителя (не является корневым)
- /// - Или имеет хотя бы одну вкладку (не пустой)
- ///
- ///
- /// В зависимости от наличия активного контента создаются разные данные:
- ///
- ///
- /// -
- /// Если есть активный контент - создается
- /// для перетаскивания конкретной вкладки
- ///
- /// -
- /// Если нет активного контента - создается
- /// для перетаскивания всего листа
- ///
- ///
- ///
- public bool CanStartDrag(out DragInfo? dragInfo)
- {
- dragInfo = null;
-
- // DockLeaf можно перетаскивать
- if (Parent == null && Children.Count == 0)
- return false; // Не перетаскиваем пустые корневые листья
-
- object data;
-
- // Если есть активный контент, перетаскиваем контент, иначе перетаскиваем весь лист
- if (ActiveContent != null)
- {
- data = new ContentDragData
- {
- ElementId = Id,
- ContentId = ActiveContent.Id,
- ContentTitle = ActiveContent.Title,
- ContentType = ActiveContent.GetType().Name
- };
- }
- else
- {
- data = new DockElementDragData
- {
- ElementId = Id,
- ElementType = GetType().Name,
- IsGroup = false,
- Width = Width,
- Height = Height
- };
- }
-
- dragInfo = new DragInfo(data, DragDropEffects.Move | DragDropEffects.Copy, Point.Zero, this);
- return true;
- }
-
- ///
- /// Начинает операцию перетаскивания для листа.
- ///
- ///
- /// Информация о перетаскивании.
- ///
- ///
- /// Всегда возвращает true.
- ///
- ///
- /// Для этот метод не выполняет дополнительных действий.
- ///
- public bool StartDrag(DragInfo dragInfo)
- {
- // DockLeaf не требует дополнительной подготовки
- return true;
- }
-
- ///
- /// Вызывается при завершении операции перетаскивания.
- ///
- ///
- /// Исходная информация о перетаскивании.
- ///
- ///
- /// Эффекты, которые были применены при сбросе.
- ///
- ///
- /// Для этот метод не выполняет действий.
- ///
- public void DragCompleted(DragInfo dragInfo, DragDropEffects effects)
- {
- // Если лист был перемещен или скопирован, LayoutManager уже обработал это
- }
-
- ///
- /// Вызывается при отмене операции перетаскивания.
- ///
- ///
- /// Исходная информация о перетаскивании.
- ///
- ///
- /// Для отмена перетаскивания не требует действий.
- ///
- public void DragCancelled(DragInfo dragInfo)
- {
- // Отмена не требует действий
- }
-
- #endregion
-
- #region Реализация IDropTarget
-
- ///
- /// Определяет, может ли лист принять сбрасываемые данные.
- ///
- ///
- /// Информация о потенциальном сбросе.
- ///
- ///
- /// true, если лист может принять данные; в противном случае — false.
- ///
- ///
- /// Лист может принимать:
- ///
- /// -
- ///
для других листов и групп
- /// (для объединения или разделения)
- ///
- /// -
- ///
для вкладок (для объединения вкладок)
- ///
- ///
- ///
- public bool CanAcceptDrop(DropInfo dropInfo)
- {
- if (dropInfo.Data is DockElementDragData elementData)
- {
- // Можно принимать другие листы и группы
- return elementData.ElementType == nameof(DockLeaf) ||
- elementData.ElementType == nameof(DockGroup);
- }
- else if (dropInfo.Data is ContentDragData contentData)
- {
- // Можно принимать контент для объединения вкладок
- return true;
- }
-
- return false;
- }
-
- ///
- /// Вызывается, когда перетаскиваемый объект находится над листом.
- ///
- ///
- /// Информация о текущем положении перетаскивания.
- ///
- ///
- ///
- /// В зависимости от типа данных устанавливаются разные предлагаемые эффекты:
- ///
- ///
- /// -
- /// Для
- эффект копирования (объединение вкладок)
- ///
- /// -
- /// Для
- эффект перемещения
- ///
- ///
- ///
- public void DragOver(DropInfo dropInfo)
- {
- if (CanAcceptDrop(dropInfo))
- {
- if (dropInfo.Data is ContentDragData)
- {
- // Для контента предлагаем копирование (объединение вкладок)
- dropInfo.SuggestedEffects = DragDropEffects.Copy;
- }
- else
- {
- // Для элементов предлагаем перемещение
- dropInfo.SuggestedEffects = DragDropEffects.Move;
- }
- }
- else
- {
- dropInfo.SuggestedEffects = DragDropEffects.None;
- }
- }
-
- ///
- /// Вызывается, когда пользователь сбрасывает данные на лист.
- ///
- ///
- /// Информация о сбросе.
- ///
- ///
- /// Обработка сброса делегируется .
- ///
- public void Drop(DropInfo dropInfo)
- {
- // Обработка делегируется LayoutManager
- dropInfo.MarkAsHandled();
- }
-
- ///
- /// Вызывается, когда перетаскиваемый объект покидает область листа.
- ///
- ///
- /// Очистка визуальной обратной связи выполняется в UI-слое.
- ///
- public void DragLeave()
- {
- // Очистка визуальной обратной связи
- }
-
- #endregion
-}
-
-///
-/// Представляет данные для перетаскивания элементов док-системы (групп или листов).
-/// Используется при перетаскивании целых структурных элементов дерева компоновки.
-///
-///
-/// Этот класс сериализуется и передается между компонентами системы перетаскивания
-/// для идентификации перетаскиваемого элемента и его свойств.
-///
-public class DockElementDragData
-{
- ///
- /// Получает или задает уникальный идентификатор элемента.
- ///
- ///
- /// Идентификатор элемента, соответствующий свойству .
- ///
- public string ElementId { get; set; } = string.Empty;
-
- ///
- /// Получает или задает тип элемента.
- ///
- ///
- /// Имя типа элемента (обычно "DockGroup" или "DockLeaf").
- ///
- public string ElementType { get; set; } = string.Empty;
-
- ///
- /// Получает или задает значение, указывающее, является ли элемент группой.
- ///
- ///
- /// true, если элемент является ; false, если .
- ///
- public bool IsGroup { get; set; }
-
- ///
- /// Получает или задает идентификатор родительского элемента.
- ///
- ///
- /// Идентификатор родительского элемента или null, если элемент корневой.
- ///
- public string? ParentId { get; set; }
-
- ///
- /// Получает или задает ширину элемента.
- ///
- ///
- /// Текущая ширина элемента в пикселях.
- ///
- public double Width { get; set; }
-
- ///
- /// Получает или задает высоту элемента.
- ///
- ///
- /// Текущая высота элемента в пикселях.
- ///
- public double Height { get; set; }
-}
-
-///
-/// Представляет данные для перетаскивания контента (вкладок).
-/// Используется при перетаскивании отдельных вкладок между контейнерами.
-///
-///
-/// Этот класс позволяет идентифицировать конкретную вкладку для операций
-/// объединения или перемещения между контейнерами.
-///
-public class ContentDragData
-{
- ///
- /// Получает или задает идентификатор контейнера (листа), содержащего контент.
- ///
- ///
- /// Идентификатор , в котором находится перетаскиваемая вкладка.
- ///
- public string ElementId { get; set; } = string.Empty;
-
- ///
- /// Получает или задает уникальный идентификатор контента.
- ///
- ///
- /// Идентификатор контента, соответствующий свойству .
- ///
- public string ContentId { get; set; } = string.Empty;
-
- ///
- /// Получает или задает заголовок контента.
- ///
- ///
- /// Текст, отображаемый на вкладке.
- ///
- public string ContentTitle { get; set; } = string.Empty;
-
- ///
- /// Получает или задает тип контента.
- ///
- ///
- /// Имя типа контента (например, "TextEditor", "Toolbox", и т.д.).
- ///
- public string ContentType { get; set; } = string.Empty;
-
- ///
- /// Получает или задает значение, указывающее, можно ли закрыть контент.
- ///
- ///
- /// true, если контент можно закрыть; в противном случае — false.
- ///
- public bool CanClose { get; set; } = true;
}
\ No newline at end of file
diff --git a/Lattice.Core.Docking/Models/DockPosition.cs b/Lattice.Core.Docking/Models/DockPosition.cs
index f34ea72..1c705aa 100644
--- a/Lattice.Core.Docking/Models/DockPosition.cs
+++ b/Lattice.Core.Docking/Models/DockPosition.cs
@@ -1,13 +1,33 @@
namespace Lattice.Core.Docking.Models;
///
-/// Определяет позицию вставки при операции Drag-and-Drop.
+/// Определяет позицию вставки элемента относительно целевого элемента.
+/// Используется при операциях перемещения и вставки элементов в дерево компоновки.
///
public enum DockPosition
{
+ ///
+ /// Слева от целевого элемента.
+ ///
Left,
+
+ ///
+ /// Справа от целевого элемента.
+ ///
Right,
+
+ ///
+ /// Сверху от целевого элемента.
+ ///
Top,
+
+ ///
+ /// Снизу от целевого элемента.
+ ///
Bottom,
+
+ ///
+ /// В центре целевого элемента (для объединения вкладок).
+ ///
Center,
-}
+}
\ No newline at end of file
diff --git a/Lattice.Core.Docking/Models/DockSide.cs b/Lattice.Core.Docking/Models/DockSide.cs
index 6975ee6..4fb714c 100644
--- a/Lattice.Core.Docking/Models/DockSide.cs
+++ b/Lattice.Core.Docking/Models/DockSide.cs
@@ -5,15 +5,23 @@
///
public enum DockSide
{
- /// Левая сторона окна.
+ ///
+ /// Левая сторона окна.
+ ///
Left,
- /// Правая сторона окна.
+ ///
+ /// Правая сторона окна.
+ ///
Right,
- /// Верхняя сторона окна.
+ ///
+ /// Верхняя сторона окна.
+ ///
Top,
- /// Нижняя сторона окна.
+ ///
+ /// Нижняя сторона окна.
+ ///
Bottom
}
\ No newline at end of file
diff --git a/Lattice.Core.Docking/Models/DockWindow.cs b/Lattice.Core.Docking/Models/DockWindow.cs
index cb73925..408f3f9 100644
--- a/Lattice.Core.Docking/Models/DockWindow.cs
+++ b/Lattice.Core.Docking/Models/DockWindow.cs
@@ -3,21 +3,66 @@
namespace Lattice.Core.Docking.Models;
///
-/// Описывает состояние плавающего окна в системе Lattice.
+/// Представляет плавающее окно в системе докинга.
+/// Плавающие окна могут перемещаться по экрану независимо от главного окна.
///
public class DockWindow
{
- /// Уникальный ID окна для сохранения его позиции в конфиге.
+ ///
+ /// Получает уникальный идентификатор окна.
+ ///
+ ///
+ /// Строковый идентификатор, сгенерированный с помощью GUID.
+ /// Используется для сохранения позиции и размера окна в конфигурации.
+ ///
public string Id { get; } = Guid.NewGuid().ToString();
- /// Корневой элемент макета внутри данного окна.
+ ///
+ /// Получает или задает корневой элемент макета внутри данного окна.
+ ///
+ ///
+ /// Корневой элемент дерева компоновки плавающего окна.
+ ///
public IDockElement? Root { get; set; }
+ ///
+ /// Получает или задает позицию X окна на экране.
+ ///
+ ///
+ /// Координата X левого верхнего угла окна в пикселях.
+ ///
public double X { get; set; }
+
+ ///
+ /// Получает или задает позицию Y окна на экране.
+ ///
+ ///
+ /// Координата Y левого верхнего угла окна в пикселях.
+ ///
public double Y { get; set; }
+
+ ///
+ /// Получает или задает ширину окна.
+ ///
+ ///
+ /// Ширина окна в пикселях. Значение по умолчанию: 800.
+ ///
public double Width { get; set; } = 800;
+
+ ///
+ /// Получает или задает высоту окна.
+ ///
+ ///
+ /// Высота окна в пикселях. Значение по умолчанию: 600.
+ ///
public double Height { get; set; } = 600;
- /// Заголовок окна (обычно берется из активного контента).
+ ///
+ /// Получает или задает заголовок окна.
+ ///
+ ///
+ /// Текст заголовка окна. Обычно берется из активного контента.
+ /// Значение по умолчанию: "Lattice Tool Window".
+ ///
public string Title { get; set; } = "Lattice Tool Window";
-}
+}
\ No newline at end of file
diff --git a/Lattice.Core.Docking/Models/SplitDirection.cs b/Lattice.Core.Docking/Models/SplitDirection.cs
index faee1bf..c80f524 100644
--- a/Lattice.Core.Docking/Models/SplitDirection.cs
+++ b/Lattice.Core.Docking/Models/SplitDirection.cs
@@ -1,12 +1,17 @@
namespace Lattice.Core.Docking.Models;
///
-/// Перечисление направлений разделения пространства внутри группы.
+/// Определяет направление разделения пространства внутри группы.
///
public enum SplitDirection
{
- /// Разделение по горизонтали (создает левую и правую области).
+ ///
+ /// Разделение по горизонтали (создает левую и правую области).
+ ///
Horizontal,
- /// Разделение по вертикали (создает верхнюю и нижнюю области).
+
+ ///
+ /// Разделение по вертикали (создает верхнюю и нижнюю области).
+ ///
Vertical
-}
+}
\ No newline at end of file
diff --git a/Lattice.Core.Docking/Models/TabPlacement.cs b/Lattice.Core.Docking/Models/TabPlacement.cs
index 5dbd7d4..fe962d5 100644
--- a/Lattice.Core.Docking/Models/TabPlacement.cs
+++ b/Lattice.Core.Docking/Models/TabPlacement.cs
@@ -5,8 +5,23 @@
///
public enum TabPlacement
{
+ ///
+ /// Вкладки располагаются сверху.
+ ///
Top,
+
+ ///
+ /// Вкладки располагаются снизу.
+ ///
Bottom,
+
+ ///
+ /// Вкладки располагаются слева.
+ ///
Left,
+
+ ///
+ /// Вкладки располагаются справа.
+ ///
Right,
}
\ No newline at end of file
diff --git a/Lattice.Core.Docking/Serialization/ILayoutSerializer.cs b/Lattice.Core.Docking/Serialization/ILayoutSerializer.cs
index a4b6246..496602e 100644
--- a/Lattice.Core.Docking/Serialization/ILayoutSerializer.cs
+++ b/Lattice.Core.Docking/Serialization/ILayoutSerializer.cs
@@ -1,7 +1,7 @@
namespace Lattice.Core.Docking.Serialization;
///
-/// Абстракция для сериализации и десериализации состояния макета док-системы.
+/// Определяет контракт для сериализации и десериализации состояния макета док-системы.
/// Позволяет сохранять и восстанавливать расположение панелей, окон и их состояние.
///
///
@@ -14,7 +14,12 @@ public interface ILayoutSerializer
/// Сериализует состояние менеджера макета в строку.
///
/// Менеджер макета для сериализации.
- /// Строковое представление состояния макета.
+ ///
+ /// Строковое представление состояния макета.
+ ///
+ ///
+ /// Выбрасывается, когда равен null.
+ ///
string Serialize(Engine.LayoutManager manager);
///
@@ -26,6 +31,10 @@ public interface ILayoutSerializer
/// Функция разрешения контента по идентификатору, используемая для восстановления
/// ссылок на контент в десериализованном состоянии.
///
+ ///
+ /// Выбрасывается, когда или
+ /// равны null.
+ ///
void Deserialize(Engine.LayoutManager manager, string serializedLayout,
Func contentResolver);
-}
+}
\ No newline at end of file
diff --git a/Lattice.Core.Docking/Serialization/ISerializableLayout.cs b/Lattice.Core.Docking/Serialization/ISerializableLayout.cs
index 92167ed..3ca8be3 100644
--- a/Lattice.Core.Docking/Serialization/ISerializableLayout.cs
+++ b/Lattice.Core.Docking/Serialization/ISerializableLayout.cs
@@ -1,19 +1,24 @@
namespace Lattice.Core.Docking.Serialization;
///
-/// Контракт для объектов, которые могут предоставлять состояние для сериализации.
+/// Определяет контракт для объектов, которые могут предоставлять состояние для сериализации.
///
public interface ISerializableLayout
{
///
- /// Получает состояние для сериализации.
+ /// Получает состояние объекта для сериализации.
///
- /// Объект состояния, готовый к сериализации.
+ ///
+ /// Объект состояния, готовый к сериализации.
+ ///
object GetSerializableState();
///
- /// Восстанавливает состояние из десериализованного объекта.
+ /// Восстанавливает состояние объекта из десериализованного объекта.
///
/// Десериализованное состояние.
+ ///
+ /// Выбрасывается, когда равен null.
+ ///
void RestoreFromState(object state);
}
\ No newline at end of file
diff --git a/Lattice.Core.Docking/Services/ContentRegistry.cs b/Lattice.Core.Docking/Services/ContentRegistry.cs
index 0daee2c..0b49376 100644
--- a/Lattice.Core.Docking/Services/ContentRegistry.cs
+++ b/Lattice.Core.Docking/Services/ContentRegistry.cs
@@ -2,7 +2,7 @@
///
/// Реестр типов содержимого, который позволяет создавать экземпляры контента по типу.
-/// Этот сервис является центральным для динамического создания панелей инструментов и документов в IDE.
+/// Этот сервис является центральным для динамического создания панелей инструментов и документов.
///
///
/// Реализует шаблон "Фабрика" для создания экземпляров .
@@ -16,12 +16,19 @@ public class ContentRegistry
///
/// Регистрирует фабричный метод для создания контента указанного типа.
///
- /// Тип контента, реализующий .
+ ///
+ /// Тип контента, реализующий .
+ ///
/// Уникальный идентификатор типа контента.
/// Фабричный метод для создания экземпляров контента.
/// Метаданные типа контента (опционально).
- /// Выбрасывается, если contentTypeId или factory равны null.
- /// Выбрасывается, если contentTypeId уже зарегистрирован.
+ ///
+ /// Выбрасывается, если или
+ /// равны null.
+ ///
+ ///
+ /// Выбрасывается, если уже зарегистрирован.
+ ///
public void Register(string contentTypeId, Func 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
///
/// Идентификатор типа контента.
/// Уникальный идентификатор для создаваемого экземпляра контента.
- /// Новый экземпляр контента.
- /// Выбрасывается, если тип контента не зарегистрирован.
+ ///
+ /// Новый экземпляр контента.
+ ///
+ ///
+ /// Выбрасывается, если равен null или пустой строке.
+ ///
+ ///
+ /// Выбрасывается, если тип контента не зарегистрирован.
+ ///
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
/// Получает метаданные для указанного типа контента.
///
/// Идентификатор типа контента.
- /// Метаданные типа контента или null, если тип не найден.
+ ///
+ /// Метаданные типа контента или null, если тип не найден.
+ ///
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
///
/// Получает все зарегистрированные типы контента.
///
- /// Коллекция идентификаторов зарегистрированных типов контента.
+ ///
+ /// Коллекция идентификаторов зарегистрированных типов контента.
+ ///
public IEnumerable GetRegisteredTypes() => _contentTypes.Keys;
///
/// Проверяет, зарегистрирован ли указанный тип контента.
///
- public bool IsRegistered(string contentTypeId) => _contentTypes.ContainsKey(contentTypeId);
+ /// Идентификатор типа контента.
+ ///
+ /// true, если тип контента зарегистрирован; в противном случае false.
+ ///
+ public bool IsRegistered(string contentTypeId)
+ {
+ if (string.IsNullOrWhiteSpace(contentTypeId))
+ return false;
+
+ return _contentTypes.ContainsKey(contentTypeId);
+ }
///
- /// Дескриптор типа контента, содержащий информацию о фабричном методе и метаданных.
+ /// Представляет дескриптор типа контента, содержащий информацию о фабричном методе и метаданных.
///
private class ContentDescriptor
{
+ ///
+ /// Получает тип контента.
+ ///
public Type ContentType { get; }
+
+ ///
+ /// Получает фабричный метод для создания экземпляров контента.
+ ///
public Func Factory { get; }
+
+ ///
+ /// Получает метаданные типа контента.
+ ///
public ContentMetadata Metadata { get; }
- public ContentDescriptor(Type contentType, Func factory, ContentMetadata metadata)
+ ///
+ /// Инициализирует новый экземпляр класса .
+ ///
+ /// Тип контента.
+ /// Фабричный метод.
+ /// Метаданные.
+ public ContentDescriptor(Type contentType, Func factory,
+ ContentMetadata metadata)
{
ContentType = contentType;
Factory = factory;
@@ -105,54 +158,80 @@ public class ContentRegistry
}
///
-/// Метаданные типа контента, предоставляющие дополнительную информацию для отображения в UI.
+/// Представляет метаданные типа контента, предоставляющие дополнительную информацию для отображения в UI.
///
public class ContentMetadata
{
///
- /// Идентификатор типа контента.
+ /// Получает идентификатор типа контента.
///
+ ///
+ /// Уникальный строковый идентификатор типа контента.
+ ///
public string ContentTypeId { get; }
///
- /// Отображаемое имя типа контента.
+ /// Получает или задает отображаемое имя типа контента.
///
+ ///
+ /// Имя типа контента, отображаемое пользователю.
+ ///
public string DisplayName { get; set; }
///
- /// Описание типа контента.
+ /// Получает или задает описание типа контента.
///
+ ///
+ /// Текстовое описание функциональности контента.
+ ///
public string Description { get; set; }
///
- /// Имя ресурса для иконки (опционально).
+ /// Получает или задает имя ресурса для иконки типа контента.
///
+ ///
+ /// Имя ресурса иконки или null, если иконка не определена.
+ ///
public string? IconResource { get; set; }
///
- /// Признак того, что контент является документом (а не инструментальной панелью).
+ /// Получает или задает значение, указывающее, является ли контент документом
+ /// (а не инструментальной панелью).
///
+ ///
+ /// true, если контент является документом; в противном случае false.
+ ///
public bool IsDocument { get; set; }
///
- /// Минимальная ширина контента в пикселях.
+ /// Получает или задает ширину контента по умолчанию.
///
+ ///
+ /// Ширина контента в пикселях. Значение по умолчанию: 300.
+ ///
public double DefaultWidth { get; set; } = 300;
///
- /// Минимальная высота контента в пикселях.
+ /// Получает или задает высоту контента по умолчанию.
///
+ ///
+ /// Высота контента в пикселях. Значение по умолчанию: 200.
+ ///
public double DefaultHeight { get; set; } = 200;
///
- /// Инициализирует новый экземпляр метаданных контента.
+ /// Инициализирует новый экземпляр класса .
///
/// Идентификатор типа контента.
- /// Отображаемое имя.
+ /// Отображаемое имя типа контента.
+ ///
+ /// Выбрасывается, если или
+ /// равны null.
+ ///
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;
}
}
\ No newline at end of file
diff --git a/Lattice.Core.DragDrop.Tests/DragDropServiceTests.cs b/Lattice.Core.DragDrop.Tests/DragDropServiceTests.cs
deleted file mode 100644
index 4b02109..0000000
--- a/Lattice.Core.DragDrop.Tests/DragDropServiceTests.cs
+++ /dev/null
@@ -1,115 +0,0 @@
-using Lattice.Core.DragDrop.Abstractions;
-using Lattice.Core.DragDrop.Enums;
-using Lattice.Core.DragDrop.Models;
-using Lattice.Core.DragDrop.Services;
-using Lattice.Core.Geometry;
-using Moq;
-using Xunit;
-
-namespace Lattice.Core.DragDrop.Tests;
-
-public class DragDropServiceTests
-{
- [Fact]
- public void StartDrag_WithValidSource_StartsDragOperation()
- {
- // Arrange
- var service = new DragDropService();
- var mockSource = new Mock();
- var dragInfo = new DragInfo("test", DragDropEffects.Copy, new Point(0, 0));
-
- mockSource.Setup(s => s.CanStartDrag(out dragInfo)).Returns(true);
- mockSource.Setup(s => s.StartDrag(It.IsAny())).Returns(true);
-
- // Act
- var result = service.StartDrag(mockSource.Object, new Point(0, 0));
-
- // Assert
- Assert.True(result);
- Assert.True(service.IsDragActive);
- Assert.NotNull(service.CurrentDragInfo);
- }
-
- [Fact]
- public void RegisterDropTarget_ReturnsValidId()
- {
- // Arrange
- var service = new DragDropService();
- var mockTarget = new Mock();
- var bounds = new Rect(0, 0, 100, 100);
-
- // Act
- var id = service.RegisterDropTarget(mockTarget.Object, bounds);
-
- // Assert
- Assert.NotNull(id);
- Assert.NotEmpty(id);
- }
-
- [Fact]
- public void UpdateDrag_WithValidDropTarget_CallsDragOver()
- {
- // Arrange
- var service = new DragDropService();
- var mockSource = new Mock();
- var mockTarget = new Mock();
-
- var dragInfo = new DragInfo("test", DragDropEffects.Copy, new Point(0, 0));
- mockSource.Setup(s => s.CanStartDrag(out dragInfo)).Returns(true);
- mockSource.Setup(s => s.StartDrag(It.IsAny())).Returns(true);
-
- var targetId = service.RegisterDropTarget(mockTarget.Object, new Rect(0, 0, 100, 100));
- service.StartDrag(mockSource.Object, new Point(0, 0));
-
- // Act
- service.UpdateDrag(new Point(50, 50));
-
- // Assert
- mockTarget.Verify(t => t.DragOver(It.IsAny()), Times.AtLeastOnce());
- }
-
- [Fact]
- public void EndDrag_WithValidDrop_CallsDrop()
- {
- // Arrange
- var service = new DragDropService();
- var mockSource = new Mock();
- var mockTarget = new Mock();
-
- var dragInfo = new DragInfo("test", DragDropEffects.Copy, new Point(0, 0));
- mockSource.Setup(s => s.CanStartDrag(out dragInfo)).Returns(true);
- mockSource.Setup(s => s.StartDrag(It.IsAny())).Returns(true);
-
- service.RegisterDropTarget(mockTarget.Object, new Rect(0, 0, 100, 100));
- service.StartDrag(mockSource.Object, new Point(0, 0));
- service.UpdateDrag(new Point(50, 50));
-
- // Act
- var effects = service.EndDrag(new Point(50, 50));
-
- // Assert
- mockTarget.Verify(t => t.Drop(It.IsAny()), Times.Once());
- Assert.False(service.IsDragActive);
- }
-
- [Fact]
- public void CancelDrag_WithActiveDrag_CallsDragCancelled()
- {
- // Arrange
- var service = new DragDropService();
- var mockSource = new Mock();
- var dragInfo = new DragInfo("test", DragDropEffects.Copy, new Point(0, 0));
-
- mockSource.Setup(s => s.CanStartDrag(out dragInfo)).Returns(true);
- mockSource.Setup(s => s.StartDrag(It.IsAny())).Returns(true);
-
- service.StartDrag(mockSource.Object, new Point(0, 0));
-
- // Act
- service.CancelDrag();
-
- // Assert
- mockSource.Verify(s => s.DragCancelled(It.IsAny()), Times.Once());
- Assert.False(service.IsDragActive);
- }
-}
\ No newline at end of file
diff --git a/Lattice.Core.DragDrop.Tests/Lattice.Core.DragDrop.Tests.csproj b/Lattice.Core.DragDrop.Tests/Lattice.Core.DragDrop.Tests.csproj
deleted file mode 100644
index 32a369c..0000000
--- a/Lattice.Core.DragDrop.Tests/Lattice.Core.DragDrop.Tests.csproj
+++ /dev/null
@@ -1,27 +0,0 @@
-
-
-
- net8.0
- enable
- false
-
-
-
-
-
-
-
- runtime; build; native; contentfiles; analyzers; buildtransitive
- all
-
-
- runtime; build; native; contentfiles; analyzers; buildtransitive
- all
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/Lattice.Core.DragDrop/Abstractions/IDragSource.cs b/Lattice.Core.DragDrop/Abstractions/IDragSource.cs
deleted file mode 100644
index 989b274..0000000
--- a/Lattice.Core.DragDrop/Abstractions/IDragSource.cs
+++ /dev/null
@@ -1,84 +0,0 @@
-namespace Lattice.Core.DragDrop.Abstractions;
-
-///
-/// Определяет контракт для объектов, которые могут быть источником данных
-/// в операции перетаскивания.
-///
-///
-///
-/// Объекты, реализующие этот интерфейс, могут инициировать операции перетаскивания
-/// и предоставлять данные для передачи другим элементам через механизм drag-and-drop.
-///
-///
-/// Интерфейс полностью асинхронный и поддерживает отмену операций через CancellationToken.
-/// Все методы должны быть потокобезопасными и поддерживать вызов из любого потока.
-///
-///
-public interface IDragSource
-{
- ///
- /// Пытается начать операцию перетаскивания из указанной позиции.
- ///
- /// Начальная позиция операции в координатах экрана.
- /// Токен отмены операции.
- ///
- /// Информация о перетаскивании, если операция может быть начата; в противном случае — null.
- /// Возвращаемый объект должен быть полностью инициализирован,
- /// включая данные, разрешенные эффекты и ссылку на источник.
- ///
- ///
- ///
- /// Этот метод вызывается сервисом перетаскивания при попытке начать операцию
- /// (обычно при нажатии и перемещении мыши). Метод должен проверить, может ли
- /// источник начать перетаскивание в текущем контексте.
- ///
- ///
- /// Реализация должна быть быстрой и не выполнять длительных операций.
- /// Если подготовка данных требует времени, ее следует выполнить асинхронно
- /// после подтверждения возможности начала.
- ///
- ///
- Task TryStartDragAsync(Geometry.Point startPosition, CancellationToken cancellationToken = default);
-
- ///
- /// Уведомляет источник о завершении операции перетаскивания.
- ///
- /// Информация о перетаскивании, полученная при начале операции.
- /// Эффекты, которые были применены при сбросе.
- /// Токен отмены операции.
- /// Задача, представляющая асинхронную операцию.
- ///
- ///
- /// Этот метод вызывается после завершения операции перетаскивания
- /// (успешного или неуспешного). Реализация может:
- ///
- ///
- /// - Выполнить очистку ресурсов, связанных с операцией
- /// - Обновить состояние на основе результата (например, удалить данные при перемещении)
- /// - Отобразить визуальную обратную связь о результате
- ///
- ///
- /// Если операция завершилась с эффектом ,
- /// источник обычно должен удалить или обновить исходные данные.
- ///
- ///
- Task OnDragCompletedAsync(Models.DragInfo dragInfo, Enums.DragDropEffects effects, CancellationToken cancellationToken = default);
-
- ///
- /// Уведомляет источник об отмене операции перетаскивания.
- ///
- /// Информация о перетаскивании, полученная при начале операции.
- /// Токен отмены операции.
- /// Задача, представляющая асинхронную операцию.
- ///
- ///
- /// Этот метод вызывается, когда операция перетаскивания была отменена
- /// пользователем (например, нажатием клавиши Escape) или системой.
- ///
- ///
- /// Реализация должна выполнить очистку и восстановить исходное состояние.
- /// Обычно это включает освобождение ресурсов и сброс визуальных индикаторов.
- ///
- ///
- Task OnDragCancelledAsync(Models.DragInfo dragInfo, CancellationToken cancellationToken = default);
-}
\ No newline at end of file
diff --git a/Lattice.Core.DragDrop/Abstractions/IDropTarget.cs b/Lattice.Core.DragDrop/Abstractions/IDropTarget.cs
deleted file mode 100644
index 5a15f2d..0000000
--- a/Lattice.Core.DragDrop/Abstractions/IDropTarget.cs
+++ /dev/null
@@ -1,118 +0,0 @@
-namespace Lattice.Core.DragDrop.Abstractions;
-
-///
-/// Определяет контракт для объектов, которые могут принимать сбрасываемые данные
-/// в операции перетаскивания.
-///
-///
-///
-/// Объекты, реализующие этот интерфейс, могут обрабатывать данные, сброшенные
-/// пользователем, и предоставлять визуальную обратную связь во время перетаскивания.
-///
-///
-/// Интерфейс поддерживает асинхронные операции и отмену через CancellationToken.
-/// Все методы должны быть потокобезопасными и идемпотентными (многократный вызов
-/// с одинаковыми параметрами должен давать одинаковый результат).
-///
-///
-public interface IDropTarget
-{
- ///
- /// Определяет, может ли объект принять сбрасываемые данные.
- ///
- /// Информация о потенциальном сбросе.
- /// Токен отмены операции.
- ///
- /// true, если объект может принять данные; в противном случае — false.
- ///
- ///
- ///
- /// Этот метод вызывается, когда перетаскиваемый объект находится над целью.
- /// Реализация должна проверить, совместимы ли данные с целью, и установить
- /// предлагаемые эффекты в свойстве .
- ///
- ///
- /// Метод может вызываться многократно при перемещении курсора над целью.
- /// Реализация должна быть эффективной и избегать длительных операций.
- ///
- ///
- /// Если метод возвращает false, система не будет вызывать другие методы
- /// для этой цели до тех пор, пока курсор не покинет ее область.
- ///
- ///
- Task CanAcceptDropAsync(Models.DropInfo dropInfo, CancellationToken cancellationToken = default);
-
- ///
- /// Вызывается, когда перетаскиваемый объект перемещается над целью.
- ///
- /// Информация о текущем положении перетаскивания.
- /// Токен отмены операции.
- /// Задача, представляющая асинхронную операцию.
- ///
- ///
- /// Этот метод вызывается постоянно, пока пользователь перемещает объект над целью.
- /// Реализация может:
- ///
- ///
- /// - Обновить визуальную обратную связь (подсветка, изменение курсора)
- /// - Вычислить точную позицию сброса (например, между элементами списка)
- /// - Уточнить предлагаемые эффекты на основе текущей позиции
- /// - Прокрутить содержимое, если цель поддерживает прокрутку
- ///
- ///
- /// Метод должен быть оптимизирован для частого вызова. Длительные операции
- /// должны выполняться асинхронно без блокировки потока.
- ///
- ///
- Task OnDragOverAsync(Models.DropInfo dropInfo, CancellationToken cancellationToken = default);
-
- ///
- /// Вызывается, когда пользователь сбрасывает данные на цель.
- ///
- /// Информация о сбросе, включая данные и позицию.
- /// Токен отмены операции.
- /// Задача, представляющая асинхронную операцию.
- ///
- ///
- /// Этот метод вызывается, когда пользователь отпускает кнопку мыши над целью.
- /// Реализация должна обработать принятие данных и выполнить соответствующее действие:
- ///
- ///
- /// - Добавить данные в коллекцию (для копирования)
- /// - Переместить данные (при поддержке перемещения)
- /// - Создать ссылку на данные
- /// - Выполнить пользовательскую логику обработки
- ///
- ///
- /// После успешной обработки данных следует вызвать ,
- /// чтобы указать системе, что операция обработана и дополнительная обработка не требуется.
- ///
- ///
- /// Если операция завершилась успешно, система уведомит источник через
- /// с соответствующими эффектами.
- ///
- ///
- Task OnDropAsync(Models.DropInfo dropInfo, CancellationToken cancellationToken = default);
-
- ///
- /// Вызывается, когда перетаскиваемый объект покидает область цели.
- ///
- /// Токен отмены операции.
- /// Задача, представляющая асинхронную операцию.
- ///
- ///
- /// Этот метод вызывается, когда пользователь перемещает объект за пределы цели.
- /// Реализация должна:
- ///
- ///
- /// - Очистить любую визуальную обратную связь, установленную ранее
- /// - Сбросить временное состояние, связанное с операцией
- /// - Освободить ресурсы, выделенные для предварительного просмотра
- ///
- ///
- /// Метод гарантированно вызывается после любого успешного или неуспешного
- /// вызова , если курсор покидает область цели.
- ///
- ///
- Task OnDragLeaveAsync(CancellationToken cancellationToken = default);
-}
\ No newline at end of file
diff --git a/Lattice.Core.DragDrop/Constants/DragDropConstants.cs b/Lattice.Core.DragDrop/Constants/DragDropConstants.cs
deleted file mode 100644
index 6bb931c..0000000
--- a/Lattice.Core.DragDrop/Constants/DragDropConstants.cs
+++ /dev/null
@@ -1,27 +0,0 @@
-namespace Lattice.Core.DragDrop.Constants;
-
-///
-/// Константы для системы перетаскивания.
-///
-public static class DragDropConstants
-{
- ///
- /// Порог начала перетаскивания по умолчанию (пикселей).
- ///
- public const double DefaultDragThreshold = 3.0;
-
- ///
- /// Интервал очистки по умолчанию (миллисекунды).
- ///
- public const int DefaultCleanupInterval = 60000;
-
- ///
- /// Таймаут асинхронных операций по умолчанию (миллисекунды).
- ///
- public const int DefaultAsyncTimeout = 5000;
-
- ///
- /// Время жизни неиспользуемых целей (минуты).
- ///
- public const int TargetLifetimeMinutes = 10;
-}
\ No newline at end of file
diff --git a/Lattice.Core.DragDrop/Enums/DragDropEffects.cs b/Lattice.Core.DragDrop/Enums/DragDropEffects.cs
deleted file mode 100644
index 705232a..0000000
--- a/Lattice.Core.DragDrop/Enums/DragDropEffects.cs
+++ /dev/null
@@ -1,115 +0,0 @@
-namespace Lattice.Core.DragDrop.Enums;
-
-///
-/// Определяет эффекты, которые могут быть применены при операции перетаскивания.
-///
-///
-/// Этот перечисление используется для указания допустимых операций перетаскивания
-/// и передачи информации о результате операции между источником и целью.
-///
-[Flags]
-public enum DragDropEffects
-{
- ///
- /// Операция перетаскивания не разрешена.
- ///
- None = 0,
-
- ///
- /// Данные копируются из источника в цель.
- ///
- Copy = 1 << 0,
-
- ///
- /// Данные перемещаются из источника в цель.
- ///
- Move = 1 << 1,
-
- ///
- /// Создается ссылка на исходные данные.
- ///
- Link = 1 << 2,
-
- ///
- /// Целевой элемент может прокручиваться во время перетаскивания.
- ///
- Scroll = 1 << 3,
-
- ///
- /// Комбинированный эффект копирования и перемещения.
- ///
- CopyOrMove = Copy | Move,
-
- ///
- /// Все эффекты разрешены.
- ///
- All = Copy | Move | Link | Scroll
-}
-
-///
-/// Расширения для работы с DragDropEffects.
-///
-public static class DragDropEffectsExtensions
-{
- ///
- /// Проверяет, содержит ли эффекты указанный эффект.
- ///
- /// Эффекты для проверки.
- /// Эффект для поиска.
- /// true, если эффект присутствует; в противном случае — false.
- public static bool HasEffect(this DragDropEffects effects, DragDropEffects effect)
- {
- return (effects & effect) == effect;
- }
-
- ///
- /// Проверяет, содержат ли эффекты копирование.
- ///
- /// Эффекты для проверки.
- /// true, если разрешено копирование; в противном случае — false.
- public static bool CanCopy(this DragDropEffects effects)
- {
- return effects.HasEffect(DragDropEffects.Copy);
- }
-
- ///
- /// Проверяет, содержат ли эффекты перемещение.
- ///
- /// Эффекты для проверки.
- /// true, если разрешено перемещение; в противном случае — false.
- public static bool CanMove(this DragDropEffects effects)
- {
- return effects.HasEffect(DragDropEffects.Move);
- }
-
- ///
- /// Проверяет, содержат ли эффекты ссылку.
- ///
- /// Эффекты для проверки.
- /// true, если разрешена ссылка; в противном случае — false.
- public static bool CanLink(this DragDropEffects effects)
- {
- return effects.HasEffect(DragDropEffects.Link);
- }
-
- ///
- /// Получает наиболее подходящий эффект на основе модификаторов клавиатуры.
- ///
- /// Нажата ли клавиша Control.
- /// Нажата ли клавиша Shift.
- /// Нажата ли клавиша Alt.
- /// Наиболее подходящий эффект перетаскивания.
- public static DragDropEffects GetEffectFromKeys(bool controlKey, bool shiftKey, bool altKey)
- {
- if (controlKey && shiftKey)
- return DragDropEffects.Link;
- if (controlKey)
- return DragDropEffects.Copy;
- if (shiftKey)
- return DragDropEffects.Move;
- if (altKey)
- return DragDropEffects.Link;
-
- return DragDropEffects.Move; // По умолчанию
- }
-}
\ No newline at end of file
diff --git a/Lattice.Core.DragDrop/Enums/DropPosition.cs b/Lattice.Core.DragDrop/Enums/DropPosition.cs
deleted file mode 100644
index 50e15da..0000000
--- a/Lattice.Core.DragDrop/Enums/DropPosition.cs
+++ /dev/null
@@ -1,14 +0,0 @@
-namespace Lattice.Core.DragDrop.Enums;
-
-///
-/// Позиция сброса относительно цели.
-///
-public enum DropPosition
-{
- Inside,
- Top,
- Bottom,
- Left,
- Right,
- Center
-}
\ No newline at end of file
diff --git a/Lattice.Core.DragDrop/Exceptions/DragDropException.cs b/Lattice.Core.DragDrop/Exceptions/DragDropException.cs
deleted file mode 100644
index 66c8e91..0000000
--- a/Lattice.Core.DragDrop/Exceptions/DragDropException.cs
+++ /dev/null
@@ -1,85 +0,0 @@
-namespace Lattice.Core.DragDrop.Exceptions;
-
-///
-/// Исключение, возникающее при ошибках в системе перетаскивания.
-///
-public class DragDropException : Exception
-{
- ///
- /// Код ошибки.
- ///
- public string ErrorCode { get; }
-
- ///
- /// Инициализирует новый экземпляр класса .
- ///
- public DragDropException()
- : base("Drag & Drop operation failed.")
- {
- ErrorCode = "DRAGDROP_0001";
- }
-
- ///
- /// Инициализирует новый экземпляр класса с указанным сообщением.
- ///
- public DragDropException(string message)
- : base(message)
- {
- ErrorCode = "DRAGDROP_0002";
- }
-
- ///
- /// Инициализирует новый экземпляр класса с кодом ошибки.
- ///
- public DragDropException(string errorCode, string message)
- : base(message)
- {
- ErrorCode = errorCode;
- }
-
- ///
- /// Инициализирует новый экземпляр класса
- /// с указанным сообщением и внутренним исключением.
- ///
- public DragDropException(string message, Exception innerException)
- : base(message, innerException)
- {
- ErrorCode = "DRAGDROP_0003";
- }
-
- ///
- /// Инициализирует новый экземпляр класса
- /// с кодом ошибки, сообщением и внутренним исключением.
- ///
- public DragDropException(string errorCode, string message, Exception innerException)
- : base(message, innerException)
- {
- ErrorCode = errorCode;
- }
-}
-
-///
-/// Коды ошибок Drag and Drop системы.
-///
-public static class DragDropErrorCodes
-{
- // Общие ошибки
- public const string OperationAlreadyActive = "DRAGDROP_1001";
- public const string OperationNotActive = "DRAGDROP_1002";
- public const string InvalidData = "DRAGDROP_1003";
- public const string Timeout = "DRAGDROP_1004";
-
- // Ошибки источников
- public const string SourceCannotDrag = "DRAGDROP_2001";
- public const string SourceStartFailed = "DRAGDROP_2002";
-
- // Ошибки целей
- public const string TargetNotFound = "DRAGDROP_3001";
- public const string TargetCannotAccept = "DRAGDROP_3002";
- public const string TargetDropFailed = "DRAGDROP_3003";
-
- // Ошибки системы
- public const string SystemNotInitialized = "DRAGDROP_4001";
- public const string SystemDisposed = "DRAGDROP_4002";
- public const string MemoryAllocationFailed = "DRAGDROP_4003";
-}
\ No newline at end of file
diff --git a/Lattice.Core.DragDrop/Extensions/DropInfoExtensions.cs b/Lattice.Core.DragDrop/Extensions/DropInfoExtensions.cs
deleted file mode 100644
index a6e9c55..0000000
--- a/Lattice.Core.DragDrop/Extensions/DropInfoExtensions.cs
+++ /dev/null
@@ -1,74 +0,0 @@
-using Lattice.Core.DragDrop.Enums;
-using Lattice.Core.Geometry;
-
-namespace Lattice.Core.DragDrop.Extensions;
-
-///
-/// Методы расширения для DropInfo.
-///
-public static class DropInfoExtensions
-{
- ///
- /// Проверяет, могут ли данные быть приведены к указанному типу.
- ///
- /// Тип данных для проверки.
- /// Информация о сбросе.
- /// true, если данные могут быть приведены к типу T; в противном случае — false.
- public static bool CanAccept(this Models.DropInfo dropInfo)
- where T : class
- {
- return dropInfo.Data is T;
- }
-
- ///
- /// Пытается получить данные как указанный тип.
- ///
- /// Тип, к которому нужно привести данные.
- /// Информация о сбросе.
- /// Данные как тип T или null.
- public static T? GetDataAs(this Models.DropInfo dropInfo)
- where T : class
- {
- return dropInfo.Data as T;
- }
-
- ///
- /// Получает данные как указанный тип или выбрасывает исключение.
- ///
- /// Тип, к которому нужно привести данные.
- /// Информация о сбросе.
- /// Данные как тип T.
- /// Выбрасывается, если данные не могут быть приведены к типу T.
- public static T GetRequiredDataAs(this Models.DropInfo dropInfo)
- where T : class
- {
- if (dropInfo.Data is not T data)
- {
- throw new InvalidCastException(
- $"Ожидался тип {typeof(T).Name}, но получен {dropInfo.Data?.GetType().Name ?? "null"}");
- }
- return data;
- }
-
- ///
- /// Проверяет, содержится ли позиция в указанных границах.
- ///
- /// Информация о сбросе.
- /// Границы для проверки.
- /// true, если позиция находится в границах; в противном случае — false.
- public static bool IsInBounds(this Models.DropInfo dropInfo, Rect bounds)
- {
- return bounds.Contains(dropInfo.Position);
- }
-
- ///
- /// Проверяет можно ли добавить эффект перетаскивания.
- ///
- ///
- ///
- ///
- public static bool CanAcceptEffect(this Models.DropInfo dropInfo, DragDropEffects effect)
- {
- return dropInfo.AllowedEffects.HasEffect(effect);
- }
-}
\ No newline at end of file
diff --git a/Lattice.Core.DragDrop/Factories/DragDropFactory.cs b/Lattice.Core.DragDrop/Factories/DragDropFactory.cs
deleted file mode 100644
index df48263..0000000
--- a/Lattice.Core.DragDrop/Factories/DragDropFactory.cs
+++ /dev/null
@@ -1,581 +0,0 @@
-using Lattice.Core.DragDrop.Abstractions;
-using Lattice.Core.DragDrop.Enums;
-using Lattice.Core.DragDrop.Models;
-using Lattice.Core.DragDrop.Services;
-using Lattice.Core.Geometry;
-
-namespace Lattice.Core.DragDrop.Factories;
-
-///
-/// Фабрика для создания компонентов системы перетаскивания.
-/// Предоставляет методы для создания сервисов, источников и целей перетаскивания.
-///
-///
-/// Эта фабрика позволяет создавать компоненты системы перетаскивания без использования
-/// Dependency Injection, предоставляя простой и понятный API для наиболее распространенных сценариев.
-///
-public static class DragDropFactory
-{
- #region Сервисы перетаскивания
-
- ///
- /// Создает новый экземпляр сервиса перетаскивания с настройками по умолчанию.
- ///
- ///
- /// Экземпляр с настройками по умолчанию.
- ///
- ///
- /// Созданный сервис имеет следующие настройки по умолчанию:
- ///
- /// - Порог начала перетаскивания: 3.0 пикселей
- /// - Таймаут асинхронных операций: 5000 миллисекунд
- /// - Асинхронные операции: включены
- ///
- ///
- public static IDragDropService CreateDragDropService()
- {
- return new DragDropService();
- }
-
- ///
- /// Создает новый экземпляр сервиса перетаскивания с пользовательскими настройками.
- ///
- ///
- /// Делегат для настройки опций сервиса. Передает экземпляр
- /// для настройки параметров.
- ///
- ///
- /// Настроенный экземпляр .
- ///
- ///
- ///
- /// var service = DragDropFactory.CreateDragDropService(options =>
- /// {
- /// options.DragStartThreshold = 5.0;
- /// options.AsyncOperationTimeout = 3000;
- /// options.EnableAsyncOperations = true;
- /// });
- ///
- ///
- public static IDragDropService CreateDragDropService(Action configure)
- {
- var options = new DragDropServiceOptions();
- configure(options);
-
- return new DragDropService
- {
- DragStartThreshold = options.DragStartThreshold,
- AsyncOperationTimeout = options.AsyncOperationTimeout,
- EnableAsyncOperations = options.EnableAsyncOperations
- };
- }
-
- ///
- /// Создает сервис перетаскивания, оптимизированный для сенсорных устройств.
- ///
- ///
- /// Экземпляр с увеличенным порогом перетаскивания.
- ///
- ///
- /// Этот метод создает сервис с увеличенным порогом начала перетаскивания (10.0 пикселей),
- /// что уменьшает вероятность случайного начала перетаскивания при использовании сенсорного экрана.
- ///
- public static IDragDropService CreateTouchOptimizedService()
- {
- return new DragDropService
- {
- DragStartThreshold = 10.0,
- AsyncOperationTimeout = 3000,
- EnableAsyncOperations = true
- };
- }
-
- ///
- /// Создает сервис перетаскивания для точных операций (графические редакторы, карты).
- ///
- ///
- /// Экземпляр с уменьшенным порогом перетаскивания.
- ///
- ///
- /// Этот метод создает сервис с минимальным порогом начала перетаскивания (1.0 пиксель),
- /// что позволяет начинать перетаскивание с максимальной точностью.
- ///
- public static IDragDropService CreatePrecisionDragService()
- {
- return new DragDropService
- {
- DragStartThreshold = 1.0,
- AsyncOperationTimeout = 10000, // Больше времени для сложных операций
- EnableAsyncOperations = true
- };
- }
-
- #endregion
-
- #region Источники перетаскивания
-
- ///
- /// Создает простой источник перетаскивания с фиксированными данными.
- ///
- ///
- /// Данные, которые будут перетаскиваться. Не может быть null.
- ///
- ///
- /// Разрешенные эффекты перетаскивания. По умолчанию разрешены копирование и перемещение.
- ///
- ///
- /// Экземпляр , который всегда предоставляет указанные данные.
- ///
- ///
- /// Выбрасывается, когда равен null.
- ///
- ///
- /// Этот источник подходит для случаев, когда данные для перетаскивания известны заранее
- /// и не изменяются в процессе операции.
- ///
- public static IDragSource CreateSimpleSource(object data, DragDropEffects allowedEffects = DragDropEffects.Copy | DragDropEffects.Move)
- {
- if (data == null)
- throw new ArgumentNullException(nameof(data));
-
- return new SimpleDragSource(data, allowedEffects);
- }
-
- ///
- /// Создает источник перетаскивания с отложенной загрузкой данных.
- ///
- ///
- /// Фабрика данных, которая будет вызвана при начале перетаскивания.
- ///
- ///
- /// Функция проверки возможности начала перетаскивания.
- ///
- ///
- /// Разрешенные эффекты перетаскивания.
- ///
- ///
- /// Экземпляр с отложенной загрузкой данных.
- ///
- ///
- /// Этот источник полезен, когда данные для перетаскивания дорого создавать заранее
- /// или зависят от контекста в момент начала операции.
- ///
- public static IDragSource CreateLazySource(
- Func dataFactory,
- Func canDragChecker = null,
- DragDropEffects allowedEffects = DragDropEffects.Copy | DragDropEffects.Move)
- {
- if (dataFactory == null)
- throw new ArgumentNullException(nameof(dataFactory));
-
- return new LazyDragSource(dataFactory, canDragChecker, allowedEffects);
- }
-
- ///
- /// Создает источник перетаскивания для коллекции элементов.
- ///
- ///
- /// Тип элементов в коллекции.
- ///
- ///
- /// Коллекция элементов для перетаскивания.
- ///
- ///
- /// Функция выбора конкретного элемента для перетаскивания из коллекции.
- ///
- ///
- /// Разрешенные эффекты перетаскивания.
- ///
- ///
- /// Экземпляр для коллекции элементов.
- ///
- ///
- /// Этот источник позволяет перетаскивать элементы из коллекции. Функция
- /// определяет, какой именно элемент из коллекции будет перетаскиваться в текущем контексте.
- ///
- public static IDragSource CreateCollectionSource(
- IEnumerable items,
- Func, T> itemSelector,
- DragDropEffects allowedEffects = DragDropEffects.Copy | DragDropEffects.Move)
- {
- if (items == null)
- throw new ArgumentNullException(nameof(items));
- if (itemSelector == null)
- throw new ArgumentNullException(nameof(itemSelector));
-
- return new CollectionDragSource(items, itemSelector, allowedEffects);
- }
-
- #endregion
-
- #region Цели сброса
-
- ///
- /// Создает простую цель сброса, которая принимает данные любого типа.
- ///
- ///
- /// Обработчик, вызываемый при сбросе данных.
- ///
- ///
- /// Экземпляр , который принимает любые данные.
- ///
- ///
- /// Эта цель подходит для простых сценариев, когда не требуется валидация типа данных.
- ///
- public static IDropTarget CreateSimpleTarget(Action onDrop)
- {
- if (onDrop == null)
- throw new ArgumentNullException(nameof(onDrop));
-
- return new SimpleDropTarget(onDrop);
- }
-
- ///
- /// Создает цель сброса с фильтрацией по типу данных.
- ///
- ///
- /// Тип данных, которые может принимать цель.
- ///
- ///
- /// Обработчик, вызываемый при сбросе данных.
- ///
- ///
- /// Экземпляр , который принимает только данные типа .
- ///
- ///
- /// Эта цель автоматически проверяет тип сбрасываемых данных и вызывает обработчик только
- /// если данные могут быть приведены к указанному типу.
- ///
- public static IDropTarget CreateTypedTarget(Action onDrop) where T : class
- {
- if (onDrop == null)
- throw new ArgumentNullException(nameof(onDrop));
-
- return new TypedDropTarget(onDrop);
- }
-
- ///
- /// Создает цель сброса с пользовательской логикой валидации.
- ///
- ///
- /// Функция проверки возможности приема данных.
- ///
- ///
- /// Обработчик, вызываемый при сбросе данных.
- ///
- ///
- /// Экземпляр с пользовательской логикой валидации.
- ///
- ///
- /// Эта цель позволяет реализовать сложную логику валидации, выходящую за рамки простой проверки типа.
- ///
- public static IDropTarget CreateConditionalTarget(Func canAccept, Action onDrop)
- {
- if (canAccept == null)
- throw new ArgumentNullException(nameof(canAccept));
- if (onDrop == null)
- throw new ArgumentNullException(nameof(onDrop));
-
- return new ConditionalDropTarget(canAccept, onDrop);
- }
-
- #endregion
-
- #region Вспомогательные методы
-
- ///
- /// Создает стандартные эффекты перетаскивания на основе модификаторов клавиатуры.
- ///
- ///
- /// Нажата ли клавиша Control.
- ///
- ///
- /// Нажата ли клавиша Shift.
- ///
- ///
- /// Нажата ли клавиша Alt.
- ///
- ///
- /// , соответствующие комбинации клавиш.
- ///
- ///
- /// Стандартная логика:
- ///
- /// - Control + Shift: Link
- /// - Control: Copy
- /// - Shift: Move
- /// - Alt: Link
- /// - Без модификаторов: Move
- ///
- ///
- public static DragDropEffects GetEffectsFromKeys(bool controlKey, bool shiftKey, bool altKey)
- {
- return DragDropEffectsExtensions.GetEffectFromKeys(controlKey, shiftKey, altKey);
- }
-
- #endregion
-}
-
-///
-/// Опции для настройки сервиса перетаскивания.
-///
-public class DragDropServiceOptions
-{
- ///
- /// Получает или задает порог начала перетаскивания в пикселях.
- ///
- ///
- /// Минимальное расстояние, которое должен пройти курсор, чтобы началась операция перетаскивания.
- /// Значение по умолчанию: 3.0.
- ///
- public double DragStartThreshold { get; set; } = Constants.DragDropConstants.DefaultDragThreshold;
-
- ///
- /// Получает или задает максимальное время ожидания асинхронных операций в миллисекундах.
- ///
- ///
- /// Время в миллисекундах, после которого асинхронная операция будет прервана.
- /// Значение по умолчанию: 5000.
- ///
- public int AsyncOperationTimeout { get; set; } = Constants.DragDropConstants.DefaultAsyncTimeout;
-
- ///
- /// Получает или задает значение, указывающее, включены ли асинхронные операции.
- ///
- ///
- /// true, если асинхронные операции включены; в противном случае — false.
- /// Значение по умолчанию: true.
- ///
- public bool EnableAsyncOperations { get; set; } = true;
-
- ///
- /// Получает или задает интервал автоматической очистки неиспользуемых целей в минутах.
- ///
- ///
- /// Интервал в минутах, через который будут удаляться цели сброса, к которым не было обращений.
- /// Значение по умолчанию: 10.
- ///
- public int CleanupInterval { get; set; } = Constants.DragDropConstants.TargetLifetimeMinutes;
-}
-
-#region Внутренние реализации
-
-internal class SimpleDragSource : IDragSource
-{
- private readonly object _data;
- private readonly DragDropEffects _allowedEffects;
-
- public SimpleDragSource(object data, DragDropEffects allowedEffects)
- {
- _data = data ?? throw new ArgumentNullException(nameof(data));
- _allowedEffects = allowedEffects;
- }
-
- public Task TryStartDragAsync(Point startPosition, CancellationToken cancellationToken = default)
- {
- var dragInfo = new DragInfo(_data, _allowedEffects, startPosition, this);
- return Task.FromResult(dragInfo);
- }
-
- public Task OnDragCompletedAsync(DragInfo dragInfo, DragDropEffects effects, CancellationToken cancellationToken = default)
- {
- return Task.CompletedTask;
- }
-
- public Task OnDragCancelledAsync(DragInfo dragInfo, CancellationToken cancellationToken = default)
- {
- return Task.CompletedTask;
- }
-}
-
-internal class LazyDragSource : IDragSource
-{
- private readonly Func _dataFactory;
- private readonly Func _canDragChecker;
- private readonly DragDropEffects _allowedEffects;
-
- public LazyDragSource(Func dataFactory, Func canDragChecker, DragDropEffects allowedEffects)
- {
- _dataFactory = dataFactory ?? throw new ArgumentNullException(nameof(dataFactory));
- _canDragChecker = canDragChecker ?? (() => true);
- _allowedEffects = allowedEffects;
- }
-
- public Task TryStartDragAsync(Point startPosition, CancellationToken cancellationToken = default)
- {
- if (!_canDragChecker())
- return Task.FromResult(null);
-
- var data = _dataFactory();
- if (data == null)
- return Task.FromResult(null);
-
- var dragInfo = new DragInfo(data, _allowedEffects, startPosition, this);
- return Task.FromResult(dragInfo);
- }
-
- public Task OnDragCompletedAsync(DragInfo dragInfo, DragDropEffects effects, CancellationToken cancellationToken = default)
- {
- return Task.CompletedTask;
- }
-
- public Task OnDragCancelledAsync(DragInfo dragInfo, CancellationToken cancellationToken = default)
- {
- return Task.CompletedTask;
- }
-}
-
-internal class CollectionDragSource : IDragSource
-{
- private readonly IEnumerable _items;
- private readonly Func, T> _itemSelector;
- private readonly DragDropEffects _allowedEffects;
-
- public CollectionDragSource(IEnumerable items, Func, T> itemSelector, DragDropEffects allowedEffects)
- {
- _items = items ?? throw new ArgumentNullException(nameof(items));
- _itemSelector = itemSelector ?? throw new ArgumentNullException(nameof(itemSelector));
- _allowedEffects = allowedEffects;
- }
-
- public Task TryStartDragAsync(Point startPosition, CancellationToken cancellationToken = default)
- {
- var selectedItem = _itemSelector(_items);
- if (selectedItem == null)
- return Task.FromResult(null);
-
- var dragInfo = new DragInfo(selectedItem, _allowedEffects, startPosition, this);
- return Task.FromResult(dragInfo);
- }
-
- public Task OnDragCompletedAsync(DragInfo dragInfo, DragDropEffects effects, CancellationToken cancellationToken = default)
- {
- return Task.CompletedTask;
- }
-
- public Task OnDragCancelledAsync(DragInfo dragInfo, CancellationToken cancellationToken = default)
- {
- return Task.CompletedTask;
- }
-}
-
-internal class SimpleDropTarget : IDropTarget
-{
- private readonly Action _onDrop;
-
- public SimpleDropTarget(Action onDrop)
- {
- _onDrop = onDrop ?? throw new ArgumentNullException(nameof(onDrop));
- }
-
- public Task CanAcceptDropAsync(DropInfo dropInfo, CancellationToken cancellationToken = default)
- {
- return Task.FromResult(dropInfo.Data != null);
- }
-
- public Task OnDragOverAsync(DropInfo dropInfo, CancellationToken cancellationToken = default)
- {
- if (dropInfo.Data != null)
- {
- dropInfo.SuggestedEffects = DragDropEffects.Move;
- }
- return Task.CompletedTask;
- }
-
- public Task OnDropAsync(DropInfo dropInfo, CancellationToken cancellationToken = default)
- {
- if (dropInfo.Data != null)
- {
- _onDrop(dropInfo);
- dropInfo.MarkAsHandled();
- }
- return Task.CompletedTask;
- }
-
- public Task OnDragLeaveAsync(CancellationToken cancellationToken = default)
- {
- return Task.CompletedTask;
- }
-}
-
-internal class TypedDropTarget : IDropTarget where T : class
-{
- private readonly Action _onDrop;
-
- public TypedDropTarget(Action onDrop)
- {
- _onDrop = onDrop ?? throw new ArgumentNullException(nameof(onDrop));
- }
-
- public Task CanAcceptDropAsync(DropInfo dropInfo, CancellationToken cancellationToken = default)
- {
- return Task.FromResult(dropInfo.Data is T);
- }
-
- public Task OnDragOverAsync(DropInfo dropInfo, CancellationToken cancellationToken = default)
- {
- if (dropInfo.Data is T)
- {
- dropInfo.SuggestedEffects = DragDropEffects.Move;
- }
- return Task.CompletedTask;
- }
-
- public Task OnDropAsync(DropInfo dropInfo, CancellationToken cancellationToken = default)
- {
- if (dropInfo.Data is T data)
- {
- _onDrop(data, dropInfo);
- dropInfo.MarkAsHandled();
- }
- return Task.CompletedTask;
- }
-
- public Task OnDragLeaveAsync(CancellationToken cancellationToken = default)
- {
- return Task.CompletedTask;
- }
-}
-
-internal class ConditionalDropTarget : IDropTarget
-{
- private readonly Func _canAccept;
- private readonly Action _onDrop;
-
- public ConditionalDropTarget(Func canAccept, Action onDrop)
- {
- _canAccept = canAccept ?? throw new ArgumentNullException(nameof(canAccept));
- _onDrop = onDrop ?? throw new ArgumentNullException(nameof(onDrop));
- }
-
- public Task CanAcceptDropAsync(DropInfo dropInfo, CancellationToken cancellationToken = default)
- {
- return Task.FromResult(_canAccept(dropInfo));
- }
-
- public Task OnDragOverAsync(DropInfo dropInfo, CancellationToken cancellationToken = default)
- {
- if (_canAccept(dropInfo))
- {
- dropInfo.SuggestedEffects = DragDropEffects.Move;
- }
- return Task.CompletedTask;
- }
-
- public Task OnDropAsync(DropInfo dropInfo, CancellationToken cancellationToken = default)
- {
- if (_canAccept(dropInfo))
- {
- _onDrop(dropInfo);
- dropInfo.MarkAsHandled();
- }
- return Task.CompletedTask;
- }
-
- public Task OnDragLeaveAsync(CancellationToken cancellationToken = default)
- {
- return Task.CompletedTask;
- }
-}
-
-#endregion
\ No newline at end of file
diff --git a/Lattice.Core.DragDrop/Lattice.Core.DragDrop.csproj b/Lattice.Core.DragDrop/Lattice.Core.DragDrop.csproj
deleted file mode 100644
index c18ab9e..0000000
--- a/Lattice.Core.DragDrop/Lattice.Core.DragDrop.csproj
+++ /dev/null
@@ -1,20 +0,0 @@
-
-
-
- net8.0;net9.0;net10.0
- enable
- enable
- latest
- true
- true
- Lattice.Core.DragDrop
- 1.0.0
- FrigaT
- Professional drag-and-drop system for Lattice UI Framework
- ui;framework;drag;drop;docking;toolbox
-
-
-
-
-
-
diff --git a/Lattice.Core.DragDrop/Models/DragInfo.cs b/Lattice.Core.DragDrop/Models/DragInfo.cs
deleted file mode 100644
index 8547d1e..0000000
--- a/Lattice.Core.DragDrop/Models/DragInfo.cs
+++ /dev/null
@@ -1,247 +0,0 @@
-using Lattice.Core.Geometry;
-using System.Collections.Concurrent;
-
-namespace Lattice.Core.DragDrop.Models;
-
-///
-/// Содержит информацию о начале операции перетаскивания.
-/// Этот класс передается от источника перетаскивания к системе перетаскивания
-/// для инициализации и управления операцией.
-///
-///
-///
-/// является ключевым компонентом системы перетаскивания,
-/// инкапсулирующим все необходимые данные для начала операции. Он содержит:
-///
-///
-/// - Данные для передачи
-/// - Разрешенные эффекты перетаскивания
-/// - Начальную позицию операции
-/// - Ссылку на источник перетаскивания
-/// - Дополнительные параметры операции
-///
-///
-/// Этот класс используется как внутренний механизм передачи данных между
-/// и системой управления перетаскиванием.
-///
-///
-public class DragInfo : IDisposable, ICloneable
-{
- private readonly ConcurrentDictionary _parameters = new();
- private bool _disposed;
-
- ///
- /// Получает данные, которые передаются в операции перетаскивания.
- ///
- ///
- /// Объект, содержащий данные для передачи. Может быть любого типа,
- /// поддерживаемого системой перетаскивания.
- ///
- ///
- /// Эти данные будут доступны цели сброса через .
- /// Важно, чтобы данные были сериализуемыми, если операция перетаскивания
- /// может выходить за пределы процесса приложения.
- ///
- public object Data { get; }
-
- ///
- /// Получает разрешенные эффекты для этой операции перетаскивания.
- ///
- ///
- /// Комбинация флагов , определяющая,
- /// какие операции разрешены для этого перетаскивания.
- ///
- ///
- /// Этот параметр используется системой для фильтрации допустимых операций
- /// и предоставления соответствующей визуальной обратной связи пользователю.
- ///
- public Enums.DragDropEffects AllowedEffects { get; }
-
- ///
- /// Получает начальную позицию операции перетаскивания в координатах экрана.
- ///
- ///
- /// Точка в экранных координатах, где была начата операция перетаскивания.
- ///
- ///
- /// Эта позиция используется для вычисления смещения при создании визуального
- /// представления перетаскивания и для определения порога начала операции.
- ///
- public Point StartPosition { get; }
-
- ///
- /// Получает источник перетаскивания, который инициировал операцию.
- ///
- ///
- /// Объект, реализующий , или null,
- /// если источник не доступен или не требуется.
- ///
- ///
- /// Эта ссылка может использоваться для уведомления источника о результате
- /// операции перетаскивания (завершении или отмене).
- ///
- public object? Source { get; }
-
- ///
- /// Получает или задает дополнительные параметры, специфичные для конкретной
- /// реализации перетаскивания.
- ///
- ///
- /// Словарь, содержащий пары ключ-значение с дополнительными параметрами.
- ///
- ///
- /// Используется для передачи контекстной информации, которая не входит
- /// в стандартный набор свойств, но может быть полезной для обработки
- /// операции перетаскивания.
- ///
- public IReadOnlyDictionary Parameters => _parameters;
-
- ///
- /// Инициализирует новый экземпляр класса .
- ///
- ///
- /// Данные, которые передаются в операции перетаскивания.
- /// Не может быть null.
- ///
- ///
- /// Разрешенные эффекты для этой операции перетаскивания.
- ///
- ///
- /// Начальная позиция операции перетаскивания в координатах экрана.
- ///
- ///
- /// Источник перетаскивания, который инициировал операцию. Может быть null.
- ///
- ///
- /// Выбрасывается, когда равен null.
- ///
- ///
- /// Конструктор создает экземпляр с указанными
- /// параметрами и инициализирует коллекцию параметров пустым словарем.
- ///
- public DragInfo(object data, Enums.DragDropEffects allowedEffects, Point startPosition, object? source = null)
- {
- Data = data ?? throw new ArgumentNullException(nameof(data));
-
- // Проверка допустимых значений перечисления
- if (!Enum.IsDefined(typeof(Enums.DragDropEffects), allowedEffects))
- {
- throw new ArgumentException(
- $"Недопустимое значение для {nameof(allowedEffects)}: {allowedEffects}",
- nameof(allowedEffects));
- }
-
- AllowedEffects = allowedEffects;
- StartPosition = startPosition;
- Source = source;
- }
-
- ///
- /// Создает новый экземпляр с теми же данными,
- /// но новой позицией.
- ///
- ///
- /// Новая позиция для информации о перетаскивании.
- ///
- ///
- /// Новый экземпляр с обновленной позицией.
- ///
- ///
- /// Этот метод используется для обновления информации о перетаскивании
- /// при перемещении курсора, сохраняя исходные данные и параметры.
- ///
- public DragInfo CloneWithPosition(Point newPosition)
- {
- ThrowIfDisposed();
-
- var clone = new DragInfo(Data, AllowedEffects, newPosition, Source)
- {
- _disposed = false,
- };
-
- foreach (var kvp in _parameters)
- {
- clone._parameters[kvp.Key] = kvp.Value;
- }
-
- return clone;
- }
-
- ///
- /// Создает новый экземпляр с теми же данными.
- ///
- public DragInfo Clone() => new DragInfo(Data, AllowedEffects, StartPosition, Source);
-
- ///
- object ICloneable.Clone() => this.Clone();
-
- ///
- /// Получает или дополнительные параметры, специфичные для конкретной
- /// реализации перетаскивания.
- ///
- public T? GetParameter(string key, T? defaultValue = default)
- {
- if (Parameters.TryGetValue(key, out var value) && value is T typedValue)
- {
- return typedValue;
- }
- return defaultValue;
- }
-
- ///
- /// Получает или дополнительные параметры, специфичные для конкретной
- /// реализации перетаскивания.
- ///
- public bool TryGetParameter(string key, out T? value)
- {
- value = default;
-
- if (_parameters.TryGetValue(key, out var objValue) && objValue is T typedValue)
- {
- value = typedValue;
- return true;
- }
-
- return false;
- }
-
- ///
- /// Задает дополнительные параметры, специфичные для конкретной
- /// реализации перетаскивания.
- ///
- public void SetParameter(string key, T value)
- {
- _parameters[key] = value!;
- }
-
- ///
- /// Освобождает ресурсы.
- ///
- public void Dispose()
- {
- if (_disposed) return;
-
- foreach (var value in _parameters.Values)
- {
- if (value is IDisposable disposable)
- {
- disposable.Dispose();
- }
- }
-
- _parameters.Clear();
- _disposed = true;
- GC.SuppressFinalize(this);
- }
-
- private void ThrowIfDisposed()
- {
- if (_disposed)
- throw new ObjectDisposedException(nameof(DragInfo));
- }
-
- ~DragInfo()
- {
- Dispose();
- }
-}
\ No newline at end of file
diff --git a/Lattice.Core.DragDrop/Models/DropInfo.cs b/Lattice.Core.DragDrop/Models/DropInfo.cs
deleted file mode 100644
index 66dd2f6..0000000
--- a/Lattice.Core.DragDrop/Models/DropInfo.cs
+++ /dev/null
@@ -1,281 +0,0 @@
-using Lattice.Core.DragDrop.Enums;
-using Lattice.Core.Geometry;
-
-namespace Lattice.Core.DragDrop.Models;
-
-///
-/// Содержит информацию о потенциальном или фактическом сбросе в операции перетаскивания.
-/// Этот класс используется для передачи данных между системой перетаскивания
-/// и целью сброса ( ).
-///
-///
-///
-/// предоставляет цель сброса всей необходимой информацией
-/// для принятия решения о возможности сброса и выполнения соответствующей операции.
-/// Ключевые аспекты включают:
-///
-///
-/// - Предлагаемые для сброса данные
-/// - Текущую позицию курсора
-/// - Разрешенные эффекты от источника
-/// - Предлагаемые эффекты для сброса
-/// - Ссылку на цель сброса
-/// - Флаг обработки операции
-///
-///
-/// Этот класс является изменяемым, позволяя цели сброса обновлять предлагаемые
-/// эффекты и помечать операцию как обработанную.
-///
-///
-public class DropInfo
-{
- private DragDropEffects _effects = DragDropEffects.None;
-
- ///
- /// Получает или задает позицию сброса относительно цели.
- ///
- public DropPosition DropPosition { get; set; } = DropPosition.Inside;
-
- ///
- /// Получает или задает значение, указывающее, нужно ли показывать визуальную обратную связь.
- ///
- public bool ShowVisualFeedback { get; set; } = true;
-
- ///
- /// Получает или задает данные для визуальной обратной связи.
- ///
- public object? VisualFeedbackData { get; set; }
-
- ///
- /// Получает данные, которые предлагаются для сброса.
- ///
- ///
- /// Данные, переданные от источника перетаскивания, или null, если данные
- /// не доступны или операция была отменена.
- ///
- ///
- /// Эти данные соответствуют свойству из
- /// исходной информации о перетаскивании.
- ///
- public object? Data { get; }
-
- ///
- /// Получает текущую позицию курсора в координатах экрана.
- ///
- ///
- /// Точка в экранных координатах, представляющая текущее положение курсора
- /// мыши во время операции перетаскивания.
- ///
- ///
- /// Эта позиция используется для определения точного места сброса и может
- /// влиять на предлагаемые эффекты (например, различные операции для
- /// разных областей цели сброса).
- ///
- public Point Position { get; }
-
- ///
- /// Получает разрешенные эффекты от источника перетаскивания.
- ///
- ///
- /// Комбинация флагов , определяющая,
- /// какие операции разрешил источник.
- ///
- ///
- /// Цель сброса должна уважать эти ограничения и не предлагать эффекты,
- /// которые не разрешены источником.
- ///
- public Enums.DragDropEffects AllowedEffects { get; }
-
- ///
- /// Получает или задает предлагаемые эффекты для операции сброса.
- ///
- ///
- /// Комбинация флагов , предлагаемая
- /// целью сброса. По умолчанию равно .
- ///
- ///
- ///
- /// Цель сброса должна установить это свойство в методе
- /// на основе анализа предоставленных данных и текущего контекста.
- ///
- ///
- /// Если цель не устанавливает это свойство, система перетаскивания
- /// будет использовать эффекты по умолчанию.
- ///
- ///
- public Enums.DragDropEffects SuggestedEffects
- {
- get => _effects;
- set => _effects = value;
- }
-
- ///
- /// Получает цель сброса, которая обрабатывает эту информацию.
- ///
- ///
- /// Объект, реализующий , или null,
- /// если цель не определена.
- ///
- ///
- /// Эта ссылка позволяет системе идентифицировать, какая цель обрабатывает
- /// информацию о сбросе, и используется для отслеживания изменений цели
- /// во время операции перетаскивания.
- ///
- public object? Target { get; }
-
- ///
- /// Получает или задает дополнительные параметры, специфичные для конкретной
- /// реализации перетаскивания.
- ///
- ///
- /// Словарь, содержащий пары ключ-значение с дополнительными параметрами.
- ///
- ///
- /// Может использоваться для передачи контекстной информации между
- /// различными компонентами системы перетаскивания или для хранения
- /// временных данных во время обработки операции.
- ///
- public Dictionary Parameters { get; set; }
-
- ///
- /// Получает значение, указывающее, был ли сброс уже обработан.
- ///
- ///
- /// true, если операция сброса была помечена как обработанная;
- /// в противном случае — false.
- ///
- ///
- ///
- /// Это свойство используется для предотвращения множественной обработки
- /// одной и той же операции сброса. После вызова метода ,
- /// свойство становится true.
- ///
- ///
- /// Система перетаскивания может проверять это свойство, чтобы определить,
- /// нужно ли выполнять дополнительную обработку по умолчанию.
- ///
- ///
- public bool Handled { get; private set; }
-
- ///
- /// Получает дополнительные параметры, специфичные для конкретной
- /// реализации перетаскивания.
- ///
- public T? GetParameter(string key, T? defaultValue = default)
- {
- if (Parameters.TryGetValue(key, out var value) && value is T typedValue)
- {
- return typedValue;
- }
- return defaultValue;
- }
-
- ///
- /// Получает дополнительные параметры, специфичные для конкретной
- /// реализации перетаскивания.
- ///
- public bool TryGetParameter(string key, out T? value)
- {
- value = default;
-
- if (Parameters.TryGetValue(key, out var objValue) && objValue is T typedValue)
- {
- value = typedValue;
- return true;
- }
-
- return false;
- }
-
- ///
- /// Задает дополнительные параметры, специфичные для конкретной
- /// реализации перетаскивания.
- ///
- public void SetParameter(string key, T value)
- {
- Parameters[key] = value!;
- }
-
- ///
- /// Инициализирует новый экземпляр класса .
- ///
- ///
- /// Данные, которые предлагаются для сброса. Может быть null.
- ///
- ///
- /// Текущая позиция курсора в координатах экрана.
- ///
- ///
- /// Разрешенные эффекты от источника перетаскивания.
- ///
- ///
- /// Цель сброса, которая обрабатывает эту информацию. Может быть null.
- ///
- ///
- /// Конструктор создает экземпляр с указанными
- /// параметрами, инициализирует коллекцию параметров пустым словарем
- /// и устанавливает флаг в false.
- ///
- public DropInfo(object? data, Point position, Enums.DragDropEffects allowedEffects, object? target = null)
- {
- Data = data;
- Position = position;
- AllowedEffects = allowedEffects;
- Target = target;
- Parameters = new Dictionary();
- Handled = false;
- }
-
- ///
- /// Помечает сброс как обработанный.
- ///
- ///
- ///
- /// Этот метод должен вызываться целью сброса в методе ,
- /// если она успешно обработала операцию сброса.
- ///
- ///
- /// После вызова этого метода свойство становится true,
- /// что сигнализирует системе перетаскивания о том, что дополнительная
- /// обработка не требуется.
- ///
- ///
- public void MarkAsHandled()
- {
- Handled = true;
- }
-
- ///
- /// Создает новый экземпляр с теми же данными,
- /// но новой позицией.
- ///
- ///
- /// Новая позиция для информации о сбросе.
- ///
- ///
- /// Новый экземпляр с обновленной позицией.
- ///
- ///
- /// Этот метод используется для обновления информации о сбросе при
- /// перемещении курсора, сохраняя исходные данные и параметры.
- ///
- public DropInfo WithPosition(Point newPosition)
- {
- return new DropInfo(Data, newPosition, AllowedEffects, Target)
- {
- Parameters = new Dictionary(Parameters),
- SuggestedEffects = _effects,
- DropPosition = DropPosition,
- ShowVisualFeedback = ShowVisualFeedback,
- VisualFeedbackData = VisualFeedbackData
- };
- }
-
- ///
- /// Проверка установки эффекта перетаскивания в разрешенные эффекты.
- ///
- public bool CanAcceptEffect(Enums.DragDropEffects effect)
- {
- return (AllowedEffects & effect) != Enums.DragDropEffects.None;
- }
-}
\ No newline at end of file
diff --git a/Lattice.Core.DragDrop/README.md b/Lattice.Core.DragDrop/README.md
deleted file mode 100644
index 2a1dba8..0000000
--- a/Lattice.Core.DragDrop/README.md
+++ /dev/null
@@ -1,829 +0,0 @@
-# Lattice.Core.DragDrop
-
-Профессиональная, асинхронная система перетаскивания для .NET приложений. Полностью потокобезопасная, расширяемая архитектура с поддержкой кросс-платформенности.
-
-## 📋 Особенности
-
-- ✅ **Полная асинхронная поддержка** - async/await для всех операций
-- ✅ **Потокобезопасность** - `ReaderWriterLockSlim` для эффективной синхронизации
-- ✅ **Производительность** - Оптимизированные алгоритмы, кэширование, минимальные аллокации
-- ✅ **Расширяемость** - Легкая интеграция с любыми UI фреймворками
-- ✅ **Надежность** - Таймауты, обработка ошибок, корректное освобождение ресурсов
-- ✅ **Статистика** - Встроенный мониторинг производительности
-
-## 🏗️ Архитектура
-
-### Основные компоненты
-
-```
-Lattice.Core.DragDrop/
-├── Abstractions/ # Интерфейсы
-│ ├── IDragSource.cs # Источник перетаскивания (синхронный)
-│ ├── IAsyncDragSource.cs # Асинхронный источник
-│ ├── IDropTarget.cs # Цель сброса (синхронная)
-│ └── IAsyncDropTarget.cs # Асинхронная цель
-├── Enums/ # Перечисления
-├── Exceptions/ # Исключения с кодами ошибок
-├── Extensions/ # Расширения для DI
-├── Models/ # Модели данных
-│ ├── DragInfo.cs # Информация о перетаскивании
-│ └── DropInfo.cs # Информация о сбросе
-└── Services/ # Сервисы
- ├── IDragDropService.cs # Основной интерфейс
- ├── DragDropService.cs # Реализация сервиса
- └── EventArgs/ # Аргументы событий
-```
-
-## 🚀 Быстрый старт
-
-### 1. Установка
-
-```csharp
-// Добавьте проект Lattice.Core.DragDrop в ваше решение
-// или создайте NuGet пакет
-```
-
-### 2. Базовое использование
-
-```csharp
-using Lattice.Core.DragDrop;
-using Lattice.Core.DragDrop.Abstractions;
-using Lattice.Core.DragDrop.Services;
-using Lattice.Core.Geometry;
-
-// Создаем сервис
-var dragDropService = new DragDropService();
-
-// Создаем простой источник перетаскивания
-var dragSource = DragDropUtilities.CreateSimpleDragSource(
- dataProvider: () => "Example Data",
- canDrag: () => true,
- onCompleted: (dragInfo, effects) =>
- Console.WriteLine($"Drag completed with effects: {effects}"),
- onCancelled: dragInfo =>
- Console.WriteLine("Drag cancelled")
-);
-
-// Создаем простую цель сброса
-var dropTarget = DragDropUtilities.CreateSimpleDropTarget(
- canAccept: dropInfo => dropInfo.Data is string,
- onDragOver: dropInfo =>
- dropInfo.SuggestedEffects = DragDropEffects.Copy,
- onDrop: dropInfo =>
- {
- Console.WriteLine($"Dropped: {dropInfo.Data}");
- dropInfo.MarkAsHandled();
- }
-);
-
-// Регистрируем цель
-string targetId = dragDropService.RegisterDropTarget(
- dropTarget,
- new Rect(100, 100, 300, 200)
-);
-
-// Начинаем перетаскивание
-bool started = dragDropService.StartDrag(
- dragSource,
- new Point(50, 50)
-);
-
-if (started)
-{
- // Обновляем позицию
- dragDropService.UpdateDrag(new Point(150, 150));
-
- // Завершаем
- var effects = dragDropService.EndDrag(new Point(200, 200));
-}
-```
-
-## 📖 Подробное руководство
-
-### Сервис перетаскивания
-
-Основной класс системы - `DragDropService`, реализующий `IDragDropService`.
-
-```csharp
-// Создание с кастомными настройками
-var service = new DragDropService(options =>
-{
- options.DragStartThreshold = 5.0;
- options.EnableAsyncOperations = true;
- options.AsyncOperationTimeout = 3000;
- options.EnableAutoCleanup = true;
-});
-
-// Свойства
-bool isActive = service.IsDragActive; // Активна ли операция
-DragInfo? currentDrag = service.CurrentDragInfo; // Текущая информация
-double threshold = service.DragStartThreshold; // Порог начала
-
-// События
-service.DragStarted += OnDragStarted;
-service.DragUpdated += OnDragUpdated;
-service.DragCompleted += OnDragCompleted;
-service.DragCancelled += OnDragCancelled;
-service.ErrorOccurred += OnErrorOccurred;
-
-// Регистрация целей
-string id = service.RegisterDropTarget(
- target, // IDropTarget
- bounds, // Rect
- priority: 1, // Приоритет (выше = выше приоритет)
- group: "main" // Группа для группового удаления
-);
-
-// Обновление границ
-service.UpdateDropTargetBounds(id, newBounds);
-
-// Удаление
-service.UnregisterDropTarget(id);
-service.UnregisterDropTargetsInGroup("main");
-```
-
-### Асинхронное использование
-
-```csharp
-// Асинхронные методы
-bool started = await service.StartDragAsync(source, startPosition);
-await service.UpdateDragAsync(currentPosition);
-DragDropEffects effects = await service.EndDragAsync(dropPosition);
-await service.CancelDragAsync();
-
-// Статистика
-var stats = service.GetStats();
-Console.WriteLine($"Operations: {stats.TotalDragOperations}");
-Console.WriteLine($"Success rate: {stats.SuccessfulDrops}/{stats.TotalDragOperations}");
-Console.WriteLine($"Avg time: {stats.AverageOperationTime.TotalMilliseconds}ms");
-```
-
-### Создание кастомных источников и целей
-
-#### Синхронная реализация
-
-```csharp
-public class FileDragSource : IDragSource
-{
- private readonly FileInfo _file;
-
- public FileDragSource(FileInfo file) => _file = file;
-
- public bool CanStartDrag(out DragInfo? dragInfo)
- {
- // Проверяем условия
- if (!_file.Exists || _file.Length > 100 * 1024 * 1024) // 100 MB limit
- {
- dragInfo = null;
- return false;
- }
-
- // Создаем DragInfo
- dragInfo = new DragInfo(
- data: _file,
- allowedEffects: DragDropEffects.Copy | DragDropEffects.Move,
- startPosition: Point.Zero,
- source: this
- );
-
- // Добавляем дополнительные параметры
- dragInfo.SetParameter("FileSize", _file.Length);
- dragInfo.SetParameter("MimeType", GetMimeType(_file));
-
- return true;
- }
-
- public bool StartDrag(DragInfo dragInfo)
- {
- // Подготовка к перетаскиванию
- // Можно создать визуальное представление и т.д.
- Console.WriteLine($"Starting drag of {_file.Name}");
- return true;
- }
-
- public void DragCompleted(DragInfo dragInfo, DragDropEffects effects)
- {
- Console.WriteLine($"File drag completed with {effects}");
-
- if (effects == DragDropEffects.Move)
- {
- // Файл был перемещен - возможно, удалить оригинал
- // _file.Delete();
- }
- }
-
- public void DragCancelled(DragInfo dragInfo)
- {
- Console.WriteLine("File drag cancelled");
- }
-}
-```
-
-#### Асинхронная реализация
-
-```csharp
-public class DatabaseItemDragSource : IAsyncDragSource
-{
- private readonly DatabaseService _db;
- private readonly int _itemId;
-
- public async Task<(bool CanStart, DragInfo? DragInfo)> CanStartDragAsync()
- {
- try
- {
- // Асинхронные проверки
- var canDrag = await _db.CanDragItemAsync(_itemId);
- if (!canDrag) return (false, null);
-
- // Асинхронная загрузка данных
- var data = await _db.GetItemForDragAsync(_itemId);
- if (data == null) return (false, null);
-
- var dragInfo = new DragInfo(
- data: data,
- allowedEffects: DragDropEffects.Copy | DragDropEffects.Move,
- startPosition: Point.Zero,
- source: this
- );
-
- return (true, dragInfo);
- }
- catch (Exception ex)
- {
- // Логирование ошибки
- return (false, null);
- }
- }
-
- public Task StartDragAsync(DragInfo dragInfo)
- {
- // Асинхронная подготовка
- return Task.FromResult(true);
- }
-
- public async Task DragCompletedAsync(DragInfo dragInfo, DragDropEffects effects)
- {
- // Асинхронная обработка завершения
- await _db.LogDragOperationAsync(_itemId, effects);
-
- if (effects == DragDropEffects.Move)
- {
- await _db.MarkItemAsMovedAsync(_itemId);
- }
- }
-
- public Task DragCancelledAsync(DragInfo dragInfo)
- {
- return Task.CompletedTask;
- }
-
- // Синхронные методы для совместимости
- public bool CanStartDrag(out DragInfo? dragInfo)
- {
- var result = Task.Run(() => CanStartDragAsync()).GetAwaiter().GetResult();
- dragInfo = result.DragInfo;
- return result.CanStart;
- }
-
- // ... остальные синхронные методы
-}
-```
-
-### Работа с моделями данных
-
-#### DragInfo
-
-```csharp
-// Создание
-var dragInfo = new DragInfo(
- data: myObject,
- allowedEffects: DragDropEffects.Copy | DragDropEffects.Move,
- startPosition: new Point(x, y),
- source: this
-);
-
-// Параметры
-dragInfo.SetParameter("Timestamp", DateTime.UtcNow);
-dragInfo.SetParameter("UserId", currentUser.Id);
-
-// Получение параметров
-if (dragInfo.TryGetParameter("Category", out var category))
-{
- // Используем категорию
-}
-
-// Клонирование с новой позицией
-var updatedDragInfo = dragInfo.CloneWithPosition(newPosition);
-
-// Очистка ресурсов
-dragInfo.Dispose();
-```
-
-#### DropInfo
-
-```csharp
-// Создается сервисом автоматически
-// Работа с DropInfo в методах цели:
-
-public void DragOver(DropInfo dropInfo)
-{
- // Проверяем данные
- if (dropInfo.Data is MyDataType myData)
- {
- // Определяем позицию относительно цели
- dropInfo.DropPosition = CalculateDropPosition(dropInfo.Position);
-
- // Предлагаем эффекты
- if (CanAcceptData(myData))
- {
- dropInfo.SuggestedEffects = DragDropEffects.Move;
- dropInfo.ShowVisualFeedback = true;
- dropInfo.VisualFeedbackData = CreatePreview(myData);
- }
- else
- {
- dropInfo.SuggestedEffects = DragDropEffects.None;
- }
- }
-}
-
-public void Drop(DropInfo dropInfo)
-{
- if (dropInfo.Data is MyDataType myData)
- {
- // Обработка сброса
- ProcessDrop(myData, dropInfo.DropPosition);
-
- // Помечаем как обработанное
- dropInfo.MarkAsHandled();
- }
-}
-```
-
-### Утилиты и фабрики
-
-#### Синхронные утилиты
-
-```csharp
-// Простые реализации
-var simpleSource = DragDropUtilities.CreateSimpleDragSource(
- () => data,
- () => true,
- (dragInfo, effects) => Console.WriteLine("Completed"),
- dragInfo => Console.WriteLine("Cancelled")
-);
-
-var simpleTarget = DragDropUtilities.CreateSimpleDropTarget(
- dropInfo => dropInfo.Data != null,
- dropInfo => dropInfo.SuggestedEffects = DragDropEffects.Copy,
- dropInfo => Console.WriteLine($"Dropped: {dropInfo.Data}"),
- () => Console.WriteLine("Drag left")
-);
-
-// Геометрия
-double distance = DragDropUtilities.CalculateDistance(p1, p2);
-bool exceeded = DragDropUtilities.HasExceededDragThreshold(start, current, threshold);
-DropPosition position = DragDropUtilities.GetDropPosition(point, bounds, edgeThreshold);
-
-// Проверка совместимости
-bool compatible = DragDropUtilities.AreEffectsCompatible(sourceEffects, targetEffects);
-bool typeMatch = DragDropUtilities.IsDataCompatible(data, new[] { typeof(string), typeof(int) });
-```
-
-#### Асинхронные утилиты
-
-```csharp
-// Асинхронные реализации
-var asyncSource = AsyncDragDropUtilities.CreateAsyncDragSource(
- async () => await LoadDataAsync(),
- async () => await CanDragAsync(),
- async (dragInfo, effects) => await OnCompletedAsync(dragInfo, effects),
- async dragInfo => await OnCancelledAsync(dragInfo)
-);
-
-var asyncTarget = AsyncDragDropUtilities.CreateAsyncDropTarget(
- async dropInfo => await CanAcceptAsync(dropInfo.Data),
- async dropInfo => await OnDragOverAsync(dropInfo),
- async dropInfo => await OnDropAsync(dropInfo),
- async () => await OnDragLeaveAsync()
-);
-
-// Адаптеры для синхронных интерфейсов
-IAsyncDragSource asyncFromSync = AsyncDragDropUtilities.CreateAsyncAdapter(syncSource);
-IAsyncDropTarget asyncTargetFromSync = AsyncDragDropUtilities.CreateAsyncAdapter(syncTarget);
-
-// Комбинированные реализации (fallback стратегия)
-var combined = AsyncDragDropUtilities.Combine(
- syncSource,
- asyncSource,
- preferAsync: true // При ошибке в async использует sync
-);
-
-// Таймауты
-var result = await AsyncDragDropUtilities.ExecuteWithTimeoutAsync(
- task: LongOperationAsync(),
- timeout: TimeSpan.FromSeconds(5),
- defaultValue: fallbackValue
-);
-```
-
-### Обработка ошибок
-
-```csharp
-// Подписка на ошибки
-service.ErrorOccurred += (sender, e) =>
-{
- Console.WriteLine($"Error in {e.Operation}: {e.Exception.Message}");
-
- // Коды ошибок определены в DragDropErrorCodes
- switch (e.ErrorCode)
- {
- case DragDropErrorCodes.Timeout:
- Console.WriteLine("Operation timed out");
- break;
- case DragDropErrorCodes.SourceCannotDrag:
- Console.WriteLine("Source cannot drag");
- break;
- case DragDropErrorCodes.TargetCannotAccept:
- Console.WriteLine("Target cannot accept");
- break;
- }
-};
-
-// Использование в коде
-try
-{
- await service.StartDragAsync(source, position);
-}
-catch (DragDropException ex)
-{
- // Обработка специфичных для DragDrop ошибок
- Console.WriteLine($"DragDrop error {ex.ErrorCode}: {ex.Message}");
-}
-catch (Exception ex)
-{
- // Обработка других ошибок
- Console.WriteLine($"General error: {ex.Message}");
-}
-```
-
-## 🔧 Интеграция с UI фреймворками
-
-### Базовый адаптер для WinUI/WPF
-
-```csharp
-public class UIElementDragSource : IAsyncDragSource
-{
- private readonly FrameworkElement _element;
- private readonly Func _dataProvider;
-
- public UIElementDragSource(FrameworkElement element, Func dataProvider)
- {
- _element = element;
- _dataProvider = dataProvider;
-
- // Подписка на события
- _element.PointerPressed += OnPointerPressed;
- _element.PointerMoved += OnPointerMoved;
- _element.PointerReleased += OnPointerReleased;
- }
-
- private Point _dragStartPosition;
- private bool _isDragging;
-
- private void OnPointerPressed(object sender, PointerRoutedEventArgs e)
- {
- var point = e.GetCurrentPoint(_element);
- _dragStartPosition = new Point(point.Position.X, point.Position.Y);
- }
-
- private async void OnPointerMoved(object sender, PointerRoutedEventArgs e)
- {
- if (_isDragging) return;
-
- var point = e.GetCurrentPoint(_element);
- var current = new Point(point.Position.X, point.Position.Y);
-
- var distance = Math.Sqrt(
- Math.Pow(current.X - _dragStartPosition.X, 2) +
- Math.Pow(current.Y - _dragStartPosition.Y, 2));
-
- if (distance > 3.0) // Порог
- {
- _isDragging = true;
-
- // Начинаем перетаскивание через сервис
- var service = GetDragDropService();
- await service.StartDragAsync(this, ConvertToScreen(_dragStartPosition));
- }
- }
-
- public async Task<(bool CanStart, DragInfo? DragInfo)> CanStartDragAsync()
- {
- var data = _dataProvider();
- if (data == null) return (false, null);
-
- var dragInfo = new DragInfo(
- data,
- DragDropEffects.Copy | DragDropEffects.Move,
- Point.Zero,
- this
- );
-
- return (true, dragInfo);
- }
-
- // ... остальная реализация
-}
-```
-
-## 🧪 Тестирование
-
-### Примеры модульных тестов
-
-```csharp
-[TestClass]
-public class DragDropServiceTests
-{
- private DragDropService _service;
- private Mock _mockSource;
- private Mock _mockTarget;
-
- [TestInitialize]
- public void Setup()
- {
- _service = new DragDropService();
- _mockSource = new Mock();
- _mockTarget = new Mock();
- }
-
- [TestMethod]
- public async Task StartDrag_ValidSource_ReturnsTrue()
- {
- // Arrange
- var dragInfo = new DragInfo("test", DragDropEffects.Copy, Point.Zero);
- _mockSource.Setup(s => s.CanStartDragAsync())
- .ReturnsAsync((true, dragInfo));
- _mockSource.Setup(s => s.StartDragAsync(It.IsAny()))
- .ReturnsAsync(true);
-
- // Act
- var result = await _service.StartDragAsync(_mockSource.Object, Point.Zero);
-
- // Assert
- Assert.IsTrue(result);
- Assert.IsTrue(_service.IsDragActive);
- }
-
- [TestMethod]
- public async Task UpdateDrag_FindsTarget_CallsDragOver()
- {
- // Arrange
- var targetId = _service.RegisterDropTarget(
- _mockTarget.Object,
- new Rect(0, 0, 100, 100)
- );
-
- await StartTestDrag();
-
- _mockTarget.Setup(t => t.CanAcceptDropAsync(It.IsAny()))
- .ReturnsAsync(true);
-
- // Act
- await _service.UpdateDragAsync(new Point(50, 50));
-
- // Assert
- _mockTarget.Verify(t => t.DragOverAsync(It.IsAny()), Times.Once);
- }
-
- private async Task StartTestDrag()
- {
- var dragInfo = new DragInfo("test", DragDropEffects.Copy, Point.Zero);
- _mockSource.Setup(s => s.CanStartDragAsync())
- .ReturnsAsync((true, dragInfo));
- _mockSource.Setup(s => s.StartDragAsync(It.IsAny()))
- .ReturnsAsync(true);
-
- await _service.StartDragAsync(_mockSource.Object, Point.Zero);
- }
-}
-```
-
-## 📊 Мониторинг и производительность
-
-### Сбор статистики
-
-```csharp
-// Получение статистики
-var stats = service.GetStats();
-
-Console.WriteLine($"Total operations: {stats.TotalDragOperations}");
-Console.WriteLine($"Successful: {stats.SuccessfulDrops}");
-Console.WriteLine($"Cancelled: {stats.CancelledOperations}");
-Console.WriteLine($"Errors: {stats.ErrorCount}");
-Console.WriteLine($"Avg time: {stats.AverageOperationTime.TotalMilliseconds}ms");
-
-// Мониторинг в реальном времени
-private Stopwatch _operationTimer;
-
-service.DragStarted += (s, e) =>
-{
- _operationTimer = Stopwatch.StartNew();
- Console.WriteLine($"Drag started from {e.DragInfo.Source}");
-};
-
-service.DragCompleted += (s, e) =>
-{
- _operationTimer.Stop();
- Console.WriteLine($"Drag completed in {_operationTimer.ElapsedMilliseconds}ms");
-
- if (service.EnableAsyncOperations)
- {
- var stats = service.GetStats();
- Console.WriteLine($"Success rate: {(double)stats.SuccessfulDrops / stats.TotalDragOperations:P}");
- }
-};
-```
-
-### Оптимизация производительности
-
-```csharp
-// 1. Настройка параметров
-var service = new DragDropService(options =>
-{
- options.DragStartThreshold = 4.0; // Увеличить порог для предотвращения случайных перетаскиваний
- options.AsyncOperationTimeout = 2000; // Уменьшить таймаут для отзывчивости
- options.EnableAutoCleanup = true; // Автоочистка неиспользуемых целей
-});
-
-// 2. Группировка целей
-_service.RegisterDropTarget(target1, bounds1, group: "toolbox");
-_service.RegisterDropTarget(target2, bounds2, group: "toolbox");
-// Быстрое удаление всех целей группы
-_service.UnregisterDropTargetsInGroup("toolbox");
-
-// 3. Приоритеты для оптимизации поиска
-_service.RegisterDropTarget(importantTarget, bounds, priority: 100); // Высокий приоритет
-_service.RegisterDropTarget(defaultTarget, bounds, priority: 0); // Низкий приоритет
-
-// 4. Периодическая очистка
-service.ClearAllDropTargets(); // При смене контекста
-```
-
-## 🚀 Продвинутые сценарии
-
-### Переупорядочивание элементов
-
-```csharp
-public class ReorderableListDropTarget : IAsyncDropTarget
-{
- private readonly IList _items;
-
- public async Task CanAcceptDropAsync(DropInfo dropInfo)
- {
- return dropInfo.Data is object && _items.Contains(dropInfo.Data);
- }
-
- public async Task DropAsync(DropInfo dropInfo)
- {
- var item = dropInfo.Data;
- var insertIndex = CalculateInsertIndex(dropInfo);
-
- // Удаляем из старой позиции
- _items.Remove(item);
-
- // Вставляем в новую позицию
- if (insertIndex < _items.Count)
- _items.Insert(insertIndex, item);
- else
- _items.Add(item);
-
- dropInfo.MarkAsHandled();
- }
-
- private int CalculateInsertIndex(DropInfo dropInfo)
- {
- // Логика определения позиции вставки на основе dropInfo.Position
- // и визуального расположения элементов
- return 0;
- }
-}
-```
-
-### Мультиселект и групповое перетаскивание
-
-```csharp
-public class MultiSelectionDragSource : IAsyncDragSource
-{
- private readonly IEnumerable _selectedItems;
-
- public async Task<(bool CanStart, DragInfo? DragInfo)> CanStartDragAsync()
- {
- if (!_selectedItems.Any()) return (false, null);
-
- // Создаем коллекцию для перетаскивания
- var dragData = new DragItemCollection(_selectedItems);
-
- var dragInfo = new DragInfo(
- dragData,
- DragDropEffects.Copy | DragDropEffects.Move,
- Point.Zero,
- this
- );
-
- dragInfo.SetParameter("ItemCount", _selectedItems.Count());
- dragInfo.SetParameter("IsMultiSelect", true);
-
- return (true, dragInfo);
- }
-}
-```
-
-## 📚 API Reference
-
-### Основные интерфейсы
-
-#### IDragDropService
-```csharp
-bool IsDragActive { get; }
-DragInfo? CurrentDragInfo { get; }
-IDropTarget? CurrentDropTarget { get; }
-double DragStartThreshold { get; set; }
-bool EnableAsyncOperations { get; set; }
-
-// Регистрация целей
-string RegisterDropTarget(IDropTarget target, Rect bounds, int priority = 0, string? group = null);
-bool UpdateDropTargetBounds(string id, Rect bounds);
-bool UnregisterDropTarget(string id);
-void UnregisterDropTargetsInGroup(string group);
-
-// Асинхронные операции
-Task StartDragAsync(IDragSource source, Point startPosition);
-Task UpdateDragAsync(Point position);
-Task EndDragAsync(Point position);
-Task CancelDragAsync();
-
-// Синхронные операции
-bool StartDrag(IDragSource source, Point startPosition);
-void UpdateDrag(Point position);
-DragDropEffects EndDrag(Point position);
-void CancelDrag();
-
-// Утилиты
-void ClearAllDropTargets();
-DragDropStats GetStats();
-```
-
-#### IAsyncDragSource
-```csharp
-Task<(bool CanStart, DragInfo? DragInfo)> CanStartDragAsync();
-Task StartDragAsync(DragInfo dragInfo);
-Task DragCompletedAsync(DragInfo dragInfo, DragDropEffects effects);
-Task DragCancelledAsync(DragInfo dragInfo);
-```
-
-#### IAsyncDropTarget
-```csharp
-Task CanAcceptDropAsync(DropInfo dropInfo);
-Task DragOverAsync(DropInfo dropInfo);
-Task DropAsync(DropInfo dropInfo);
-Task DragLeaveAsync();
-```
-
-### Перечисления
-
-#### DragDropEffects
-```csharp
-[Flags]
-None = 0
-Copy = 1 << 0 // Копирование данных
-Move = 1 << 1 // Перемещение данных
-Link = 1 << 2 // Ссылка на данные
-CopyOrMove = Copy | Move
-All = Copy | Move | Link
-
-// Методы расширения:
-bool CanCopy(this DragDropEffects effects)
-bool CanMove(this DragDropEffects effects)
-bool CanLink(this DragDropEffects effects)
-DragDropEffects GetEffectFromKeys(bool controlKey, bool shiftKey, bool altKey)
-```
-
-#### DropPosition
-```csharp
-Inside // Внутри элемента
-Top // Сверху
-Bottom // Снизу
-Left // Слева
-Right // Справа
-Center // По центру
-```
-
-## 🔮 Планы развития
-
-1. **Интеграция с популярными UI фреймворками** (WinUI, Uno Platform, Avalonia)
-2. **Поддержка жестов** (тач, мультитач)
-3. **Виртуализация** для работы с большими наборами данных
-4. **Продвинутые визуальные эффекты** (анимации, превью)
-5. **Source Generators** для автоматической генерации кода
-6. **Инструменты разработчика** (дебаггер, профилировщик)
\ No newline at end of file
diff --git a/Lattice.Core.DragDrop/Services/DragDropService.cs b/Lattice.Core.DragDrop/Services/DragDropService.cs
deleted file mode 100644
index fab0243..0000000
--- a/Lattice.Core.DragDrop/Services/DragDropService.cs
+++ /dev/null
@@ -1,846 +0,0 @@
-using Lattice.Core.DragDrop.Abstractions;
-using Lattice.Core.DragDrop.Constants;
-using Lattice.Core.DragDrop.Enums;
-using Lattice.Core.DragDrop.Models;
-using Lattice.Core.Geometry;
-
-namespace Lattice.Core.DragDrop.Services;
-
-///
-/// Центральный сервис управления операциями перетаскивания.
-///
-///
-///
-/// является основным компонентом системы drag-and-drop,
-/// который координирует взаимодействие между источниками данных ( )
-/// и целями сброса ( ).
-///
-///
-/// Основные функции сервиса:
-///
-///
-/// - Регистрация и управление целями сброса
-/// - Оркестрация жизненного цикла операций перетаскивания
-/// - Обработка событий мыши и клавиатуры
-/// - Распространение информации между компонентами
-/// - Обеспечение потокобезопасности операций
-///
-///
-/// Сервис поддерживает полностью асинхронную модель работы, уведомления через события
-/// и статистику использования. Все операции защищены от параллельного доступа
-/// и обеспечивают корректную очистку ресурсов.
-///
-///
-/// Для использования сервиса необходимо:
-///
-/// - Зарегистрировать цели сброса с помощью
-/// - Вызывать методы
, ,
-/// в ответ на действия пользователя
-/// - Подписаться на события для отслеживания состояния операций
-///
-///
-///
-public sealed class DragDropService : IDragDropService
-{
- #region Nested Types
-
- ///
- /// Информация о зарегистрированной цели сброса.
- ///
- private sealed class DropTargetInfo : IDisposable
- {
- public required Abstractions.IDropTarget Target { get; init; }
- public required Geometry.Rect Bounds { get; set; }
- public required int Priority { get; init; }
- public required string? Group { get; init; }
- public required string Id { get; init; }
- public DateTime LastAccessTime { get; set; } = DateTime.UtcNow;
- public int UsageCount { get; set; }
-
- public void Dispose()
- {
- if (Target is IDisposable disposable)
- disposable.Dispose();
- }
- }
-
- ///
- /// Контекст текущей операции перетаскивания.
- ///
- private sealed class DragOperationContext : IDisposable
- {
- public Abstractions.IDragSource? Source { get; set; }
- public Models.DragInfo? DragInfo { get; set; }
- public Abstractions.IDropTarget? CurrentDropTarget { get; set; }
- public CancellationTokenSource? CancellationTokenSource { get; set; }
- public DateTime StartTime { get; set; } = DateTime.UtcNow;
- public bool ThresholdExceeded { get; set; }
- public Point LastPosition { get; set; }
-
- public void Dispose()
- {
- DragInfo?.Dispose();
- CancellationTokenSource?.Dispose();
- }
- }
-
- #endregion
-
- #region Fields
-
- private readonly Dictionary _dropTargets = new();
- private readonly ReaderWriterLockSlim _dropTargetsLock = new(LockRecursionPolicy.NoRecursion);
- private readonly object _dragOperationLock = new();
-
- private DragOperationContext? _currentDragOperation;
- private Timer? _cleanupTimer;
- private bool _disposed;
-
- private int _totalDragOperations;
- private int _successfulDrops;
- private int _cancelledOperations;
- private int _errorCount;
- private long _totalOperationTicks;
-
- #endregion
-
- #region Events
-
- private event EventHandler? _dragStarted;
- private event EventHandler? _dragUpdated;
- private event EventHandler? _dropTargetChanged;
- private event EventHandler? _dragCompleted;
- private event EventHandler? _dragCancelled;
- private event EventHandler? _errorOccurred;
-
- #endregion
-
- #region Properties
-
- ///
- public bool IsDragActive => Volatile.Read(ref _currentDragOperation) != null;
-
- ///
- public Models.DragInfo? CurrentDragInfo => _currentDragOperation?.DragInfo;
-
- ///
- public Abstractions.IDropTarget? CurrentDropTarget => _currentDragOperation?.CurrentDropTarget;
-
- ///
- public double DragStartThreshold { get; set; } = DragDropConstants.DefaultDragThreshold;
-
- ///
- public bool EnableAsyncOperations { get; set; } = true;
-
- ///
- public int AsyncOperationTimeout { get; set; } = DragDropConstants.DefaultAsyncTimeout;
-
- ///
- public event EventHandler DragStarted
- {
- add => _dragStarted += value;
- remove => _dragStarted -= value;
- }
-
- ///
- public event EventHandler DragUpdated
- {
- add => _dragUpdated += value;
- remove => _dragUpdated -= value;
- }
-
- ///
- public event EventHandler DropTargetChanged
- {
- add => _dropTargetChanged += value;
- remove => _dropTargetChanged -= value;
- }
-
- ///
- public event EventHandler DragCompleted
- {
- add => _dragCompleted += value;
- remove => _dragCompleted -= value;
- }
-
- ///
- public event EventHandler DragCancelled
- {
- add => _dragCancelled += value;
- remove => _dragCancelled -= value;
- }
-
- ///
- public event EventHandler ErrorOccurred
- {
- add => _errorOccurred += value;
- remove => _errorOccurred -= value;
- }
-
- #endregion
-
- #region Constructor
-
- ///
- /// Инициализирует новый экземпляр класса .
- ///
- ///
- /// Создает сервис с настройками по умолчанию:
- ///
- /// - Порог начала перетаскивания:
пикселей
- /// - Таймаут асинхронных операций:
миллисекунд
- /// - Включены асинхронные операции: true
- ///
- ///
- public DragDropService()
- {
- // Инициализация таймера очистки (каждые 5 минут)
- _cleanupTimer = new Timer(CleanupExpiredTargets, null,
- TimeSpan.FromMinutes(Constants.DragDropConstants.TargetLifetimeMinutes / 2),
- TimeSpan.FromMinutes(Constants.DragDropConstants.TargetLifetimeMinutes / 2));
- }
-
- #endregion
-
- #region Registration Methods
-
- ///
- public string RegisterDropTarget(Abstractions.IDropTarget target, Geometry.Rect bounds, int priority = 0, string? group = null)
- {
- ThrowIfDisposed();
- if (target == null) throw new ArgumentNullException(nameof(target));
-
- var id = Guid.NewGuid().ToString("N");
- var info = new DropTargetInfo
- {
- Target = target,
- Bounds = bounds,
- Priority = priority,
- Group = group,
- Id = id
- };
-
- _dropTargetsLock.EnterWriteLock();
- try
- {
- _dropTargets[id] = info;
- return id;
- }
- finally
- {
- _dropTargetsLock.ExitWriteLock();
- }
- }
-
- ///
- public bool UpdateDropTargetBounds(string id, Geometry.Rect bounds)
- {
- ThrowIfDisposed();
-
- _dropTargetsLock.EnterUpgradeableReadLock();
- try
- {
- if (!_dropTargets.TryGetValue(id, out var info))
- return false;
-
- _dropTargetsLock.EnterWriteLock();
- try
- {
- info.Bounds = bounds;
- info.LastAccessTime = DateTime.UtcNow;
- return true;
- }
- finally
- {
- _dropTargetsLock.ExitWriteLock();
- }
- }
- finally
- {
- _dropTargetsLock.ExitUpgradeableReadLock();
- }
- }
-
- ///
- public bool UnregisterDropTarget(string id)
- {
- ThrowIfDisposed();
-
- _dropTargetsLock.EnterWriteLock();
- try
- {
- if (_dropTargets.Remove(id, out var info))
- {
- info.Dispose();
- return true;
- }
- return false;
- }
- finally
- {
- _dropTargetsLock.ExitWriteLock();
- }
- }
-
- ///
- public void UnregisterDropTargetsInGroup(string group)
- {
- ThrowIfDisposed();
- if (string.IsNullOrEmpty(group)) return;
-
- _dropTargetsLock.EnterWriteLock();
- try
- {
- var idsToRemove = new List();
-
- foreach (var kvp in _dropTargets)
- {
- if (kvp.Value.Group == group)
- {
- idsToRemove.Add(kvp.Key);
- }
- }
-
- foreach (var id in idsToRemove)
- {
- if (_dropTargets.Remove(id, out var info))
- {
- info.Dispose();
- }
- }
- }
- finally
- {
- _dropTargetsLock.ExitWriteLock();
- }
- }
-
- #endregion
-
- #region Async Operations
-
- ///
- public async Task StartDragAsync(IDragSource source, Point startPosition)
- {
- ThrowIfDisposed();
- if (source == null) throw new ArgumentNullException(nameof(source));
-
- lock (_dragOperationLock)
- {
- if (_currentDragOperation != null)
- return false;
- }
-
- try
- {
- Interlocked.Increment(ref _totalDragOperations);
-
- DragInfo? dragInfo;
-
- // Пытаемся начать перетаскивание
- if (EnableAsyncOperations)
- {
- dragInfo = await ExecuteWithTimeoutAsync(
- source.TryStartDragAsync(startPosition),
- "TryStartDragAsync",
- source);
- }
- else
- {
- dragInfo = await source.TryStartDragAsync(startPosition);
- }
-
- if (dragInfo == null)
- return false;
-
- var updatedDragInfo = dragInfo.CloneWithPosition(startPosition);
-
- lock (_dragOperationLock)
- {
- _currentDragOperation = new DragOperationContext
- {
- Source = source,
- DragInfo = updatedDragInfo,
- CancellationTokenSource = new CancellationTokenSource(),
- LastPosition = startPosition
- };
- }
-
- // Вызов события
- _dragStarted?.Invoke(this, new DragStartedEventArgs(updatedDragInfo, startPosition));
- return true;
- }
- catch (Exception ex)
- {
- Interlocked.Increment(ref _errorCount);
- HandleError(ex, "StartDragAsync", source);
- return false;
- }
- }
-
- ///
- public async Task UpdateDragAsync(Point position)
- {
- ThrowIfDisposed();
-
- DragOperationContext? context;
- lock (_dragOperationLock)
- {
- context = _currentDragOperation;
- }
-
- if (context?.DragInfo == null || context.Source == null)
- return;
-
- try
- {
- // Проверка порога начала
- if (!context.ThresholdExceeded && DragStartThreshold > 0)
- {
- var distance = CalculateDistance(context.DragInfo.StartPosition, position);
- if (distance < DragStartThreshold)
- return;
-
- context.ThresholdExceeded = true;
- }
-
- var updatedDragInfo = context.DragInfo.CloneWithPosition(position);
- context.DragInfo.Dispose();
- context.DragInfo = updatedDragInfo;
- context.LastPosition = position;
-
- // Поиск новой цели сброса
- var newDropTarget = await FindDropTargetAsync(position, updatedDragInfo);
-
- // Обработка смены цели
- if (context.CurrentDropTarget != newDropTarget?.Target)
- {
- if (context.CurrentDropTarget != null)
- {
- await ExecuteTargetOperationAsync(
- context.CurrentDropTarget,
- t => t.OnDragLeaveAsync(),
- "OnDragLeave");
- }
-
- context.CurrentDropTarget = newDropTarget?.Target;
-
- if (newDropTarget != null)
- {
- newDropTarget.UsageCount++;
- _dropTargetChanged?.Invoke(this, new DropTargetChangedEventArgs(
- updatedDragInfo, position, newDropTarget.Target, newDropTarget.Bounds));
- }
- }
-
- // Уведомление текущей цели
- if (context.CurrentDropTarget != null)
- {
- var dropInfo = new DropInfo(
- updatedDragInfo.Data,
- position,
- updatedDragInfo.AllowedEffects,
- context.CurrentDropTarget);
-
- await ExecuteTargetOperationAsync(
- context.CurrentDropTarget,
- t => t.OnDragOverAsync(dropInfo),
- "OnDragOver");
- }
-
- _dragUpdated?.Invoke(this, new DragUpdatedEventArgs(updatedDragInfo, position));
- }
- catch (Exception ex)
- {
- Interlocked.Increment(ref _errorCount);
- HandleError(ex, "UpdateDragAsync", context);
- }
- }
-
- ///
- public async Task EndDragAsync(Point position)
- {
- ThrowIfDisposed();
-
- DragOperationContext? context;
- lock (_dragOperationLock)
- {
- context = _currentDragOperation;
- _currentDragOperation = null;
- }
-
- if (context == null || context.DragInfo == null || context.Source == null)
- {
- Reset();
- return DragDropEffects.None;
- }
-
- try
- {
- var effects = DragDropEffects.None;
- var operationTime = DateTime.UtcNow - context.StartTime;
- Interlocked.Add(ref _totalOperationTicks, operationTime.Ticks);
-
- // Выполнение сброса
- if (context.CurrentDropTarget != null)
- {
- var dropInfo = new DropInfo(
- context.DragInfo.Data,
- position,
- context.DragInfo.AllowedEffects,
- context.CurrentDropTarget);
-
- await ExecuteTargetOperationAsync(
- context.CurrentDropTarget,
- t => t.OnDropAsync(dropInfo),
- "OnDrop");
-
- if (dropInfo.Handled)
- {
- effects = dropInfo.SuggestedEffects;
- Interlocked.Increment(ref _successfulDrops);
- }
- }
-
- // Уведомление источника
- await ExecuteSourceOperationAsync(
- context.Source,
- s => s.OnDragCompletedAsync(context.DragInfo, effects),
- "OnDragCompleted");
-
- // Событие завершения
- _dragCompleted?.Invoke(this, new DragCompletedEventArgs(
- context.DragInfo, position, effects));
-
- return effects;
- }
- catch (Exception ex)
- {
- Interlocked.Increment(ref _errorCount);
- HandleError(ex, "EndDragAsync", context);
- return DragDropEffects.None;
- }
- finally
- {
- context.Dispose();
- }
- }
-
- ///
- public async Task CancelDragAsync()
- {
- ThrowIfDisposed();
-
- DragOperationContext? context;
- lock (_dragOperationLock)
- {
- context = _currentDragOperation;
- _currentDragOperation = null;
- }
-
- if (context == null || context.DragInfo == null || context.Source == null)
- {
- Reset();
- return;
- }
-
- try
- {
- context.CancellationTokenSource?.Cancel();
- Interlocked.Increment(ref _cancelledOperations);
-
- await ExecuteSourceOperationAsync(
- context.Source,
- s => s.OnDragCancelledAsync(context.DragInfo),
- "OnDragCancelled");
-
- _dragCancelled?.Invoke(this, new DragCancelledEventArgs(context.DragInfo, context.LastPosition));
- }
- catch (Exception ex)
- {
- Interlocked.Increment(ref _errorCount);
- HandleError(ex, "CancelDragAsync", context);
- }
- finally
- {
- context.Dispose();
- }
- }
-
- #endregion
-
- #region Utility Methods
-
- ///
- public void ClearAllDropTargets()
- {
- ThrowIfDisposed();
-
- _dropTargetsLock.EnterWriteLock();
- try
- {
- foreach (var info in _dropTargets.Values)
- {
- info.Dispose();
- }
- _dropTargets.Clear();
- }
- finally
- {
- _dropTargetsLock.ExitWriteLock();
- }
- }
-
- ///
- public DragDropStats GetStats()
- {
- return new DragDropStats
- {
- TotalDragOperations = _totalDragOperations,
- SuccessfulDrops = _successfulDrops,
- CancelledOperations = _cancelledOperations,
- ErrorCount = _errorCount,
- RegisteredTargets = _dropTargets.Count,
- AverageOperationTime = _totalDragOperations > 0
- ? TimeSpan.FromTicks(_totalOperationTicks / _totalDragOperations)
- : TimeSpan.Zero
- };
- }
-
- #endregion
-
- #region Private Helper Methods
-
- private async Task FindDropTargetAsync(Geometry.Point position, Models.DragInfo dragInfo)
- {
- DropTargetInfo? bestTarget = null;
-
- _dropTargetsLock.EnterReadLock();
- try
- {
- // Фильтруем цели по границам и сортируем по приоритету
- var candidates = _dropTargets.Values
- .Where(info => info.Bounds.Contains(position))
- .OrderByDescending(info => info.Priority)
- .ToList();
-
- foreach (var info in candidates)
- {
- var dropInfo = new Models.DropInfo(
- dragInfo.Data,
- position,
- dragInfo.AllowedEffects,
- info.Target);
-
- bool canAccept = await ExecuteWithTimeoutAsync(
- info.Target.CanAcceptDropAsync(dropInfo),
- "CanAcceptDropAsync",
- info.Target);
-
- if (canAccept)
- {
- info.LastAccessTime = DateTime.UtcNow;
- bestTarget = info;
- break; // Берем первую подходящую с наивысшим приоритетом
- }
- }
- }
- finally
- {
- _dropTargetsLock.ExitReadLock();
- }
-
- return bestTarget;
- }
-
- private async Task ExecuteTargetOperationAsync(
- IDropTarget target,
- Func operation,
- string operationName)
- {
- try
- {
- if (EnableAsyncOperations)
- {
- await ExecuteWithTimeoutAsync(
- operation(target),
- $"{operationName}Async",
- target);
- }
- else
- {
- await operation(target);
- }
- }
- catch (Exception ex)
- {
- Interlocked.Increment(ref _errorCount);
- HandleError(ex, operationName, target);
- }
- }
-
- private async Task ExecuteSourceOperationAsync(
- IDragSource source,
- Func operation,
- string operationName)
- {
- try
- {
- if (EnableAsyncOperations)
- {
- await ExecuteWithTimeoutAsync(
- operation(source),
- $"{operationName}Async",
- source);
- }
- else
- {
- await operation(source);
- }
- }
- catch (Exception ex)
- {
- Interlocked.Increment(ref _errorCount);
- HandleError(ex, operationName, source);
- }
- }
-
- private async Task ExecuteWithTimeoutAsync(Task task, string operationName, object? context = null)
- {
- if (AsyncOperationTimeout <= 0)
- return await task;
-
- var timeoutTask = Task.Delay(AsyncOperationTimeout);
- var completedTask = await Task.WhenAny(task, timeoutTask);
-
- if (completedTask == timeoutTask)
- {
- throw new TimeoutException($"{operationName} timed out after {AsyncOperationTimeout}ms");
- }
-
- return await task;
- }
-
- private async Task ExecuteWithTimeoutAsync(Task task, string operationName, object? context = null)
- {
- if (AsyncOperationTimeout <= 0)
- {
- await task;
- return;
- }
-
- var timeoutTask = Task.Delay(AsyncOperationTimeout);
- var completedTask = await Task.WhenAny(task, timeoutTask);
-
- if (completedTask == timeoutTask)
- {
- throw new TimeoutException($"{operationName} timed out after {AsyncOperationTimeout}ms");
- }
-
- await task;
- }
-
- private double CalculateDistance(Geometry.Point p1, Geometry.Point p2)
- {
- var dx = p2.X - p1.X;
- var dy = p2.Y - p1.Y;
- return Math.Sqrt(dx * dx + dy * dy);
- }
-
- private void Reset()
- {
- lock (_dragOperationLock)
- {
- _currentDragOperation?.Dispose();
- _currentDragOperation = null;
- }
- }
-
- private void CleanupExpiredTargets(object? state)
- {
- var expirationTime = DateTime.UtcNow.AddMinutes(-Constants.DragDropConstants.TargetLifetimeMinutes);
-
- _dropTargetsLock.EnterWriteLock();
- try
- {
- var idsToRemove = new List();
- var currentTarget = _currentDragOperation?.CurrentDropTarget;
-
- foreach (var kvp in _dropTargets)
- {
- if (kvp.Value.LastAccessTime < expirationTime && !ReferenceEquals(kvp.Value.Target, currentTarget))
- {
- idsToRemove.Add(kvp.Key);
- }
- }
-
- foreach (var id in idsToRemove)
- {
- if (_dropTargets.Remove(id, out var info))
- {
- info.Dispose();
- }
- }
- }
- finally
- {
- _dropTargetsLock.ExitWriteLock();
- }
- }
-
- private void HandleError(Exception exception, string operation, object? context = null)
- {
- _errorOccurred?.Invoke(this, new DragDropErrorEventArgs(exception, operation, context));
- }
-
- private void ThrowIfDisposed()
- {
- if (_disposed)
- throw new ObjectDisposedException(GetType().Name);
- }
-
- #endregion
-
- #region IDisposable Implementation
-
- public void Dispose()
- {
- if (_disposed) return;
-
- lock (_dragOperationLock)
- {
- if (_disposed) return;
-
- var timer = Interlocked.Exchange(ref _cleanupTimer, null);
- timer?.Dispose();
-
- _cleanupTimer?.Dispose();
- _cleanupTimer = null;
-
- if (_currentDragOperation != null)
- {
- _currentDragOperation.CancellationTokenSource?.Cancel();
- _currentDragOperation.Dispose();
- _currentDragOperation = null;
- }
-
- ClearAllDropTargets();
-
- _dropTargetsLock.Dispose();
-
- // Очистка событий
- _dragStarted = null;
- _dragUpdated = null;
- _dropTargetChanged = null;
- _dragCompleted = null;
- _dragCancelled = null;
- _errorOccurred = null;
-
- _disposed = true;
- }
-
- GC.SuppressFinalize(this);
- }
-
- #endregion
-}
\ No newline at end of file
diff --git a/Lattice.Core.DragDrop/Services/EventArgs/DragEventArgs.cs b/Lattice.Core.DragDrop/Services/EventArgs/DragEventArgs.cs
deleted file mode 100644
index 1138456..0000000
--- a/Lattice.Core.DragDrop/Services/EventArgs/DragEventArgs.cs
+++ /dev/null
@@ -1,237 +0,0 @@
-using Lattice.Core.DragDrop.Models;
-using Lattice.Core.Geometry;
-
-namespace Lattice.Core.DragDrop.Services;
-
-///
-/// Предоставляет базовые данные для событий перетаскивания.
-///
-///
-/// Этот класс содержит общие свойства, которые используются в большинстве событий
-/// системы перетаскивания. Является базовым классом для специализированных событий.
-///
-public abstract class DragEventArgs : EventArgs
-{
- ///
- /// Получает информацию о текущей операции перетаскивания.
- ///
- ///
- /// Объект , содержащий данные, эффекты и метаданные операции.
- /// Всегда возвращает актуальную информацию на момент возникновения события.
- ///
- public DragInfo DragInfo { get; }
-
- ///
- /// Получает текущую позицию курсора в координатах экрана.
- ///
- ///
- /// Точка, представляющая положение курсора мыши в момент события.
- /// Используется для точного позиционирования и визуальной обратной связи.
- ///
- public Point Position { get; }
-
- ///
- /// Инициализирует новый экземпляр класса .
- ///
- /// Информация о перетаскивании.
- /// Текущая позиция курсора.
- ///
- /// Выбрасывается, когда равен null.
- ///
- protected DragEventArgs(DragInfo dragInfo, Point position)
- {
- DragInfo = dragInfo ?? throw new ArgumentNullException(nameof(dragInfo));
- Position = position;
- }
-}
-
-///
-/// Предоставляет данные для события начала перетаскивания.
-///
-///
-/// Возникает, когда пользователь начинает операцию перетаскивания.
-/// Это первое событие в жизненном цикле операции.
-///
-public sealed class DragStartedEventArgs : DragEventArgs
-{
- ///
- /// Инициализирует новый экземпляр класса .
- ///
- /// Информация о перетаскивании.
- /// Начальная позиция перетаскивания.
- public DragStartedEventArgs(DragInfo dragInfo, Point position)
- : base(dragInfo, position)
- {
- }
-}
-
-///
-/// Предоставляет данные для события обновления позиции перетаскивания.
-///
-///
-/// Возникает при каждом перемещении курсора во время операции перетаскивания.
-/// Может вызываться многократно с высокой частотой, поэтому обработчики
-/// должны быть оптимизированы для производительности.
-///
-public sealed class DragUpdatedEventArgs : DragEventArgs
-{
- ///
- /// Инициализирует новый экземпляр класса .
- ///
- /// Информация о перетаскивании.
- /// Текущая позиция курсора.
- public DragUpdatedEventArgs(DragInfo dragInfo, Point position)
- : base(dragInfo, position)
- {
- }
-}
-
-///
-/// Предоставляет данные для события завершения перетаскивания.
-///
-///
-/// Возникает, когда пользователь завершает операцию перетаскивания
-/// (отпускает кнопку мыши над целью или вне области сброса).
-/// Содержит информацию о примененных эффектах и результатах операции.
-///
-public sealed class DragCompletedEventArgs : DragEventArgs
-{
- ///
- /// Получает эффекты, примененные при завершении операции.
- ///
- ///
- /// Комбинация флагов , указывающая,
- /// как были обработаны данные (копирование, перемещение и т.д.).
- ///
- public Enums.DragDropEffects Effects { get; }
-
- ///
- /// Инициализирует новый экземпляр класса .
- ///
- /// Информация о перетаскивании.
- /// Позиция завершения операции.
- /// Примененные эффекты перетаскивания.
- public DragCompletedEventArgs(DragInfo dragInfo, Point position, Enums.DragDropEffects effects)
- : base(dragInfo, position)
- {
- Effects = effects;
- }
-}
-
-///
-/// Предоставляет данные для события отмены перетаскивания.
-///
-///
-/// Возникает, когда операция перетаскивания была отменена пользователем
-/// (например, нажатием клавиши Escape) или системой (например, при ошибке).
-/// После этого события система возвращается в исходное состояние.
-///
-public sealed class DragCancelledEventArgs : DragEventArgs
-{
- ///
- /// Инициализирует новый экземпляр класса .
- ///
- /// Информация о перетаскивании.
- /// Позиция в момент отмены.
- public DragCancelledEventArgs(DragInfo dragInfo, Point position)
- : base(dragInfo, position)
- {
- }
-}
-
-///
-/// Предоставляет данные для события изменения цели сброса.
-///
-///
-/// Возникает, когда курсор перемещается с одной цели сброса на другую
-/// или покидает область всех целей. Позволяет обновлять визуальную
-/// обратную связь при изменении контекста сброса.
-///
-public sealed class DropTargetChangedEventArgs : DragEventArgs
-{
- ///
- /// Получает новую цель сброса, над которой находится курсор.
- ///
- ///
- /// Объект , готовый принять данные,
- /// или null, если курсор покинул область всех целей.
- ///
- public Abstractions.IDropTarget? Target { get; }
-
- ///
- /// Получает границы новой цели сброса.
- ///
- ///
- /// Прямоугольник, определяющий область цели в координатах экрана.
- /// Может использоваться для точного позиционирования визуальной обратной связи.
- ///
- public Rect TargetBounds { get; }
-
- ///
- /// Инициализирует новый экземпляр класса .
- ///
- /// Информация о перетаскивании.
- /// Текущая позиция курсора.
- /// Новая цель сброса.
- /// Границы цели сброса.
- public DropTargetChangedEventArgs(DragInfo dragInfo, Point position, Abstractions.IDropTarget? target, Rect targetBounds)
- : base(dragInfo, position)
- {
- Target = target;
- TargetBounds = targetBounds;
- }
-}
-
-///
-/// Предоставляет данные для события ошибки в операции перетаскивания.
-///
-///
-/// Возникает при возникновении исключения в любом из компонентов
-/// системы перетаскивания. Позволяет централизованно обрабатывать ошибки
-/// и предоставлять пользователю информацию о проблемах.
-///
-public sealed class DragDropErrorEventArgs : EventArgs
-{
- ///
- /// Получает исключение, вызвавшее ошибку.
- ///
- ///
- /// Объект , содержащий информацию об ошибке.
- /// Может быть любого типа, в зависимости от источника ошибки.
- ///
- public Exception Exception { get; }
-
- ///
- /// Получает название операции, во время которой произошла ошибка.
- ///
- ///
- /// Строка, идентифицирующая операцию (например, "StartDragAsync",
- /// "OnDropAsync", "UpdateDropTargetBounds").
- ///
- public string Operation { get; }
-
- ///
- /// Получает контекст, в котором произошла ошибка.
- ///
- ///
- /// Объект, содержащий дополнительную информацию о контексте ошибки,
- /// или null, если контекст недоступен.
- ///
- public object? Context { get; }
-
- ///
- /// Инициализирует новый экземпляр класса .
- ///
- /// Исключение, вызвавшее ошибку.
- /// Название операции.
- /// Контекст ошибки.
- ///
- /// Выбрасывается, когда или равны null.
- ///
- public DragDropErrorEventArgs(Exception exception, string operation, object? context = null)
- {
- Exception = exception ?? throw new ArgumentNullException(nameof(exception));
- Operation = operation ?? throw new ArgumentNullException(nameof(operation));
- Context = context;
- }
-}
\ No newline at end of file
diff --git a/Lattice.Core.DragDrop/Services/IDragDropService.cs b/Lattice.Core.DragDrop/Services/IDragDropService.cs
deleted file mode 100644
index 2440076..0000000
--- a/Lattice.Core.DragDrop/Services/IDragDropService.cs
+++ /dev/null
@@ -1,240 +0,0 @@
-namespace Lattice.Core.DragDrop.Services;
-
-///
-/// Предоставляет централизованный сервис для управления операциями перетаскивания.
-///
-public interface IDragDropService : IDisposable
-{
- #region Свойства
-
- ///
- /// Получает значение, указывающее, активна ли операция перетаскивания.
- ///
- /// true, если операция перетаскивания активна; в противном случае — false.
- bool IsDragActive { get; }
-
- ///
- /// Получает информацию о текущей операции перетаскивания.
- ///
- ///
- /// Объект , содержащий данные текущей операции,
- /// или null, если операция не активна.
- ///
- Models.DragInfo? CurrentDragInfo { get; }
-
- ///
- /// Получает текущую цель сброса.
- ///
- ///
- /// Объект , над которым находится курсор,
- /// или null, если курсор не над зарегистрированной целью.
- ///
- Abstractions.IDropTarget? CurrentDropTarget { get; }
-
- ///
- /// Получает или задает порог начала перетаскивания в пикселях.
- ///
- ///
- /// Минимальное расстояние, которое должен пройти курсор мыши, чтобы начать операцию перетаскивания.
- /// Значение по умолчанию: .
- ///
- double DragStartThreshold { get; set; }
-
- ///
- /// Получает или задает значение, указывающее, включены ли асинхронные операции.
- ///
- /// true, если асинхронные операции включены; в противном случае — false.
- bool EnableAsyncOperations { get; set; }
-
- ///
- /// Получает или задает максимальное время ожидания асинхронной операции в миллисекундах.
- ///
- ///
- /// Время ожидания в миллисекундах. Значение 0 или меньше означает отсутствие таймаута.
- /// Значение по умолчанию: .
- ///
- int AsyncOperationTimeout { get; set; }
-
- #endregion
-
- #region События
-
- ///
- /// Происходит при начале операции перетаскивания.
- ///
- event EventHandler DragStarted;
-
- ///
- /// Происходит при обновлении позиции перетаскивания.
- ///
- event EventHandler DragUpdated;
-
- ///
- /// Происходит при изменении цели сброса.
- ///
- event EventHandler DropTargetChanged;
-
- ///
- /// Происходит при завершении операции перетаскивания.
- ///
- event EventHandler DragCompleted;
-
- ///
- /// Происходит при отмене операции перетаскивания.
- ///
- event EventHandler DragCancelled;
-
- ///
- /// Происходит при возникновении ошибки в операции перетаскивания.
- ///
- event EventHandler ErrorOccurred;
-
- #endregion
-
- #region Регистрация целей сброса
-
- ///
- /// Регистрирует цель сброса в системе.
- ///
- /// Цель сброса для регистрации.
- /// Границы области цели в координатах экрана.
- /// Приоритет цели (высшие значения обрабатываются первыми).
- /// Имя группы для групповой отмены регистрации.
- /// Уникальный идентификатор зарегистрированной цели.
- /// Выбрасывается, когда равен null.
- /// Выбрасывается, если сервис был удален.
- string RegisterDropTarget(Abstractions.IDropTarget target, Geometry.Rect bounds, int priority = 0, string? group = null);
-
- ///
- /// Обновляет границы цели сброса.
- ///
- /// Идентификатор цели сброса.
- /// Новые границы области цели.
- /// true, если границы успешно обновлены; в противном случае — false.
- /// Выбрасывается, если сервис был удален.
- bool UpdateDropTargetBounds(string id, Geometry.Rect bounds);
-
- ///
- /// Отменяет регистрацию цели сброса.
- ///
- /// Идентификатор цели сброса.
- /// true, если цель успешно удалена; в противном случае — false.
- /// Выбрасывается, если сервис был удален.
- bool UnregisterDropTarget(string id);
-
- ///
- /// Отменяет регистрацию всех целей сброса в указанной группе.
- ///
- /// Имя группы для удаления.
- /// Выбрасывается, если сервис был удален.
- void UnregisterDropTargetsInGroup(string group);
-
- #endregion
-
- #region Асинхронные операции
-
- ///
- /// Начинает операцию перетаскивания из указанной позиции.
- ///
- /// Источник данных для перетаскивания.
- /// Начальная позиция операции в координатах экрана.
- ///
- /// Задача, представляющая асинхронную операцию. Результат содержит true, если операция успешно начата;
- /// в противном случае — false.
- ///
- /// Выбрасывается, когда равен null.
- /// Выбрасывается, если сервис был удален.
- ///
- /// Этот метод следует вызывать в ответ на событие нажатия кнопки мыши или начала жеста перетаскивания.
- ///
- Task StartDragAsync(Abstractions.IDragSource source, Geometry.Point startPosition);
-
- ///
- /// Обновляет позицию текущей операции перетаскивания.
- ///
- /// Новая позиция курсора в координатах экрана.
- /// Задача, представляющая асинхронную операцию.
- /// Выбрасывается, если сервис был удален.
- ///
- /// Этот метод следует вызывать при каждом перемещении мыши во время операции перетаскивания.
- ///
- Task UpdateDragAsync(Geometry.Point position);
-
- ///
- /// Завершает текущую операцию перетаскивания в указанной позиции.
- ///
- /// Позиция завершения операции в координатах экрана.
- ///
- /// Задача, представляющая асинхронную операцию. Результат содержит эффекты, примененные при завершении операции.
- ///
- /// Выбрасывается, если сервис был удален.
- ///
- /// Этот метод следует вызывать при отпускании кнопки мыши или завершении жеста перетаскивания.
- ///
- Task EndDragAsync(Geometry.Point position);
-
- ///
- /// Отменяет текущую операцию перетаскивания.
- ///
- /// Задача, представляющая асинхронную операцию.
- /// Выбрасывается, если сервис был удален.
- ///
- /// Этот метод следует вызывать при отмене операции пользователем (например, нажатием клавиши Escape)
- /// или при возникновении ошибки.
- ///
- Task CancelDragAsync();
-
- #endregion
-
- #region Утилиты
-
- ///
- /// Очищает все зарегистрированные цели сброса.
- ///
- /// Выбрасывается, если сервис был удален.
- void ClearAllDropTargets();
-
- ///
- /// Получает статистику использования системы перетаскивания.
- ///
- /// Объект со статистикой использования.
- DragDropStats GetStats();
-
- #endregion
-}
-
-///
-/// Содержит статистику использования системы перетаскивания.
-///
-public class DragDropStats
-{
- ///
- /// Получает или задает общее количество операций перетаскивания.
- ///
- public int TotalDragOperations { get; set; }
-
- ///
- /// Получает или задает количество успешных сбросов.
- ///
- public int SuccessfulDrops { get; set; }
-
- ///
- /// Получает или задает количество отмененных операций.
- ///
- public int CancelledOperations { get; set; }
-
- ///
- /// Получает или задает количество ошибок.
- ///
- public int ErrorCount { get; set; }
-
- ///
- /// Получает или задает количество зарегистрированных целей сброса.
- ///
- public int RegisteredTargets { get; set; }
-
- ///
- /// Получает или задает среднее время операции перетаскивания.
- ///
- public TimeSpan AverageOperationTime { get; set; }
-}
\ No newline at end of file
diff --git a/Lattice.Example.DragDrop/App.xaml.cs b/Lattice.Example.DragDrop/App.xaml.cs
index 5177bda..a47c723 100644
--- a/Lattice.Example.DragDrop/App.xaml.cs
+++ b/Lattice.Example.DragDrop/App.xaml.cs
@@ -21,7 +21,7 @@ public partial class App : Application
themeManager.RegisterTheme(new FluentThemePack(true)); // Dark тема
// Применяем тему по умолчанию
- themeManager.ApplyTheme("Fluent");
+ themeManager.ApplyTheme("Fluent Dark");
// Создаем главное окно
_window = new MainWindow();
diff --git a/Lattice.Example.DragDrop/Handlers/CustomDropHandler.cs b/Lattice.Example.DragDrop/Handlers/CustomDropHandler.cs
deleted file mode 100644
index 9f2cb3f..0000000
--- a/Lattice.Example.DragDrop/Handlers/CustomDropHandler.cs
+++ /dev/null
@@ -1,46 +0,0 @@
-using Lattice.Core.DragDrop.Abstractions;
-using Lattice.Core.DragDrop.Models;
-using System;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace Lattice.Example.DragDrop;
-
-public class CustomDropHandler : IDropTarget
-{
- private readonly Action _onDrop;
-
- public CustomDropHandler(Action onDrop)
- {
- _onDrop = onDrop;
- }
-
- public async Task CanAcceptDropAsync(DropInfo dropInfo, CancellationToken cancellationToken = default)
- {
- // Принимаем только объекты типа DragDropItem
- return dropInfo.Data is DragDropItem;
- }
-
- public async Task OnDragOverAsync(DropInfo dropInfo, CancellationToken cancellationToken = default)
- {
- if (dropInfo.Data is DragDropItem)
- {
- dropInfo.SuggestedEffects = Core.DragDrop.Enums.DragDropEffects.Copy;
- dropInfo.ShowVisualFeedback = true;
- }
- }
-
- public async Task OnDropAsync(DropInfo dropInfo, CancellationToken cancellationToken = default)
- {
- if (dropInfo.Data is DragDropItem item)
- {
- _onDrop(item);
- dropInfo.MarkAsHandled();
- }
- }
-
- public Task OnDragLeaveAsync(CancellationToken cancellationToken = default)
- {
- return Task.CompletedTask;
- }
-}
\ No newline at end of file
diff --git a/Lattice.Example.DragDrop/Lattice.Example.DragDrop.csproj b/Lattice.Example.DragDrop/Lattice.Example.DragDrop.csproj
index 7027f4b..f13a7f7 100644
--- a/Lattice.Example.DragDrop/Lattice.Example.DragDrop.csproj
+++ b/Lattice.Example.DragDrop/Lattice.Example.DragDrop.csproj
@@ -1,4 +1,4 @@
-
+
WinExe
net8.0-windows10.0.19041.0
@@ -41,11 +41,8 @@
-
-
-
-
-
-
-
-
-
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
-
-
-
+
+
+
-
-
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+ HorizontalAlignment="Center"
+ VerticalAlignment="Center"/>
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/Lattice.Example.DragDrop/MainWindow.xaml.cs b/Lattice.Example.DragDrop/MainWindow.xaml.cs
index 7ab3850..e2d8cd5 100644
--- a/Lattice.Example.DragDrop/MainWindow.xaml.cs
+++ b/Lattice.Example.DragDrop/MainWindow.xaml.cs
@@ -1,421 +1,17 @@
-using Lattice.Core.DragDrop.Services;
-using Lattice.Themes;
-using Lattice.UI.DragDrop.WinUI.Factories;
-using Lattice.UI.DragDrop.WinUI.Services;
+using Lattice.UI.DragDrop.WinUI;
using Microsoft.UI.Xaml;
-using Microsoft.UI.Xaml.Controls;
-using System;
-using System.Collections.ObjectModel;
-using System.Linq;
-using System.Threading.Tasks;
namespace Lattice.Example.DragDrop;
public sealed partial class MainWindow : Window
{
- private readonly ObservableCollection _sourceItems = new();
- private readonly ObservableCollection _droppedItems = new();
-
- private WinUIDragDropManager? _dragDropManager;
- private IDragDropService? _dragDropService;
-
- private int _dragOperationCount = 0;
+ private bool _isInitialized = false;
public MainWindow()
{
-
- // Создаем менеджер перетаскивания
- _dragDropManager = WinUIDragDropFactory.CreateManager(this, manager =>
- {
- // Настраиваем смещение визуального элемента
- manager.DragVisualOffset = new Core.Geometry.Point(-15, -15);
- });
-
- _dragDropService = _dragDropManager.DragDropService;
-
InitializeComponent();
- // Устанавливаем размер окна
- SetWindowSize(1200, 800);
-
- // Инициализируем данные
- InitializeData();
-
- // Инициализируем drag & drop систему
- InitializeDragDrop();
-
- // Устанавливаем контекст данных
- SourceItemsControl.ItemsSource = _sourceItems;
- DroppedItemsControl.ItemsSource = _droppedItems;
-
- Closed += MainWindow_Closing;
- // Обновляем видимость инструкций
- UpdateDropInstructions();
- }
-
- private void MainWindow_Closing(object sender, WindowEventArgs args)
- {
- // Очищаем ресурсы
- _dragDropManager?.Dispose();
- }
-
- private void InitializeData()
- {
- // Создаем тестовые данные для перетаскивания
- _sourceItems.Add(new DragDropItem
- {
- Name = "Document",
- Description = "Microsoft Word document",
- Icon = "Document",
- Type = "File"
- });
-
- _sourceItems.Add(new DragDropItem
- {
- Name = "Image",
- Description = "PNG image file",
- Icon = "Pictures",
- Type = "File"
- });
-
- _sourceItems.Add(new DragDropItem
- {
- Name = "Music",
- Description = "MP3 audio file",
- Icon = "Audio",
- Type = "File"
- });
-
- _sourceItems.Add(new DragDropItem
- {
- Name = "Video",
- Description = "MP4 video file",
- Icon = "Video",
- Type = "File"
- });
-
- _sourceItems.Add(new DragDropItem
- {
- Name = "Contact",
- Description = "Contact information",
- Icon = "Contact",
- Type = "Data"
- });
-
- _sourceItems.Add(new DragDropItem
- {
- Name = "Link",
- Description = "Web URL",
- Icon = "Link",
- Type = "Link"
- });
-
- _sourceItems.Add(new DragDropItem
- {
- Name = "Folder",
- Description = "File system folder",
- Icon = "Folder",
- Type = "Folder"
- });
- }
-
- private void InitializeDragDrop()
- {
- try
- {
-
- // Настраиваем кастомный источник перетаскивания
- _dragDropManager.MakeDragSource(CustomDragSource, new DragDropItem
- {
- Name = "Custom Element",
- Description = "This is a custom drag source example",
- Icon = "Favorite",
- Type = "Custom"
- });
-
- // Настраиваем обработчик для области сброса
- _dragDropManager.MakeDropTarget(DropTargetArea);
-
- // Подписываемся на события перетаскивания
- _dragDropService.DragStarted += OnDragStarted;
- _dragDropService.DragUpdated += OnDragUpdated;
- _dragDropService.DragCompleted += OnDragCompleted;
- _dragDropService.DragCancelled += OnDragCancelled;
- _dragDropService.DropTargetChanged += OnDropTargetChanged;
- _dragDropService.ErrorOccurred += OnDragDropError;
-
- UpdateStats();
-
- StatusText.Text = "Drag & Drop system initialized";
- }
- catch (Exception ex)
- {
- StatusText.Text = $"Failed to initialize Drag & Drop: {ex.Message}";
- ShowErrorDialog("Initialization Error", ex.Message);
- }
- }
-
- private void OnDragStarted(object? sender, Core.DragDrop.Services.DragStartedEventArgs e)
- {
- _dragOperationCount++;
- UpdateStats();
-
- StatusText.Text = $"Dragging started: {GetItemName(e.DragInfo.Data)}";
- }
-
- private void OnDragUpdated(object? sender, Core.DragDrop.Services.DragUpdatedEventArgs e)
- {
- StatusText.Text = $"Dragging: {GetItemName(e.DragInfo.Data)} at ({e.Position.X:F0}, {e.Position.Y:F0})";
- }
-
- private void OnDragCompleted(object? sender, Core.DragDrop.Services.DragCompletedEventArgs e)
- {
- // Проверяем, что элемент сброшен на нашу область
- if (e.DragInfo.Data is DragDropItem item)
- {
- // Добавляем элемент в список сброшенных
- DispatcherQueue.TryEnqueue(() =>
- {
- if (!_droppedItems.Contains(item))
- {
- _droppedItems.Add(item);
-
- // Удаляем из источников, если это не кастомный элемент
- if (_sourceItems.Contains(item) && item.Name != "Custom Element")
- {
- _sourceItems.Remove(item);
- }
-
- UpdateStats();
- UpdateDropInstructions();
- StatusText.Text = $"Item dropped: {item.Name}";
-
- // Показываем уведомление
- ShowDropNotification(item);
- }
- });
- }
- else
- {
- StatusText.Text = "Drag completed";
- }
- }
-
- private async void ShowDropNotification(DragDropItem item)
- {
- var notification = new TeachingTip
- {
- Title = "Item Dropped",
- Subtitle = $"Successfully dropped '{item.Name}'",
- IsOpen = true,
- PreferredPlacement = TeachingTipPlacementMode.TopRight,
- Target = DropTargetArea
- };
-
- // Автоматически скрываем через 3 секунды
- await Task.Delay(3000);
- notification.IsOpen = false;
- }
-
- private void OnDragCancelled(object? sender, Core.DragDrop.Services.DragCancelledEventArgs e)
- {
- StatusText.Text = "Drag cancelled";
- }
-
- private void OnDropTargetChanged(object? sender, Core.DragDrop.Services.DropTargetChangedEventArgs e)
- {
- if (e.Target != null)
- {
- StatusText.Text = "Over drop target";
- }
- else
- {
- StatusText.Text = "Not over drop target";
- }
- }
-
- private async void OnDragDropError(object? sender, Core.DragDrop.Services.DragDropErrorEventArgs e)
- {
- StatusText.Text = $"Error: {e.Exception.Message}";
- await ShowErrorDialog("Drag & Drop Error", e.Exception.Message);
- }
-
- private string GetItemName(object? data)
- {
- if (data is DragDropItem item)
- {
- return item.Name;
- }
- return data?.ToString() ?? "Unknown";
- }
-
- private void UpdateStats()
- {
- DispatcherQueue.TryEnqueue(() =>
- {
- DragStatsText.Text = $"Drag operations: {_dragOperationCount}";
- DropStatsText.Text = $"Dropped items: {_droppedItems.Count}";
- });
- }
-
- private void UpdateDropInstructions()
- {
- DropInstructionsBorder.Visibility = _droppedItems.Count == 0 ?
- Visibility.Visible : Visibility.Collapsed;
- }
-
- private void OnThemeToggleToggled(object sender, RoutedEventArgs e)
- {
- if (sender is ToggleSwitch toggle)
- {
- try
- {
- var themeManager = ThemeManager.Current;
- if (toggle.IsOn)
- {
- themeManager.ApplyTheme("Fluent Dark");
- }
- else
- {
- themeManager.ApplyTheme("Fluent");
- }
-
- StatusText.Text = $"Theme changed to {(toggle.IsOn ? "Dark" : "Light")}";
- }
- catch (Exception ex)
- {
- StatusText.Text = $"Failed to change theme: {ex.Message}";
- }
- }
- }
-
- private async void OnStatsButtonClick(object sender, RoutedEventArgs e)
- {
- if (_dragDropService != null)
- {
- var stats = _dragDropService.GetStats();
-
- var dialog = new ContentDialog
- {
- Title = "Drag & Drop Statistics",
- Content = new ScrollViewer
- {
- Content = new StackPanel
- {
- Spacing = 8,
- Children =
- {
- CreateStatRow("Total Drag Operations:", stats.TotalDragOperations.ToString()),
- CreateStatRow("Successful Drops:", stats.SuccessfulDrops.ToString()),
- CreateStatRow("Cancelled Operations:", stats.CancelledOperations.ToString()),
- CreateStatRow("Errors:", stats.ErrorCount.ToString()),
- CreateStatRow("Registered Targets:", stats.RegisteredTargets.ToString()),
- CreateStatRow("Average Operation Time:",
- stats.AverageOperationTime.TotalMilliseconds.ToString("F0") + "ms")
- }
- }
- },
- PrimaryButtonText = "OK",
- XamlRoot = Content.XamlRoot
- };
-
- await dialog.ShowAsync();
- }
- }
-
- private StackPanel CreateStatRow(string label, string value)
- {
- return new StackPanel
- {
- Orientation = Orientation.Horizontal,
- Spacing = 10,
- Children =
- {
- new TextBlock
- {
- Text = label,
- FontWeight = Microsoft.UI.Text.FontWeights.SemiBold,
- Width = 200
- },
- new TextBlock
- {
- Text = value
- }
- }
- };
- }
-
- private void OnRemoveItemClick(object sender, RoutedEventArgs e)
- {
- if (sender is Button button && button.Tag is DragDropItem item)
- {
- _droppedItems.Remove(item);
- UpdateStats();
- UpdateDropInstructions();
-
- // Добавляем обратно в источники, если это не кастомный элемент
- if (item.Name != "Custom Element" && !_sourceItems.Contains(item))
- {
- _sourceItems.Add(item);
- }
-
- StatusText.Text = $"Item removed: {item.Name}";
- }
- }
-
- private void OnResetPanelsClick(object sender, RoutedEventArgs e)
- {
- // Очищаем все сброшенные элементы
- foreach (var item in _droppedItems.ToList())
- {
- if (item.Name != "Custom Element" && !_sourceItems.Contains(item))
- {
- _sourceItems.Add(item);
- }
- }
- _droppedItems.Clear();
- UpdateStats();
- UpdateDropInstructions();
-
- StatusText.Text = "Panels reset";
- }
-
- private async Task ShowErrorDialog(string title, string message)
- {
- var dialog = new ContentDialog
- {
- Title = title,
- Content = message,
- PrimaryButtonText = "OK",
- XamlRoot = Content.XamlRoot
- };
-
- await dialog.ShowAsync();
- }
-
- // Метод для установки размера окна
- private void SetWindowSize(int width, int height)
- {
- try
- {
- var hwnd = WinRT.Interop.WindowNative.GetWindowHandle(this);
- var windowId = Microsoft.UI.Win32Interop.GetWindowIdFromWindow(hwnd);
- var appWindow = Microsoft.UI.Windowing.AppWindow.GetFromWindowId(windowId);
-
- appWindow.Resize(new Windows.Graphics.SizeInt32(width, height));
-
- // Центрируем окно
- var displayArea = Microsoft.UI.Windowing.DisplayArea.GetFromWindowId(windowId,
- Microsoft.UI.Windowing.DisplayAreaFallback.Primary);
- var centerX = (displayArea.WorkArea.Width - width) / 2;
- var centerY = (displayArea.WorkArea.Height - height) / 2;
-
- appWindow.Move(new Windows.Graphics.PointInt32(centerX, centerY));
- }
- catch (Exception ex)
- {
- // Игнорируем ошибки при установке размера окна
- System.Diagnostics.Debug.WriteLine($"Failed to set window size: {ex.Message}");
- }
+ XamlInitializer.Initialize(this);
+ Title = "✅ Drag & Drop работает! Перетащите элементы →";
}
}
\ No newline at end of file
diff --git a/Lattice.Example.DragDrop/Models/DragDropItem.cs b/Lattice.Example.DragDrop/Models/DragDropItem.cs
deleted file mode 100644
index 01336a8..0000000
--- a/Lattice.Example.DragDrop/Models/DragDropItem.cs
+++ /dev/null
@@ -1,72 +0,0 @@
-using System.ComponentModel;
-
-namespace Lattice.Example.DragDrop;
-
-public class DragDropItem : INotifyPropertyChanged
-{
- private string _name = string.Empty;
- private string _description = string.Empty;
- private string _icon = string.Empty;
- private string _type = string.Empty;
-
- public event PropertyChangedEventHandler? PropertyChanged;
-
- public string Name
- {
- get => _name;
- set
- {
- if (_name != value)
- {
- _name = value;
- OnPropertyChanged(nameof(Name));
- }
- }
- }
-
- public string Description
- {
- get => _description;
- set
- {
- if (_description != value)
- {
- _description = value;
- OnPropertyChanged(nameof(Description));
- }
- }
- }
-
- public string Icon
- {
- get => _icon;
- set
- {
- if (_icon != value)
- {
- _icon = value;
- OnPropertyChanged(nameof(Icon));
- }
- }
- }
-
- public string Type
- {
- get => _type;
- set
- {
- if (_type != value)
- {
- _type = value;
- OnPropertyChanged(nameof(Type));
- }
- }
- }
-
- protected virtual void OnPropertyChanged(string propertyName)
- {
- PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
- }
-
- public override string ToString() => $"{Name} ({Type})";
-}
\ No newline at end of file
diff --git a/Lattice.Example.DragDrop/PriorityToTextConverter.cs b/Lattice.Example.DragDrop/PriorityToTextConverter.cs
deleted file mode 100644
index bd646e2..0000000
--- a/Lattice.Example.DragDrop/PriorityToTextConverter.cs
+++ /dev/null
@@ -1,27 +0,0 @@
-using Microsoft.UI.Xaml.Data;
-using System;
-
-namespace Lattice.Example.DragDrop;
-
-public class PriorityToTextConverter : IValueConverter
-{
- public object Convert(object value, Type targetType, object parameter, string language)
- {
- if (value is int priority)
- {
- return priority switch
- {
- 1 => "🔥 High",
- 2 => "⚠️ Medium",
- 3 => "📋 Low",
- _ => $"Priority {priority}"
- };
- }
- return "Normal";
- }
-
- public object ConvertBack(object value, Type targetType, object parameter, string language)
- {
- throw new NotImplementedException();
- }
-}
\ No newline at end of file
diff --git a/Lattice.Example.DragDrop/WindowExtensions.cs b/Lattice.Example.DragDrop/WindowExtensions.cs
deleted file mode 100644
index 3d75a5d..0000000
--- a/Lattice.Example.DragDrop/WindowExtensions.cs
+++ /dev/null
@@ -1,15 +0,0 @@
-using Microsoft.UI.Xaml;
-
-namespace Lattice.Example.DragDrop;
-
-public static class WindowExtensions
-{
- public static void SetWindowSize(this Window window, int width, int height)
- {
- var hwnd = WinRT.Interop.WindowNative.GetWindowHandle(window);
- var windowId = Microsoft.UI.Win32Interop.GetWindowIdFromWindow(hwnd);
- var appWindow = Microsoft.UI.Windowing.AppWindow.GetFromWindowId(windowId);
-
- appWindow.Resize(new Windows.Graphics.SizeInt32(width, height));
- }
-}
\ No newline at end of file
diff --git a/Lattice.UI.Docking.WinUI/Controls/LatticeDockGroup.cs b/Lattice.UI.Docking.WinUI/Controls/LatticeDockGroup.cs
index e52d340..ad9b45b 100644
--- a/Lattice.UI.Docking.WinUI/Controls/LatticeDockGroup.cs
+++ b/Lattice.UI.Docking.WinUI/Controls/LatticeDockGroup.cs
@@ -2,7 +2,6 @@
using Lattice.Core.Docking.Engine;
using Lattice.Core.Docking.Models;
using Lattice.UI.Docking.Abstractions;
-using Lattice.UI.Docking.Services;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using System;
@@ -12,9 +11,22 @@ using System.Runtime.CompilerServices;
namespace Lattice.UI;
///
-/// Визуальный контрол для отображения группы разделения (сплиттера).
-/// Реализует интерфейс для интеграции с системой докинга.
+/// Визуальный контрол для отображения группы разделения (сплиттера) в системе докинга.
+/// Реализует интерфейс для интеграции с системой докинга
+/// и обеспечивает отображение двух дочерних элементов с разделителем между ними.
///
+///
+///
+/// Контрол отвечает за визуальное представление узла
+/// дерева компоновки, который разделяет доступное пространство между двумя дочерними
+/// элементами. Поддерживает горизонтальное и вертикальное разделение с возможностью
+/// изменения соотношения сторон через перетаскивание разделителя.
+///
+///
+/// Контрол автоматически обновляет свое представление при изменении свойств модели
+/// и обеспечивает двустороннюю привязку данных с объектом .
+///
+///
public sealed class LatticeDockGroup : Control, IDockGroupControl, IDisposable
{
private readonly PropertyChangedEventHandler _modelPropertyChangedHandler;
@@ -24,18 +36,20 @@ public sealed class LatticeDockGroup : Control, IDockGroupControl, IDisposable
private ContentControl? _firstChildControl;
private ContentControl? _secondChildControl;
private LayoutManager? _layoutManager;
- private DockDragDropService? _dragDropService;
private IDockContextManager? _contextManager;
private bool _isSelected;
private bool _isActive;
- private bool _canDrag = true;
- private bool _canDrop = true;
private double _splitRatio = 0.5;
private double _splitterSize = 4.0;
///
/// Инициализирует новый экземпляр класса .
///
+ ///
+ /// Конструктор устанавливает ключ стиля по умолчанию, инициализирует обработчик
+ /// изменений модели и подписывается на событие изменения контекста данных.
+ /// Созданный контрол готов к использованию после применения шаблона.
+ ///
public LatticeDockGroup()
{
this.DefaultStyleKey = typeof(LatticeDockGroup);
@@ -43,7 +57,18 @@ public sealed class LatticeDockGroup : Control, IDockGroupControl, IDisposable
this.DataContextChanged += OnDataContextChanged;
}
- ///
+ ///
+ /// Получает или задает модель данных, связанную с этим контролом.
+ ///
+ ///
+ /// Экземпляр , представляющий узел разделения в дереве компоновки.
+ /// Может быть null, если контрол не связан с моделью.
+ ///
+ ///
+ /// При установке новой модели контрол автоматически подписывается на события
+ /// изменения свойств модели и обновляет свое визуальное представление.
+ /// При удалении модели происходит отписка от событий и очистка ресурсов.
+ ///
public IDockElement? Model
{
get => _model;
@@ -57,7 +82,18 @@ public sealed class LatticeDockGroup : Control, IDockGroupControl, IDisposable
}
}
- ///
+ ///
+ /// Получает или задает менеджер макета, к которому принадлежит этот контрол.
+ ///
+ ///
+ /// Экземпляр , управляющий структурой док-системы.
+ /// Может быть null, если контрол не связан с менеджером макета.
+ ///
+ ///
+ /// Менеджер макета используется для выполнения операций с деревом компоновки,
+ /// таких как перемещение элементов, создание плавающих окон и управление
+ /// автоскрываемыми панелями.
+ ///
public LayoutManager? LayoutManager
{
get => _layoutManager;
@@ -69,19 +105,17 @@ public sealed class LatticeDockGroup : Control, IDockGroupControl, IDisposable
}
}
- ///
- public IDockDragDropService? DragDropService
- {
- get => _dragDropService;
- set
- {
- if (_dragDropService == value) return;
- _dragDropService = value;
- OnPropertyChanged(nameof(DragDropService));
- }
- }
-
- ///
+ ///
+ /// Получает или задает контекстный менеджер для этого контрола.
+ ///
+ ///
+ /// Экземпляр или null, если менеджер не установлен.
+ ///
+ ///
+ /// Контекстный менеджер используется для отображения контекстных меню при щелчке
+ /// правой кнопкой мыши по контролу. Меню содержит команды, доступные для данного
+ /// элемента в текущем контексте.
+ ///
public IDockContextManager? ContextManager
{
get => _contextManager;
@@ -93,7 +127,18 @@ public sealed class LatticeDockGroup : Control, IDockGroupControl, IDisposable
}
}
- ///
+ ///
+ /// Получает или задает признак того, что контрол выбран.
+ ///
+ ///
+ /// true, если контрол выбран; в противном случае false.
+ /// Значение по умолчанию: false.
+ ///
+ ///
+ /// Выделенный контрол обычно визуально отличается от других (например, имеет
+ /// выделенную границу или фон). В каждый момент времени может быть выделен
+ /// только один контрол в пределах контейнера.
+ ///
public bool IsSelected
{
get => _isSelected;
@@ -105,7 +150,17 @@ public sealed class LatticeDockGroup : Control, IDockGroupControl, IDisposable
}
}
- ///
+ ///
+ /// Получает или задает признак того, что контрол активен.
+ ///
+ ///
+ /// true, если контрол активен; в противном случае false.
+ /// Значение по умолчанию: false.
+ ///
+ ///
+ /// Активный контрол получает фокус ввода и может обрабатывать команды клавиатуры.
+ /// Обычно соответствует последнему взаимодействию пользователя с элементом.
+ ///
public bool IsActive
{
get => _isActive;
@@ -117,31 +172,20 @@ public sealed class LatticeDockGroup : Control, IDockGroupControl, IDisposable
}
}
- ///
- public bool CanDrag
- {
- get => _canDrag;
- set
- {
- if (_canDrag == value) return;
- _canDrag = value;
- OnPropertyChanged(nameof(CanDrag));
- }
- }
-
- ///
- public bool CanDrop
- {
- get => _canDrop;
- set
- {
- if (_canDrop == value) return;
- _canDrop = value;
- OnPropertyChanged(nameof(CanDrop));
- }
- }
-
- ///
+ ///
+ /// Получает или задает ориентацию разделения группы.
+ ///
+ ///
+ /// Направление разделения (горизонтальное или вертикальное).
+ ///
+ ///
+ /// Ориентация определяет, как расположены дочерние элементы относительно друг друга:
+ ///
+ /// - элементы расположены слева и справа
+ /// - элементы расположены сверху и снизу
+ ///
+ /// Изменение ориентации приводит к перестройке внутреннего макета контрола.
+ ///
public SplitDirection Orientation
{
get => _model?.Orientation ?? SplitDirection.Horizontal;
@@ -155,7 +199,18 @@ public sealed class LatticeDockGroup : Control, IDockGroupControl, IDisposable
}
}
- ///
+ ///
+ /// Получает или задает соотношение разделения между первым и вторым элементами.
+ ///
+ ///
+ /// Значение от 0.0 до 1.0, где 0.5 означает равное разделение пространства.
+ /// Значение 0.0 отдает все пространство второму элементу, 1.0 - первому элементу.
+ ///
+ ///
+ /// Соотношение разделения определяет пропорции, в которых доступное пространство
+ /// распределяется между дочерними элементами. Изменение этого свойства приводит
+ /// к перестройке внутреннего макета и генерации события .
+ ///
public double SplitRatio
{
get => _splitRatio;
@@ -173,7 +228,17 @@ public sealed class LatticeDockGroup : Control, IDockGroupControl, IDisposable
}
}
- ///
+ ///
+ /// Получает или задает размер разделителя в пикселях.
+ ///
+ ///
+ /// Ширина разделителя в пикселях. Значение по умолчанию: 4.0.
+ ///
+ ///
+ /// Размер разделителя определяет область, доступную для перетаскивания пользователем
+ /// для изменения соотношения разделения. Увеличение размера облегчает взаимодействие,
+ /// но уменьшает полезное пространство для содержимого.
+ ///
public double SplitterSize
{
get => _splitterSize;
@@ -187,19 +252,57 @@ public sealed class LatticeDockGroup : Control, IDockGroupControl, IDisposable
}
}
- ///
+ ///
+ /// Получает контрол для первого дочернего элемента.
+ ///
+ ///
+ /// Контрол, отображающий первый дочерний элемент, или null, если элемент не установлен.
+ ///
+ ///
+ /// Первый дочерний элемент занимает левую область при горизонтальной ориентации
+ /// или верхнюю область при вертикальной ориентации.
+ ///
public IDockControl? FirstChild => _firstChildControl?.Content as IDockControl;
- ///
+ ///
+ /// Получает контрол для второго дочернего элемента.
+ ///
+ ///
+ /// Контрол, отображающий второй дочерний элемент, или null, если элемент не установлен.
+ ///
+ ///
+ /// Второй дочерний элемент занимает правую область при горизонтальной ориентации
+ /// или нижнюю область при вертикальной ориентации.
+ ///
public IDockControl? SecondChild => _secondChildControl?.Content as IDockControl;
- ///
+ ///
+ /// Происходит при изменении соотношения разделения между дочерними элементами.
+ ///
+ ///
+ /// Событие генерируется при изменении свойства ,
+ /// независимо от источника изменения (пользователь, программа или восстановление состояния).
+ /// Содержит информацию о новом соотношении и источнике изменения.
+ ///
public event EventHandler? SplitRatioChanged;
- ///
+ ///
+ /// Происходит при изменении значения свойства.
+ ///
+ ///
+ /// Событие реализует интерфейс и используется
+ /// для уведомления системы привязки данных об изменениях свойств контрола.
+ ///
public event PropertyChangedEventHandler? PropertyChanged;
- ///
+ ///
+ /// Вызывается при применении шаблона контрола.
+ ///
+ ///
+ /// Метод получает ссылки на именованные части шаблона и инициализирует
+ /// внутренние структуры контрола. Вызывает обновление макета для корректного
+ /// отображения дочерних элементов.
+ ///
protected override void OnApplyTemplate()
{
base.OnApplyTemplate();
@@ -211,11 +314,28 @@ public sealed class LatticeDockGroup : Control, IDockGroupControl, IDisposable
UpdateLayoutDefinitions();
}
+ ///
+ /// Обрабатывает изменение контекста данных контрола.
+ ///
+ /// Источник события (контрол).
+ /// Данные о изменении контекста.
+ ///
+ /// Метод автоматически устанавливает модель контрола на основе нового контекста данных,
+ /// если он является экземпляром . Это позволяет использовать
+ /// привязку данных XAML для установки модели контрола.
+ ///
private void OnDataContextChanged(FrameworkElement sender, DataContextChangedEventArgs args)
{
Model = args.NewValue as DockGroup;
}
+ ///
+ /// Присоединяет модель к контролу.
+ ///
+ ///
+ /// Подписывается на события изменения свойств модели, устанавливает контекст данных
+ /// и инициализирует свойства контрола значениями из модели. Вызывает обновление макета.
+ ///
private void AttachModel()
{
if (_model != null)
@@ -229,6 +349,13 @@ public sealed class LatticeDockGroup : Control, IDockGroupControl, IDisposable
}
}
+ ///
+ /// Отсоединяет модель от контрола.
+ ///
+ ///
+ /// Отписывается от событий изменения свойств модели, очищает контекст данных
+ /// и освобождает ресурсы, связанные с предыдущей моделью.
+ ///
private void DetachModel()
{
if (_model != null)
@@ -238,6 +365,16 @@ public sealed class LatticeDockGroup : Control, IDockGroupControl, IDisposable
}
}
+ ///
+ /// Обрабатывает изменения свойств модели.
+ ///
+ /// Источник события (модель).
+ /// Данные об изменении свойства.
+ ///
+ /// Реагирует на изменения ключевых свойств модели (Orientation, SplitRatio)
+ /// и обновляет соответствующие свойства и визуальное представление контрола.
+ /// Также уведомляет систему привязки данных об изменении свойств контрола.
+ ///
private void OnModelPropertyChanged(object? sender, PropertyChangedEventArgs e)
{
switch (e.PropertyName)
@@ -258,6 +395,14 @@ public sealed class LatticeDockGroup : Control, IDockGroupControl, IDisposable
}
}
+ ///
+ /// Обновляет определения макета сетки на основе текущей ориентации и соотношения разделения.
+ ///
+ ///
+ /// Метод перестраивает структуру строк и столбцов сетки в зависимости от ориентации
+ /// разделения и текущего соотношения между дочерними элементами. Обеспечивает
+ /// корректное позиционирование разделителя и дочерних контролов.
+ ///
private void UpdateLayoutDefinitions()
{
if (_rootGrid == null || _model == null) return;
@@ -313,7 +458,15 @@ public sealed class LatticeDockGroup : Control, IDockGroupControl, IDisposable
}
}
- ///
+ ///
+ /// Устанавливает дочерние контролы для отображения.
+ ///
+ /// Контрол для первого элемента.
+ /// Контрол для второго элемента.
+ ///
+ /// Метод назначает контролы для визуального представления дочерних элементов группы.
+ /// После установки контролов обновляет макет для корректного отображения.
+ ///
public void SetChildren(IDockControl? firstChild, IDockControl? secondChild)
{
if (_firstChildControl != null)
@@ -325,13 +478,27 @@ public sealed class LatticeDockGroup : Control, IDockGroupControl, IDisposable
UpdateLayoutDefinitions();
}
- ///
+ ///
+ /// Обновляет внешний вид контрола в соответствии с текущим состоянием модели.
+ ///
+ ///
+ /// Вызывает перестройку макета сетки для синхронизации визуального представления
+ /// с текущими значениями свойств модели (ориентация, соотношение разделения).
+ ///
public void Refresh()
{
UpdateLayoutDefinitions();
}
- ///
+ ///
+ /// Применяет указанную тему к контролу.
+ ///
+ /// Тема для применения.
+ ///
+ /// Обновляет стили и параметры отображения контрола в соответствии с заданной темой.
+ /// В текущей реализации метод является заглушкой и должен быть расширен для
+ /// поддержки динамического изменения тем оформления.
+ ///
public void ApplyTheme(IDockTheme theme)
{
// Применение темы к контролу
@@ -341,7 +508,14 @@ public sealed class LatticeDockGroup : Control, IDockGroupControl, IDisposable
}
}
- ///
+ ///
+ /// Вызывается при изменении состояния модели для обновления UI.
+ ///
+ /// Имя изменившегося свойства модели.
+ ///
+ /// Перенаправляет вызов в обработчик изменений модели, обеспечивая уведомление
+ /// контрола о конкретных изменениях в связанной модели данных.
+ ///
public void OnModelPropertyChanged(string propertyName)
{
if (_model != null)
@@ -350,12 +524,27 @@ public sealed class LatticeDockGroup : Control, IDockGroupControl, IDisposable
}
}
+ ///
+ /// Вызывает событие изменения свойства.
+ ///
+ /// Имя изменившегося свойства.
+ ///
+ /// Используется для уведомления системы привязки данных об изменениях свойств
+ /// контрола. Если имя свойства не указано, автоматически определяется по имени
+ /// вызывающего члена.
+ ///
private void OnPropertyChanged([CallerMemberName] string? propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
- ///
+ ///
+ /// Освобождает ресурсы, используемые этим экземпляром контрола.
+ ///
+ ///
+ /// Выполняет отписку от событий модели, очистку ссылок и освобождение ресурсов.
+ /// После вызова этого метода контрол не должен использоваться.
+ ///
public void Dispose()
{
if (!_disposed)
diff --git a/Lattice.UI.Docking.WinUI/Controls/LatticeDockHost.cs b/Lattice.UI.Docking.WinUI/Controls/LatticeDockHost.cs
index 390b82f..179220a 100644
--- a/Lattice.UI.Docking.WinUI/Controls/LatticeDockHost.cs
+++ b/Lattice.UI.Docking.WinUI/Controls/LatticeDockHost.cs
@@ -1,6 +1,7 @@
using Lattice.Core.Docking.Abstractions;
using Lattice.Core.Docking.Engine;
using Lattice.Core.Docking.Models;
+using Lattice.UI.Docking;
using Lattice.UI.Docking.Abstractions;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
@@ -39,7 +40,6 @@ public sealed class LatticeDockHost : Control, IDockHost, IDisposable
private bool _disposed;
private IDockElement? _model;
private LayoutManager? _layoutManager;
- private IDockDragDropService? _dragDropService;
private IDockContextManager? _contextManager;
private bool _isSelected;
private bool _isActive;
@@ -109,27 +109,6 @@ public sealed class LatticeDockHost : Control, IDockHost, IDisposable
}
}
- ///
- /// Получает или задает сервис перетаскивания, используемый этим контролом.
- ///
- ///
- /// Реализация для обработки операций перетаскивания.
- ///
- ///
- /// Сервис перетаскивания обеспечивает взаимодействие с системой drag-and-drop,
- /// включая визуальную обратную связь и обработку событий.
- ///
- public IDockDragDropService? DragDropService
- {
- get => _dragDropService;
- set
- {
- if (_dragDropService == value) return;
- _dragDropService = value;
- OnPropertyChanged(nameof(DragDropService));
- }
- }
-
///
/// Получает или задает контекстный менеджер для этого контрола.
///
diff --git a/Lattice.UI.Docking.WinUI/Controls/LatticeTabControl.cs b/Lattice.UI.Docking.WinUI/Controls/LatticeTabControl.cs
index 239c75a..8b7c2fa 100644
--- a/Lattice.UI.Docking.WinUI/Controls/LatticeTabControl.cs
+++ b/Lattice.UI.Docking.WinUI/Controls/LatticeTabControl.cs
@@ -1,9 +1,9 @@
using Lattice.Core.Docking.Abstractions;
using Lattice.Core.Docking.Engine;
using Lattice.Core.Docking.Models;
+using Lattice.UI.Docking.Abstractions;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
-using Microsoft.UI.Xaml.Input;
using Microsoft.UI.Xaml.Media;
using System;
using System.Collections.Specialized;
@@ -14,9 +14,20 @@ using System.Runtime.CompilerServices;
namespace Lattice.UI;
///
-/// Кастомный контрол вкладок с поддержкой всех позиций размещения панели вкладок.
+/// Представляет кастомный контрол вкладок с поддержкой всех позиций размещения панели вкладок.
/// Реализует интерфейс для интеграции с системой докинга.
///
+///
+///
+/// Контрол обеспечивает отображение коллекции вкладок с возможностью навигации между ними,
+/// закрытия вкладок и изменения порядка. Поддерживает все четыре позиции размещения панели
+/// вкладок: сверху, снизу, слева и справа.
+///
+///
+/// Контрол автоматически синхронизирует свое состояние с моделью данных
+/// и обеспечивает двустороннюю привязку данных через механизм INotifyPropertyChanged.
+///
+///
public sealed class LatticeTabControl : Control, IDockLeafControl, IDisposable
{
private readonly PropertyChangedEventHandler _modelPropertyChangedHandler;
@@ -26,12 +37,10 @@ public sealed class LatticeTabControl : Control, IDockLeafControl, IDisposable
private ListBox? _tabHeaderList;
private ContentControl? _contentControl;
private LayoutManager? _layoutManager;
- private IDockDragDropService? _dragDropService;
private IDockContextManager? _contextManager;
private bool _isSelected;
private bool _isActive;
- private bool _canDrag = true;
- private bool _canDrop = true;
+ private TabPlacement _tabPlacement = TabPlacement.Top;
private bool _showCloseButtons = true;
private bool _canReorderTabs = true;
@@ -43,11 +52,6 @@ public sealed class LatticeTabControl : Control, IDockLeafControl, IDisposable
this.DefaultStyleKey = typeof(LatticeTabControl);
_modelPropertyChangedHandler = OnModelPropertyChanged;
this.DataContextChanged += OnDataContextChanged;
-
- // Подписываемся на события
- this.PointerPressed += OnPointerPressed;
- this.PointerMoved += OnPointerMoved;
- this.PointerReleased += OnPointerReleased;
}
///
@@ -76,18 +80,6 @@ public sealed class LatticeTabControl : Control, IDockLeafControl, IDisposable
}
}
- ///
- public IDockDragDropService? DragDropService
- {
- get => _dragDropService;
- set
- {
- if (_dragDropService == value) return;
- _dragDropService = value;
- OnPropertyChanged(nameof(DragDropService));
- }
- }
-
///
public IDockContextManager? ContextManager
{
@@ -124,40 +116,17 @@ public sealed class LatticeTabControl : Control, IDockLeafControl, IDisposable
}
}
- ///
- public bool CanDrag
- {
- get => _canDrag;
- set
- {
- if (_canDrag == value) return;
- _canDrag = value;
- OnPropertyChanged(nameof(CanDrag));
- }
- }
-
- ///
- public bool CanDrop
- {
- get => _canDrop;
- set
- {
- if (_canDrop == value) return;
- _canDrop = value;
- OnPropertyChanged(nameof(CanDrop));
- }
- }
-
///
public TabPlacement TabPlacement
{
- get => _model?.TabPlacement ?? TabPlacement.Top;
+ get => _tabPlacement;
set
{
- if (_model != null && _model.TabPlacement != value)
+ if (_tabPlacement != value)
{
- _model.TabPlacement = value;
+ _tabPlacement = value;
UpdateTabPlacement();
+ OnPropertyChanged(nameof(TabPlacement));
}
}
}
@@ -168,10 +137,12 @@ public sealed class LatticeTabControl : Control, IDockLeafControl, IDisposable
get => _showCloseButtons;
set
{
- if (_showCloseButtons == value) return;
- _showCloseButtons = value;
- OnPropertyChanged(nameof(ShowCloseButtons));
- UpdateTabHeaders();
+ if (_showCloseButtons != value)
+ {
+ _showCloseButtons = value;
+ OnPropertyChanged(nameof(ShowCloseButtons));
+ UpdateTabHeaders();
+ }
}
}
@@ -181,9 +152,11 @@ public sealed class LatticeTabControl : Control, IDockLeafControl, IDisposable
get => _canReorderTabs;
set
{
- if (_canReorderTabs == value) return;
- _canReorderTabs = value;
- OnPropertyChanged(nameof(CanReorderTabs));
+ if (_canReorderTabs != value)
+ {
+ _canReorderTabs = value;
+ OnPropertyChanged(nameof(CanReorderTabs));
+ }
}
}
@@ -230,30 +203,38 @@ public sealed class LatticeTabControl : Control, IDockLeafControl, IDisposable
UpdateTabHeaders();
}
+ ///
+ /// Обрабатывает изменение контекста данных контрола.
+ ///
private void OnDataContextChanged(FrameworkElement sender, DataContextChangedEventArgs args)
{
Model = args.NewValue as DockLeaf;
}
+ ///
+ /// Присоединяет модель данных к контролу.
+ ///
private void AttachModel()
{
if (_model != null)
{
_model.PropertyChanged += _modelPropertyChangedHandler;
- // Подписываемся на изменения коллекции
if (_model.Children is INotifyCollectionChanged notifyCollection)
{
notifyCollection.CollectionChanged += OnChildrenCollectionChanged;
}
- // Устанавливаем DataContext для привязки в XAML
this.DataContext = _model;
+ _tabPlacement = _model.TabPlacement;
UpdateTabHeaders();
UpdateTabPlacement();
}
}
+ ///
+ /// Отсоединяет модель данных от контрола.
+ ///
private void DetachModel()
{
if (_model != null)
@@ -269,11 +250,15 @@ public sealed class LatticeTabControl : Control, IDockLeafControl, IDisposable
}
}
+ ///
+ /// Обрабатывает изменения свойств модели данных.
+ ///
private void OnModelPropertyChanged(object? sender, PropertyChangedEventArgs e)
{
switch (e.PropertyName)
{
case nameof(DockLeaf.TabPlacement):
+ _tabPlacement = _model?.TabPlacement ?? TabPlacement.Top;
OnPropertyChanged(nameof(TabPlacement));
UpdateTabPlacement();
break;
@@ -289,20 +274,24 @@ public sealed class LatticeTabControl : Control, IDockLeafControl, IDisposable
}
}
+ ///
+ /// Обрабатывает изменения коллекции вкладок.
+ ///
private void OnChildrenCollectionChanged(object? sender, NotifyCollectionChangedEventArgs e)
{
UpdateTabHeaders();
}
+ ///
+ /// Обновляет положение панели вкладок.
+ ///
private void UpdateTabPlacement()
{
if (_rootGrid == null || _model == null) return;
- // Очищаем все определения
_rootGrid.RowDefinitions.Clear();
_rootGrid.ColumnDefinitions.Clear();
- // Настраиваем Grid в зависимости от позиции вкладок
switch (_model.TabPlacement)
{
case TabPlacement.Top:
@@ -330,21 +319,24 @@ public sealed class LatticeTabControl : Control, IDockLeafControl, IDisposable
break;
}
- // Обновляем позиции элементов
UpdateElementPositions();
}
+ ///
+ /// Обновляет ориентацию списка заголовков вкладок.
+ ///
+ /// Новая ориентация списка.
private void UpdateHeaderListOrientation(Orientation orientation)
{
- if (_tabHeaderList != null && _tabHeaderList.ItemsPanelRoot is ItemsPanelTemplate panelTemplate)
+ if (_tabHeaderList?.ItemsPanelRoot is StackPanel stackPanel)
{
- if (panelTemplate.LoadContent() is StackPanel stackPanel)
- {
- stackPanel.Orientation = orientation;
- }
+ stackPanel.Orientation = orientation;
}
}
+ ///
+ /// Обновляет позиции элементов в сетке.
+ ///
private void UpdateElementPositions()
{
if (_rootGrid == null || _tabHeaderList == null || _contentControl == null) return;
@@ -381,14 +373,15 @@ public sealed class LatticeTabControl : Control, IDockLeafControl, IDisposable
}
}
+ ///
+ /// Обновляет заголовки вкладок.
+ ///
private void UpdateTabHeaders()
{
if (_tabHeaderList == null || _model == null) return;
- // Очищаем текущие элементы
_tabHeaderList.Items.Clear();
- // Добавляем новые элементы
foreach (var content in _model.Children)
{
var item = CreateTabHeaderItem(content);
@@ -398,6 +391,11 @@ public sealed class LatticeTabControl : Control, IDockLeafControl, IDisposable
UpdateSelectedTab();
}
+ ///
+ /// Создает элемент заголовка вкладки.
+ ///
+ /// Содержимое вкладки.
+ /// Созданный элемент заголовка.
private ListBoxItem CreateTabHeaderItem(IDockContent content)
{
var item = new ListBoxItem
@@ -408,7 +406,6 @@ public sealed class LatticeTabControl : Control, IDockLeafControl, IDisposable
VerticalContentAlignment = VerticalAlignment.Stretch
};
- // Обработка клика для выбора вкладки
item.PointerPressed += (sender, e) =>
{
if (e.GetCurrentPoint(item).Properties.IsLeftButtonPressed)
@@ -418,26 +415,20 @@ public sealed class LatticeTabControl : Control, IDockLeafControl, IDisposable
}
};
- // Обработка клика на кнопке закрытия
- if (_showCloseButtons && content.CanClose)
- {
- // Добавляем контекстное меню
- item.ContextRequested += (sender, e) =>
- {
- ShowTabContextMenu(item, content, e);
- };
- }
-
return item;
}
+ ///
+ /// Создает содержимое заголовка вкладки.
+ ///
+ /// Содержимое вкладки.
+ /// Созданное содержимое заголовка.
private UIElement CreateTabHeaderContent(IDockContent content)
{
var grid = new Grid();
grid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(1, GridUnitType.Star) });
grid.ColumnDefinitions.Add(new ColumnDefinition { Width = GridLength.Auto });
- // Заголовок вкладки
var textBlock = new TextBlock
{
Text = content.Title,
@@ -447,7 +438,6 @@ public sealed class LatticeTabControl : Control, IDockLeafControl, IDisposable
Grid.SetColumn(textBlock, 0);
grid.Children.Add(textBlock);
- // Кнопка закрытия (если разрешено)
if (_showCloseButtons && content.CanClose)
{
var closeButton = new Button
@@ -475,11 +465,13 @@ public sealed class LatticeTabControl : Control, IDockLeafControl, IDisposable
return grid;
}
+ ///
+ /// Обновляет выбранную вкладку.
+ ///
private void UpdateSelectedTab()
{
if (_tabHeaderList == null || _model == null) return;
- // Находим элемент, соответствующий активному контенту
foreach (var item in _tabHeaderList.Items)
{
if (item is ListBoxItem listBoxItem && listBoxItem.Tag is IDockContent content)
@@ -488,13 +480,15 @@ public sealed class LatticeTabControl : Control, IDockLeafControl, IDisposable
}
}
- // Обновляем контент
if (_contentControl != null)
{
_contentControl.Content = _model.ActiveContent?.View;
}
}
+ ///
+ /// Обрабатывает изменение выбора вкладки.
+ ///
private void OnTabSelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (_tabHeaderList?.SelectedItem is ListBoxItem selectedItem &&
@@ -511,66 +505,6 @@ public sealed class LatticeTabControl : Control, IDockLeafControl, IDisposable
}
}
- // Drag-and-Drop для переупорядочивания вкладок
- private ListBoxItem? _draggedItem;
- private Point _dragStartPoint;
-
- private void OnPointerPressed(object sender, PointerRoutedEventArgs e)
- {
- if (!_canReorderTabs) return;
-
- var pointerPoint = e.GetCurrentPoint(this);
- _dragStartPoint = new Point(pointerPoint.Position.X, pointerPoint.Position.Y);
-
- // Находим элемент под курсором
- var element = VisualTreeHelper.FindElementsInHostCoordinates(
- pointerPoint.Position, this).FirstOrDefault();
-
- if (element is ListBoxItem listBoxItem)
- {
- _draggedItem = listBoxItem;
- }
- }
-
- private void OnPointerMoved(object sender, PointerRoutedEventArgs e)
- {
- if (_draggedItem == null || !_canReorderTabs) return;
-
- var pointerPoint = e.GetCurrentPoint(this);
- var currentPoint = new Point(pointerPoint.Position.X, pointerPoint.Position.Y);
-
- // Проверяем, достаточно ли переместили для начала перетаскивания
- var distance = Math.Sqrt(
- Math.Pow(currentPoint.X - _dragStartPoint.X, 2) +
- Math.Pow(currentPoint.Y - _dragStartPoint.Y, 2));
-
- if (distance > 10 && _draggedItem.Tag is IDockContent content)
- {
- // Начинаем операцию перетаскивания
- StartTabDrag(_draggedItem, content);
- _draggedItem = null;
- }
- }
-
- private void OnPointerReleased(object sender, PointerRoutedEventArgs e)
- {
- _draggedItem = null;
- }
-
- private void StartTabDrag(ListBoxItem item, IDockContent content)
- {
- // TODO: Реализовать перетаскивание вкладок
- // Для этого нужно использовать IDockDragDropService
- }
-
- private void ShowTabContextMenu(ListBoxItem item, IDockContent content, ContextRequestedEventArgs e)
- {
- if (_contextManager == null) return;
-
- var position = e.GetPosition(this);
- _contextManager.ShowContextMenu(this, position.X, position.Y);
- }
-
///
public void AddContent(IDockContent content)
{
@@ -643,10 +577,9 @@ public sealed class LatticeTabControl : Control, IDockLeafControl, IDisposable
///
public void ApplyTheme(IDockTheme theme)
{
- // Применение темы к элементу
if (theme != null)
{
- // TODO: Применить тему к стилям контрола
+ // TODO: Реализовать применение темы к стилям контрола
}
}
@@ -659,6 +592,9 @@ public sealed class LatticeTabControl : Control, IDockLeafControl, IDisposable
}
}
+ ///
+ /// Вызывает событие изменения свойства.
+ ///
private void OnPropertyChanged([CallerMemberName] string? propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
@@ -676,11 +612,6 @@ public sealed class LatticeTabControl : Control, IDockLeafControl, IDisposable
_tabHeaderList.SelectionChanged -= OnTabSelectionChanged;
}
- // Отписываемся от событий указателя
- this.PointerPressed -= OnPointerPressed;
- this.PointerMoved -= OnPointerMoved;
- this.PointerReleased -= OnPointerReleased;
-
_disposed = true;
GC.SuppressFinalize(this);
}
diff --git a/Lattice.UI.Docking.WinUI/Factories/WinUIDockControlFactory.cs b/Lattice.UI.Docking.WinUI/Factories/WinUIDockControlFactory.cs
index e26370b..ece13a2 100644
--- a/Lattice.UI.Docking.WinUI/Factories/WinUIDockControlFactory.cs
+++ b/Lattice.UI.Docking.WinUI/Factories/WinUIDockControlFactory.cs
@@ -11,6 +11,17 @@ namespace Lattice.UI.Docking.WinUI.Factories;
/// Фабрика контролов для платформы WinUI.
/// Создает UI-элементы для отображения компонентов системы докинга.
///
+///
+///
+/// Фабрика реализует паттерн "Абстрактная фабрика", предоставляя единый интерфейс
+/// для создания всех типов контролов док-системы. Это позволяет абстрагировать
+/// конкретную UI-платформу (WinUI) от бизнес-логики системы.
+///
+///
+/// Все создаваемые контролы автоматически настраиваются: устанавливаются связи
+/// с менеджером макета, контекстным менеджером и применяется текущая тема оформления.
+///
+///
public sealed class WinUIDockControlFactory : DockControlFactoryBase, IDockControlFactory
{
private readonly IDockTheme _theme;
@@ -18,13 +29,33 @@ public sealed class WinUIDockControlFactory : DockControlFactoryBase, IDockContr
///
/// Инициализирует новый экземпляр фабрики WinUI.
///
- /// Тема оформления.
+ /// Тема оформления для применения к создаваемым контролам.
+ ///
+ /// Выбрасывается, если равен null.
+ ///
+ ///
+ /// Конструктор создает фабрику с заданной темой оформления. Все контролы,
+ /// созданные этой фабрикой, будут автоматически применять указанную тему.
+ ///
public WinUIDockControlFactory(IDockTheme theme)
{
_theme = theme ?? throw new ArgumentNullException(nameof(theme));
}
- ///
+ ///
+ /// Создает контрол для группы разделения.
+ ///
+ /// Модель группы разделения.
+ ///
+ /// Созданный контрол группы.
+ ///
+ ///
+ /// Выбрасывается, если равен null.
+ ///
+ ///
+ /// Создает экземпляр , настраивает его связи
+ /// с моделью и другими сервисами, применяет текущую тему оформления.
+ ///
public override IDockGroupControl CreateGroupControl(DockGroup group)
{
var control = new LatticeDockGroup();
@@ -33,7 +64,21 @@ public sealed class WinUIDockControlFactory : DockControlFactoryBase, IDockContr
return control;
}
- ///
+ ///
+ /// Создает контрол для контейнера вкладок.
+ ///
+ /// Модель контейнера вкладок.
+ ///
+ /// Созданный контрол листа.
+ ///
+ ///
+ /// Выбрасывается, если равен null.
+ ///
+ ///
+ /// Создает экземпляр , настраивает его связи
+ /// с моделью и другими сервисами, применяет текущую тему оформления.
+ /// Контрол поддерживает все положения панели вкладок и операции с вкладками.
+ ///
public override IDockLeafControl CreateLeafControl(DockLeaf leaf)
{
var control = new LatticeTabControl();
@@ -42,21 +87,58 @@ public sealed class WinUIDockControlFactory : DockControlFactoryBase, IDockContr
return control;
}
- ///
+ ///
+ /// Создает контрол для плавающего окна.
+ ///
+ /// Модель плавающего окна.
+ ///
+ /// Созданный контрол окна.
+ ///
+ ///
+ /// Выбрасывается, если равен null.
+ ///
+ ///
+ /// В текущей реализации метод не реализован. Плавающие окна требуют
+ /// дополнительной интеграции с оконной системой платформы.
+ ///
public override IFloatingWindowControl CreateFloatingWindowControl(DockWindow window)
{
// TODO: Реализовать создание плавающего окна
throw new NotImplementedException();
}
- ///
+ ///
+ /// Создает контрол для автоскрываемой панели.
+ ///
+ /// Модель автоскрываемой панели.
+ ///
+ /// Созданный контрол панели.
+ ///
+ ///
+ /// Выбрасывается, если равен null.
+ ///
+ ///
+ /// В текущей реализации метод не реализован. Автоскрываемые панели требуют
+ /// сложной логики анимации и взаимодействия с краями окна.
+ ///
public override IAutoHidePanelControl CreateAutoHidePanelControl(AutoHidePanel panel)
{
// TODO: Реализовать создание автоскрываемой панели
throw new NotImplementedException();
}
- ///
+ ///
+ /// Создает контрол для разделителя.
+ ///
+ /// Ориентация разделителя.
+ ///
+ /// Созданный контрол разделителя.
+ ///
+ ///
+ /// Создает экземпляр , настраивает его ориентацию
+ /// и применяет текущую тему оформления. Разделитель поддерживает перетаскивание
+ /// для изменения соотношения размеров между соседними областями.
+ ///
public override IDockSplitterControl CreateSplitterControl(SplitDirection orientation)
{
var control = new LatticeSplitter
@@ -71,6 +153,14 @@ public sealed class WinUIDockControlFactory : DockControlFactoryBase, IDockContr
///
/// Создает хост для размещения системы докинга.
///
+ ///
+ /// Созданный док-хост.
+ ///
+ ///
+ /// Создает корневой контейнер для всей системы докинга - экземпляр .
+ /// Хост управляет всем макетом приложения, включая основное дерево компоновки,
+ /// плавающие окна и автоскрываемые панели.
+ ///
public IDockHost CreateDockHost()
{
var host = new LatticeDockHost();
@@ -79,13 +169,22 @@ public sealed class WinUIDockControlFactory : DockControlFactoryBase, IDockContr
return host;
}
+ ///
+ /// Настраивает созданный контрол.
+ ///
+ /// Контрол для настройки.
+ /// Модель данных для контрола (опционально).
+ ///
+ /// Устанавливает основные связи контрола: модель данных, менеджер макета,
+ /// контекстный менеджер. Также настраивает привязку данных через DataContext.
+ /// Этот метод вызывается для всех создаваемых контролов.
+ ///
private void ConfigureControl(IDockControl control, IDockElement? model = null)
{
if (control == null) return;
control.Model = model;
control.LayoutManager = LatticeUIFramework.LayoutManager;
- control.DragDropService = LatticeUIFramework.DragDropService;
control.ContextManager = LatticeUIFramework.ContextManager;
if (control is FrameworkElement frameworkElement && model != null)
diff --git a/Lattice.UI.Docking.WinUI/Services/WinUIDockUIService.cs b/Lattice.UI.Docking.WinUI/Services/WinUIDockUIService.cs
index 45a31ac..4d4b3ac 100644
--- a/Lattice.UI.Docking.WinUI/Services/WinUIDockUIService.cs
+++ b/Lattice.UI.Docking.WinUI/Services/WinUIDockUIService.cs
@@ -8,11 +8,43 @@ using System.Threading.Tasks;
namespace Lattice.UI.Docking.WinUI.Services;
///
-/// Реализация UI-сервиса для WinUI.
+/// Реализация UI-сервиса для платформы WinUI.
+/// Инкапсулирует платформенно-зависимые операции, такие как создание окон,
+/// показ диалогов и синхронизация с UI-потоком.
///
-public sealed class WinUIDockUIService : DockUIServiceBase
+///
+///
+/// предоставляет конкретные реализации методов
+/// для платформы WinUI. Это позволяет основной
+/// бизнес-логике док-системы оставаться независимой от конкретной UI-платформы.
+///
+///
+/// Сервис использует API WinUI для создания окон, показа ContentDialog и
+/// управления диспетчером потока пользовательского интерфейса.
+///
+///
+public sealed class WinUIDockUIService : DockUIServiceBase, IDockUIService
{
- ///
+ ///
+ /// Создает главное окно приложения для размещения док-хоста.
+ ///
+ ///
+ /// Экземпляр , который будет содержаться в окне.
+ ///
+ ///
+ /// Объект окна WinUI, который можно отобразить и управлять им.
+ ///
+ ///
+ /// Выбрасывается, если равен null.
+ ///
+ ///
+ /// Выбрасывается, если не является элементом WinUI.
+ ///
+ ///
+ /// Создает окно WinUI с заголовком "Lattice IDE", устанавливает указанный хост
+ /// в качестве содержимого и регистрирует окно в системе отслеживания окон.
+ /// Окно создается с настройками по умолчанию для IDE-подобных приложений.
+ ///
public override object CreateMainWindow(IDockHost host)
{
if (host is not FrameworkElement hostElement)
@@ -28,7 +60,25 @@ public sealed class WinUIDockUIService : DockUIServiceBase
return window;
}
- ///
+ ///
+ /// Отображает модальное диалоговое окно с указанным содержимым.
+ ///
+ /// Заголовок диалогового окна.
+ /// Содержимое диалогового окна.
+ ///
+ /// Nullable boolean значение, указывающее результат диалога:
+ /// true - пользователь подтвердил действие,
+ /// false - пользователь отменил действие,
+ /// null - диалог был закрыт без выбора.
+ ///
+ ///
+ /// Выбрасывается, если или равны null.
+ ///
+ ///
+ /// Создает и показывает ContentDialog с кнопками OK и Cancel.
+ /// Блокирует взаимодействие с родительским окном до закрытия диалога.
+ /// Использует XamlRoot активного окна для корректного отображения.
+ ///
public override bool? ShowDialog(string title, object content)
{
if (content is not FrameworkElement contentElement)
@@ -53,7 +103,19 @@ public sealed class WinUIDockUIService : DockUIServiceBase
};
}
- ///
+ ///
+ /// Отображает информационное сообщение с кнопкой OK.
+ ///
+ /// Текст сообщения.
+ /// Заголовок окна сообщения.
+ ///
+ /// Выбрасывается, если или равны null.
+ ///
+ ///
+ /// Создает ContentDialog с текстом сообщения и одной кнопкой OK.
+ /// Используется для информирования пользователя о результате операции
+ /// или отображения некритичных ошибок.
+ ///
public override void ShowMessage(string message, string caption)
{
var dialog = new ContentDialog
@@ -67,7 +129,22 @@ public sealed class WinUIDockUIService : DockUIServiceBase
dialog.ShowAsync();
}
- ///
+ ///
+ /// Отображает диалог подтверждения с кнопками Yes/No.
+ ///
+ /// Текст вопроса.
+ /// Заголовок окна подтверждения.
+ ///
+ /// true, если пользователь выбрал "Yes"; false, если пользователь выбрал "No".
+ ///
+ ///
+ /// Выбрасывается, если или равны null.
+ ///
+ ///
+ /// Создает ContentDialog с кнопками Yes и No. Используется для получения
+ /// подтверждения пользователя перед выполнением критических операций,
+ /// таких как закрытие вкладок с несохраненными данными или сброс настроек.
+ ///
public override bool Confirm(string message, string caption)
{
var dialog = new ContentDialog
@@ -83,7 +160,22 @@ public sealed class WinUIDockUIService : DockUIServiceBase
return result == ContentDialogResult.Primary;
}
- ///
+ ///
+ /// Отображает диалог ввода текста.
+ ///
+ /// Текст подсказки для пользователя.
+ /// Значение по умолчанию для поля ввода.
+ ///
+ /// Введенный пользователем текст или null, если диалог был отменен.
+ ///
+ ///
+ /// Выбрасывается, если равен null.
+ ///
+ ///
+ /// Создает ContentDialog с однострочным полем ввода TextBox.
+ /// Используется для получения текстового ввода от пользователя, такого как
+ /// имена файлов, названия документов или параметры конфигурации.
+ ///
public override string? Prompt(string prompt, string? defaultValue = null)
{
var textBox = new TextBox
@@ -106,7 +198,19 @@ public sealed class WinUIDockUIService : DockUIServiceBase
return result == ContentDialogResult.Primary ? textBox.Text : null;
}
- ///
+ ///
+ /// Выполняет указанное действие в UI-потоке.
+ ///
+ /// Действие для выполнения.
+ ///
+ /// Выбрасывается, если равен null.
+ ///
+ ///
+ /// Гарантирует, что действие будет выполнено в потоке, связанном с
+ /// пользовательским интерфейсом. Если текущий поток уже является UI-потоком,
+ /// действие выполняется немедленно. В противном случае действие ставится
+ /// в очередь диспетчера WinUI.
+ ///
public override void InvokeOnUIThread(Action action)
{
if (action == null) return;
@@ -122,7 +226,21 @@ public sealed class WinUIDockUIService : DockUIServiceBase
}
}
- ///
+ ///
+ /// Выполняет указанную асинхронную функцию в UI-потоке.
+ ///
+ /// Асинхронная функция для выполнения.
+ ///
+ /// Задача, представляющая асинхронную операцию.
+ ///
+ ///
+ /// Выбрасывается, если равен null.
+ ///
+ ///
+ /// Гарантирует, что асинхронная функция будет выполнена в UI-потоке.
+ /// Используется для операций, которые требуют доступа к UI-элементам
+ /// или выполняют асинхронные вызовы с обновлением интерфейса.
+ ///
public override async Task InvokeOnUIThreadAsync(Func action)
{
if (action == null) return;
@@ -151,6 +269,17 @@ public sealed class WinUIDockUIService : DockUIServiceBase
}
}
+ ///
+ /// Получает XamlRoot активного окна приложения.
+ ///
+ ///
+ /// XamlRoot активного окна или null, если нет активных окон.
+ ///
+ ///
+ /// Используется для корректного отображения диалоговых окон в контексте
+ /// текущего окна приложения. Перебирает все зарегистрированные окна
+ /// и возвращает XamlRoot первого найденного.
+ ///
private XamlRoot? GetActiveXamlRoot()
{
// Получаем XamlRoot из активного окна
diff --git a/Lattice.UI.Docking/Abstractions/IAutoHidePanelControl.cs b/Lattice.UI.Docking/Abstractions/IAutoHidePanelControl.cs
index 74f95fd..69a2a15 100644
--- a/Lattice.UI.Docking/Abstractions/IAutoHidePanelControl.cs
+++ b/Lattice.UI.Docking/Abstractions/IAutoHidePanelControl.cs
@@ -77,21 +77,23 @@ public interface IAutoHidePanelControl : IDockControl
///
/// Задает фиксированное состояние панели.
///
- /// true, чтобы зафиксировать панель; false, чтобы разрешить автоскрытие.
+ ///
+ /// true, чтобы зафиксировать панель; false, чтобы разрешить автоскрытие.
+ ///
void SetPinned(bool pinned);
///
- /// Событие, возникающее при изменении видимости панели.
+ /// Происходит при изменении видимости панели.
///
event EventHandler VisibilityChanged;
///
- /// Событие, возникающее при наведении курсора на панель.
+ /// Происходит при наведении курсора на панель.
///
event EventHandler MouseEntered;
///
- /// Событие, возникающее при уходе курсора с панели.
+ /// Происходит при уходе курсора с панели.
///
event EventHandler MouseLeft;
}
\ No newline at end of file
diff --git a/Lattice.UI.Docking/Abstractions/IDockCommand.cs b/Lattice.UI.Docking/Abstractions/IDockCommand.cs
index e6f19dc..6eff446 100644
--- a/Lattice.UI.Docking/Abstractions/IDockCommand.cs
+++ b/Lattice.UI.Docking/Abstractions/IDockCommand.cs
@@ -1,40 +1,58 @@
namespace Lattice.UI.Docking.Abstractions;
///
-/// Определяет контракт для команды док-системы.
+/// Определяет контракт для команды в UI-слое док-системы.
+/// Команды представляют действия, которые могут быть выполнены пользователем.
///
public interface IDockCommand
{
///
- /// Получает идентификатор команды.
+ /// Получает уникальный идентификатор команды.
///
+ ///
+ /// Строковый идентификатор команды.
+ ///
string Id { get; }
///
/// Получает отображаемое имя команды.
///
+ ///
+ /// Имя команды, отображаемое в пользовательском интерфейсе.
+ ///
string Name { get; }
///
/// Получает описание команды.
///
+ ///
+ /// Текстовое описание функциональности команды.
+ ///
string Description { get; }
///
- /// Получает значок команды.
+ /// Получает идентификатор ресурса для иконки команды.
///
+ ///
+ /// Имя ресурса иконки или путь к файлу иконки.
+ ///
string Icon { get; }
///
- /// Получает комбинацию клавиш для команды.
+ /// Получает комбинацию клавиш для быстрого вызова команды.
///
+ ///
+ /// Строковое представление горячей клавиши (например, "Ctrl+S").
+ ///
string Shortcut { get; }
///
- /// Определяет, можно ли выполнить команду.
+ /// Определяет, можно ли выполнить команду в текущем контексте.
///
/// Параметр команды.
- /// true, если команду можно выполнить; в противном случае — false.
+ ///
+ /// true, если команду можно выполнить; в противном случае false.
+ ///
bool CanExecute(object? parameter);
///
@@ -44,7 +62,7 @@ public interface IDockCommand
void Execute(object? parameter);
///
- /// Событие, возникающее при изменении возможности выполнения команды.
+ /// Происходит при изменении возможности выполнения команды.
///
event EventHandler CanExecuteChanged;
-}
+}
\ No newline at end of file
diff --git a/Lattice.UI.Docking/Abstractions/IDockContextManager.cs b/Lattice.UI.Docking/Abstractions/IDockContextManager.cs
index ed5ec9b..d958981 100644
--- a/Lattice.UI.Docking/Abstractions/IDockContextManager.cs
+++ b/Lattice.UI.Docking/Abstractions/IDockContextManager.cs
@@ -11,6 +11,9 @@ public interface IDockContextManager
/// Элемент, для которого показывается меню.
/// Координата X для отображения меню.
/// Координата Y для отображения меню.
+ ///
+ /// Выбрасывается, если равен null.
+ ///
void ShowContextMenu(IDockControl element, double x, double y);
///
@@ -23,6 +26,10 @@ public interface IDockContextManager
///
/// Идентификатор команды.
/// Команда для регистрации.
+ ///
+ /// Выбрасывается, если или
+ /// равны null.
+ ///
void RegisterCommand(string commandId, IDockCommand command);
///
@@ -32,12 +39,12 @@ public interface IDockContextManager
void UnregisterCommand(string commandId);
///
- /// Событие, возникающее при показе контекстного меню.
+ /// Происходит при показе контекстного меню.
///
event EventHandler ContextMenuShown;
///
- /// Событие, возникающее при скрытии контекстного меню.
+ /// Происходит при скрытии контекстного меню.
///
event EventHandler ContextMenuHidden;
-}
+}
\ No newline at end of file
diff --git a/Lattice.UI.Docking/Abstractions/IDockControl.cs b/Lattice.UI.Docking/Abstractions/IDockControl.cs
index 2cdc0c6..fb4351d 100644
--- a/Lattice.UI.Docking/Abstractions/IDockControl.cs
+++ b/Lattice.UI.Docking/Abstractions/IDockControl.cs
@@ -7,11 +7,12 @@ namespace Lattice.UI.Docking.Abstractions;
///
/// Определяет базовый контракт для всех UI-контролов, участвующих в системе докинга.
/// Этот интерфейс предоставляет общие свойства и методы, необходимые для интеграции
-/// с менеджером макета и системой перетаскивания.
+/// с менеджером макета и UI-сервисами.
///
///
/// Реализации этого интерфейса должны отображать элементы док-системы (DockGroup, DockLeaf)
/// и обеспечивать взаимодействие пользователя с ними через жесты мыши, клавиатуру и сенсорный ввод.
+/// Интерфейс обеспечивает двухстороннюю связь между визуальными элементами и их моделями данных.
///
public interface IDockControl : INotifyPropertyChanged
{
@@ -22,6 +23,10 @@ public interface IDockControl : INotifyPropertyChanged
/// Экземпляр класса, реализующего , который представляет
/// состояние и структуру отображаемого элемента док-системы.
///
+ ///
+ /// Изменение модели должно приводить к обновлению визуального представления.
+ /// Свойство используется для привязки данных между UI-слоем и слоем бизнес-логики.
+ ///
IDockElement? Model { get; set; }
///
@@ -30,62 +35,57 @@ public interface IDockControl : INotifyPropertyChanged
///
/// Экземпляр , управляющий структурой док-системы.
///
+ ///
+ /// Менеджер макета предоставляет доступ к дереву компоновки, плавающим окнам
+ /// и автоскрываемым панелям, а также методы для манипуляции структурой.
+ ///
LayoutManager? LayoutManager { get; set; }
- ///
- /// Получает или задает сервис перетаскивания, используемый этим контролом.
- ///
- ///
- /// Реализация для обработки операций перетаскивания.
- ///
- IDragDropService? DragDropService { get; set; }
-
///
/// Получает или задает контекстный менеджер для этого контрола.
///
///
/// Экземпляр , управляющий контекстными меню и действиями.
///
+ ///
+ /// Контекстный менеджер используется для отображения контекстно-зависимых команд
+ /// при щелчке правой кнопкой мыши или других пользовательских действиях.
+ ///
IDockContextManager? ContextManager { get; set; }
///
/// Получает или задает признак того, что контрол выбран.
///
///
- /// true, если контрол выбран; в противном случае — false.
+ /// true, если контрол выбран; в противном случае false.
///
+ ///
+ /// Выделение контрола обычно визуально выделяет его границы или фон,
+ /// чтобы указать пользователю на активный элемент. В каждый момент времени
+ /// может быть выбран только один контрол в пределах контейнера.
+ ///
bool IsSelected { get; set; }
///
/// Получает или задает признак того, что контрол активен.
///
///
- /// true, если контрол активен; в противном случае — false.
+ /// true, если контрол активен; в противном случае false.
///
+ ///
+ /// Активный контрол получает фокус ввода и может обрабатывать команды клавиатуры.
+ /// Обычно соответствует активной вкладке в контейнере или активному окну.
+ ///
bool IsActive { get; set; }
- ///
- /// Получает или задает признак того, что контрол можно перетаскивать.
- ///
- ///
- /// true, если контрол можно перетаскивать; в противном случае — false.
- ///
- bool CanDrag { get; set; }
-
- ///
- /// Получает или задает признак того, что контрол может принимать сброс.
- ///
- ///
- /// true, если контрол может принимать сброс; в противном случае — false.
- ///
- bool CanDrop { get; set; }
-
///
/// Обновляет внешний вид контрола в соответствии с текущим состоянием модели.
///
///
/// Этот метод должен вызываться при изменении свойств модели или при необходимости
/// принудительного обновления UI (например, после изменения темы или масштаба).
+ /// Реализация должна обеспечить синхронизацию всех визуальных аспектов контрола
+ /// с текущими значениями свойств модели.
///
void Refresh();
@@ -93,11 +93,23 @@ public interface IDockControl : INotifyPropertyChanged
/// Применяет указанную тему к контролу.
///
/// Тема для применения.
+ ///
+ /// Выбрасывается, если равен null.
+ ///
+ ///
+ /// Метод должен обновить все стили, цвета и параметры отображения контрола
+ /// в соответствии с переданной темой. Изменения должны применяться немедленно.
+ ///
void ApplyTheme(IDockTheme theme);
///
/// Вызывается при изменении состояния модели для обновления UI.
///
/// Имя изменившегося свойства модели.
+ ///
+ /// Этот метод предназначен для уведомления UI о конкретных изменениях в модели,
+ /// что позволяет выполнять точечные обновления вместо полного перестроения.
+ /// Должен вызываться из обработчиков событий изменения свойств модели.
+ ///
void OnModelPropertyChanged(string propertyName);
}
\ No newline at end of file
diff --git a/Lattice.UI.Docking/Abstractions/IDockDragDropService.cs b/Lattice.UI.Docking/Abstractions/IDockDragDropService.cs
deleted file mode 100644
index d498f36..0000000
--- a/Lattice.UI.Docking/Abstractions/IDockDragDropService.cs
+++ /dev/null
@@ -1,256 +0,0 @@
-namespace Lattice.UI.Docking.Abstractions;
-
-///
-/// Предоставляет сервис для операций перетаскивания в UI-слое док-системы.
-/// Абстрагирует платформенно-зависимую логику перетаскивания и обеспечивает
-/// единый интерфейс для управления операциями drag-and-drop.
-///
-///
-/// Этот интерфейс служит мостом между базовым менеджером перетаскивания из Core
-/// и UI-контролами, добавляя визуальную обратную связь и обработку событий,
-/// специфичных для пользовательского интерфейса.
-///
-public interface IDockDragDropService
-{
- ///
- /// Начинает операцию перетаскивания для указанного элемента.
- ///
- /// UI-контрол, который инициирует перетаскивание.
- ///
- /// Информация о перетаскивании, содержащая данные и параметры операции.
- ///
- ///
- /// Выбрасывается, если или равны null.
- ///
- ///
- /// Этот метод должен создавать визуальное представление перетаскиваемого элемента
- /// и инициировать отслеживание перемещения мыши.
- ///
- void StartDrag(IDockControl element, Core.DragDrop.Models.DragInfo dragInfo);
-
- ///
- /// Обновляет позицию текущей операции перетаскивания.
- ///
- /// Новая координата X курсора в экранных координатах.
- /// Новая координата Y курсора в экранных координатах.
- ///
- /// Вызывается при каждом перемещении мыши во время операции перетаскивания.
- /// Должен обновлять позицию визуального представления и проверять возможные цели сброса.
- ///
- void UpdateDrag(double x, double y);
-
- ///
- /// Завершает текущую операцию перетаскивания в указанной позиции.
- ///
- /// Координата X завершения перетаскивания.
- /// Координата Y завершения перетаскивания.
- ///
- /// Выполняет сброс данных на текущую цель (если она есть) и очищает ресурсы,
- /// выделенные для операции перетаскивания.
- ///
- void EndDrag(double x, double y);
-
- ///
- /// Отменяет текущую операцию перетаскивания.
- ///
- ///
- /// Вызывается при нажатии клавиши Escape или других действиях, приводящих к отмене.
- /// Должен восстанавливать исходное состояние элементов и очищать ресурсы.
- ///
- void CancelDrag();
-
- ///
- /// Показывает визуальную подсказку о возможной позиции сброса.
- ///
- /// UI-контрол, для которого показывается подсказка.
- /// Предполагаемая позиция сброса.
- ///
- /// Используется для визуальной обратной связи, чтобы пользователь видел,
- /// куда будет помещен элемент при отпускании кнопки мыши.
- ///
- void ShowDropHint(IDockControl element, Models.DropPosition position);
-
- ///
- /// Скрывает текущую визуальную подсказку о сбросе.
- ///
- ///
- /// Вызывается, когда курсор покидает допустимую область сброса
- /// или операция перетаскивания завершается.
- ///
- void HideDropHint();
-
- ///
- /// Событие, возникающее при начале операции перетаскивания.
- ///
- event EventHandler DragStarted;
-
- ///
- /// Событие, возникающее при обновлении позиции перетаскивания.
- ///
- event EventHandler DragUpdated;
-
- ///
- /// Событие, возникающее при завершении операции перетаскивания.
- ///
- event EventHandler DragCompleted;
-
- ///
- /// Событие, возникающее при отмене операции перетаскивания.
- ///
- event EventHandler DragCancelled;
-}
-
-///
-/// Предоставляет данные для события начала перетаскивания.
-///
-public class DragStartedEventArgs : EventArgs
-{
- ///
- /// Получает UI-контрол, который инициировал перетаскивание.
- ///
- ///
- /// Экземпляр , представляющий источник перетаскивания.
- /// Может быть null, если перетаскивание инициировано не из UI-элемента.
- ///
- public IDockControl? Source { get; }
-
- ///
- /// Получает информацию о перетаскивании.
- ///
- ///
- /// Экземпляр с данными перетаскивания.
- ///
- public Core.DragDrop.Models.DragInfo DragInfo { get; }
-
- ///
- /// Инициализирует новый экземпляр класса .
- ///
- /// Источник перетаскивания.
- /// Информация о перетаскивании.
- public DragStartedEventArgs(IDockControl? source, Core.DragDrop.Models.DragInfo dragInfo)
- {
- Source = source;
- DragInfo = dragInfo;
- }
-}
-
-///
-/// Предоставляет данные для события обновления перетаскивания.
-///
-public class DragUpdatedEventArgs : EventArgs
-{
- ///
- /// Получает UI-контрол, который инициировал перетаскивание.
- ///
- ///
- /// Экземпляр , представляющий источник перетаскивания.
- ///
- public IDockControl? Source { get; }
-
- ///
- /// Получает текущую координату X курсора.
- ///
- ///
- /// Координата X в экранных координатах.
- ///
- public double X { get; }
-
- ///
- /// Получает текущую координату Y курсора.
- ///
- ///
- /// Координата Y в экранных координатах.
- ///
- public double Y { get; }
-
- ///
- /// Получает информацию о перетаскивании.
- ///
- ///
- /// Экземпляр с текущими данными перетаскивания.
- ///
- public Core.DragDrop.Models.DragInfo DragInfo { get; }
-
- ///
- /// Инициализирует новый экземпляр класса .
- ///
- /// Источник перетаскивания.
- /// Текущая координата X.
- /// Текущая координата Y.
- /// Информация о перетаскивании.
- public DragUpdatedEventArgs(IDockControl? source, double x, double y, Core.DragDrop.Models.DragInfo dragInfo)
- {
- Source = source;
- X = x;
- Y = y;
- DragInfo = dragInfo;
- }
-}
-
-///
-/// Предоставляет данные для события завершения перетаскивания.
-///
-public class DragCompletedEventArgs : EventArgs
-{
- ///
- /// Получает UI-контрол, который инициировал перетаскивание.
- ///
- ///
- /// Экземпляр , представляющий источник перетаскивания.
- /// Может быть null, если операция была инициирована не из UI.
- ///
- public IDockControl? Source { get; }
-
- ///
- /// Получает UI-контрол, на который был выполнен сброс.
- ///
- ///
- /// Экземпляр , представляющий цель сброса.
- /// Может быть null, если сброс был выполнен вне допустимой области.
- ///
- public IDockControl? Target { get; }
-
- ///
- /// Получает позицию сброса относительно целевого элемента.
- ///
- ///
- /// Значение перечисления , указывающее позицию сброса.
- ///
- public Models.DropPosition DropPosition { get; }
-
- ///
- /// Получает информацию о перетаскивании.
- ///
- ///
- /// Экземпляр с данными завершенной операции.
- /// Может быть null, если операция была отменена.
- ///
- public Core.DragDrop.Models.DragInfo? DragInfo { get; }
-
- ///
- /// Получает значение, указывающее успешность операции сброса.
- ///
- ///
- /// true, если данные были успешно сброшены на цель; false, если операция была отменена
- /// или сброс не был выполнен.
- ///
- public bool Success { get; }
-
- ///
- /// Инициализирует новый экземпляр класса .
- ///
- /// Источник перетаскивания.
- /// Цель сброса.
- /// Позиция сброса.
- /// Информация о перетаскивании.
- /// Признак успешности операции.
- public DragCompletedEventArgs(IDockControl? source, IDockControl? target,
- Models.DropPosition dropPosition, Core.DragDrop.Models.DragInfo? dragInfo, bool success)
- {
- Source = source;
- Target = target;
- DropPosition = dropPosition;
- DragInfo = dragInfo;
- Success = success;
- }
-}
\ No newline at end of file
diff --git a/Lattice.UI.Docking/Abstractions/IDockGroupControl.cs b/Lattice.UI.Docking/Abstractions/IDockGroupControl.cs
index 6cf8ec7..0fde503 100644
--- a/Lattice.UI.Docking/Abstractions/IDockGroupControl.cs
+++ b/Lattice.UI.Docking/Abstractions/IDockGroupControl.cs
@@ -60,23 +60,23 @@ public interface IDockGroupControl : IDockControl
void SetChildren(IDockControl? firstChild, IDockControl? secondChild);
///
- /// Событие, возникающее при изменении соотношения разделения.
+ /// Происходит при изменении соотношения разделения.
///
event EventHandler SplitRatioChanged;
}
///
-/// Аргументы события изменения соотношения разделения.
+/// Предоставляет данные для события изменения соотношения разделения.
///
public class SplitRatioChangedEventArgs : EventArgs
{
///
- /// Новое соотношение разделения.
+ /// Получает новое соотношение разделения.
///
public double NewRatio { get; }
///
- /// Источник изменения (пользователь или программа).
+ /// Получает источник изменения соотношения разделения.
///
public SplitRatioChangeSource Source { get; }
@@ -93,16 +93,22 @@ public class SplitRatioChangedEventArgs : EventArgs
}
///
-/// Источник изменения соотношения разделения.
+/// Определяет источник изменения соотношения разделения.
///
public enum SplitRatioChangeSource
{
- /// Изменение выполнено пользователем.
+ ///
+ /// Изменение выполнено пользователем.
+ ///
User,
- /// Изменение выполнено программой.
+ ///
+ /// Изменение выполнено программой.
+ ///
Programmatic,
- /// Изменение выполнено при восстановлении состояния.
+ ///
+ /// Изменение выполнено при восстановлении состояния.
+ ///
Restore
}
\ No newline at end of file
diff --git a/Lattice.UI.Docking/Abstractions/IDockHost.cs b/Lattice.UI.Docking/Abstractions/IDockHost.cs
index 487fb4f..aa1104f 100644
--- a/Lattice.UI.Docking/Abstractions/IDockHost.cs
+++ b/Lattice.UI.Docking/Abstractions/IDockHost.cs
@@ -10,8 +10,7 @@ namespace Lattice.UI.Docking.Abstractions;
///
///
/// Реализации этого интерфейса представляют собой центральный координатор UI-слоя,
-/// который интегрирует функциональность менеджера макета, системы перетаскивания
-/// и контекстных меню в единый визуальный компонент.
+/// который интегрирует функциональность менеджера макета и контекстных меню в единый визуальный компонент.
///
public interface IDockHost : IDockControl
{
@@ -23,8 +22,7 @@ public interface IDockHost : IDockControl
/// представляющих все активные плавающие окна в системе.
///
///
- /// Плавающие окна могут быть созданы пользователем путем перетаскивания элементов
- /// за пределы основного окна или программно через методы API.
+ /// Плавающие окна могут быть созданы пользователем или программно через методы API.
///
IEnumerable FloatingWindows { get; }
@@ -45,7 +43,7 @@ public interface IDockHost : IDockControl
/// Получает или задает значение, указывающее, отображается ли панель инструментов (Toolbox).
///
///
- /// true, если панель инструментов видима; в противном случае — false.
+ /// true, если панель инструментов видима; в противном случае false.
/// Значение по умолчанию зависит от реализации.
///
///
@@ -58,7 +56,7 @@ public interface IDockHost : IDockControl
/// Получает или задает значение, указывающее, отображается ли строка состояния.
///
///
- /// true, если строка состояния видима; в противном случае — false.
+ /// true, если строка состояния видима; в противном случае false.
/// Значение по умолчанию зависит от реализации.
///
///
@@ -71,9 +69,9 @@ public interface IDockHost : IDockControl
/// Получает или задает значение, указывающее, отображается ли главное меню приложения.
///
///
- /// true, если главное меню видимо; в противном случае — false.
+ /// true, если главное меню видимо; в противном случае false.
/// Значение по умолчанию зависит от реализации.
- ///
+ ///
bool ShowMenu { get; set; }
///
@@ -148,7 +146,7 @@ public interface IDockHost : IDockControl
void RemoveAutoHidePanel(IAutoHidePanelControl panel);
///
- /// Событие, возникающее при изменении структуры макета док-системы.
+ /// Происходит при изменении структуры макета док-системы.
///
///
/// Может вызываться при добавлении/удалении элементов, изменении размеров,
@@ -157,12 +155,12 @@ public interface IDockHost : IDockControl
event EventHandler LayoutChanged;
///
- /// Событие, возникающее при создании нового плавающего окна.
+ /// Происходит при создании нового плавающего окна.
///
event EventHandler FloatingWindowCreated;
///
- /// Событие, возникающее при закрытии плавающего окна.
+ /// Происходит при закрытии плавающего окна.
///
event EventHandler FloatingWindowClosed;
}
diff --git a/Lattice.UI.Docking/Abstractions/IDockLeafControl.cs b/Lattice.UI.Docking/Abstractions/IDockLeafControl.cs
index 9077e06..50c3092 100644
--- a/Lattice.UI.Docking/Abstractions/IDockLeafControl.cs
+++ b/Lattice.UI.Docking/Abstractions/IDockLeafControl.cs
@@ -26,7 +26,7 @@ public interface IDockLeafControl : IDockControl
/// Получает или задает признак отображения кнопки закрытия на вкладках.
///
///
- /// true, если кнопки закрытия отображаются; в противном случае — false.
+ /// true, если кнопки закрытия отображаются; в противном случае false.
///
bool ShowCloseButtons { get; set; }
@@ -34,7 +34,7 @@ public interface IDockLeafControl : IDockControl
/// Получает или задает признак возможности изменения порядка вкладок.
///
///
- /// true, если порядок вкладок можно изменять; в противном случае — false.
+ /// true, если порядок вкладок можно изменять; в противном случае false.
///
bool CanReorderTabs { get; set; }
@@ -50,25 +50,39 @@ public interface IDockLeafControl : IDockControl
/// Добавляет вкладку в контрол.
///
/// Контент для добавления.
+ ///
+ /// Выбрасывается, если равен null.
+ ///
void AddContent(IDockContent content);
///
/// Удаляет вкладку из контрола.
///
/// Контент для удаления.
+ ///
+ /// Выбрасывается, если равен null.
+ ///
void RemoveContent(IDockContent content);
///
/// Закрывает указанную вкладку.
///
/// Контент для закрытия.
- /// true, если вкладка была закрыта; в противном случае — false.
+ ///
+ /// true, если вкладка была закрыта; в противном случае false.
+ ///
+ ///
+ /// Выбрасывается, если равен null.
+ ///
bool CloseContent(IDockContent content);
///
/// Закрывает все вкладки, кроме указанной.
///
/// Вкладка, которую нужно оставить открытой.
+ ///
+ /// Выбрасывается, если равен null.
+ ///
void CloseAllExcept(IDockContent exceptContent);
///
@@ -77,39 +91,41 @@ public interface IDockLeafControl : IDockControl
void CloseAll();
///
- /// Событие, возникающее при изменении активной вкладки.
+ /// Происходит при изменении активной вкладки.
///
event EventHandler ActiveContentChanged;
///
- /// Событие, возникающее при запросе закрытия вкладки.
+ /// Происходит при запросе закрытия вкладки.
///
event EventHandler ContentClosing;
///
- /// Событие, возникающее при изменении порядка вкладок.
+ /// Происходит при изменении порядка вкладок.
///
event EventHandler TabsReordered;
}
///
-/// Аргументы события изменения активного контента.
+/// Предоставляет данные для события изменения активного контента.
///
public class ActiveContentChangedEventArgs : EventArgs
{
///
- /// Предыдущий активный контент.
+ /// Получает предыдущий активный контент.
///
public IDockContent? OldContent { get; }
///
- /// Новый активный контент.
+ /// Получает новый активный контент.
///
public IDockContent? NewContent { get; }
///
/// Инициализирует новый экземпляр класса .
///
+ /// Предыдущий активный контент.
+ /// Новый активный контент.
public ActiveContentChangedEventArgs(IDockContent? oldContent, IDockContent? newContent)
{
OldContent = oldContent;
@@ -118,63 +134,79 @@ public class ActiveContentChangedEventArgs : EventArgs
}
///
-/// Аргументы события закрытия контента.
+/// Предоставляет данные для события закрытия контента.
///
public class ContentClosingEventArgs : EventArgs
{
///
- /// Контент, который закрывается.
+ /// Получает контент, который закрывается.
///
public IDockContent Content { get; }
///
- /// Показывает, можно ли отменить закрытие.
+ /// Получает или задает значение, указывающее, можно ли отменить закрытие.
///
+ ///
+ /// true, если закрытие можно отменить; в противном случае false.
+ ///
public bool CanCancel { get; set; }
///
/// Получает или задает признак отмены закрытия.
///
+ ///
+ /// true, если закрытие отменено; в противном случае false.
+ ///
public bool Cancel { get; set; }
///
/// Инициализирует новый экземпляр класса .
///
+ /// Контент, который закрывается.
+ ///
+ /// Выбрасывается, если равен null.
+ ///
public ContentClosingEventArgs(IDockContent content)
{
- Content = content;
+ Content = content ?? throw new ArgumentNullException(nameof(content));
CanCancel = true;
Cancel = false;
}
}
///
-/// Аргументы события изменения порядка вкладок.
+/// Предоставляет данные для события изменения порядка вкладок.
///
public class TabsReorderedEventArgs : EventArgs
{
///
- /// Старый индекс вкладки.
+ /// Получает старый индекс вкладки.
///
public int OldIndex { get; }
///
- /// Новый индекс вкладки.
+ /// Получает новый индекс вкладки.
///
public int NewIndex { get; }
///
- /// Перемещаемый контент.
+ /// Получает перемещаемый контент.
///
public IDockContent Content { get; }
///
/// Инициализирует новый экземпляр класса .
///
+ /// Старый индекс вкладки.
+ /// Новый индекс вкладки.
+ /// Перемещаемый контент.
+ ///
+ /// Выбрасывается, если равен null.
+ ///
public TabsReorderedEventArgs(int oldIndex, int newIndex, IDockContent content)
{
OldIndex = oldIndex;
NewIndex = newIndex;
- Content = content;
+ Content = content ?? throw new ArgumentNullException(nameof(content));
}
}
\ No newline at end of file
diff --git a/Lattice.UI.Docking/Abstractions/IDockSplitterControl.cs b/Lattice.UI.Docking/Abstractions/IDockSplitterControl.cs
new file mode 100644
index 0000000..64bd29f
--- /dev/null
+++ b/Lattice.UI.Docking/Abstractions/IDockSplitterControl.cs
@@ -0,0 +1,72 @@
+namespace Lattice.UI.Docking.Abstractions;
+
+///
+/// Определяет контракт для контрола разделителя между элементами док-системы.
+/// Разделитель позволяет пользователю изменять размер смежных элементов.
+///
+public interface IDockSplitterControl : IDockControl
+{
+ ///
+ /// Получает или задает ориентацию разделителя.
+ ///
+ ///
+ /// Ориентация разделителя (горизонтальная или вертикальная).
+ ///
+ Core.Docking.Models.SplitDirection Orientation { get; set; }
+
+ ///
+ /// Получает или задает признак того, что разделитель активен (перетаскивается).
+ ///
+ ///
+ /// true, если разделитель активен; в противном случае false.
+ ///
+ bool IsDragging { get; set; }
+
+ ///
+ /// Происходит при начале перетаскивания разделителя.
+ ///
+ event EventHandler DragStarted;
+
+ ///
+ /// Происходит при перетаскивании разделителя.
+ ///
+ event EventHandler DragDelta;
+
+ ///
+ /// Происходит при завершении перетаскивания разделителя.
+ ///
+ event EventHandler DragCompleted;
+}
+
+///
+/// Предоставляет данные для события перетаскивания разделителя.
+///
+public class SplitterDraggedEventArgs : EventArgs
+{
+ ///
+ /// Получает изменение позиции по горизонтали.
+ ///
+ ///
+ /// Изменение по горизонтали в пикселях.
+ ///
+ public double HorizontalChange { get; }
+
+ ///
+ /// Получает изменение позиции по вертикали.
+ ///
+ ///
+ /// Изменение по вертикали в пикселях.
+ ///
+ public double VerticalChange { get; }
+
+ ///
+ /// Инициализирует новый экземпляр класса .
+ ///
+ /// Изменение по горизонтали.
+ /// Изменение по вертикали.
+ public SplitterDraggedEventArgs(double horizontalChange, double verticalChange)
+ {
+ HorizontalChange = horizontalChange;
+ VerticalChange = verticalChange;
+ }
+}
\ No newline at end of file
diff --git a/Lattice.UI.Docking/Abstractions/IDockTheme.cs b/Lattice.UI.Docking/Abstractions/IDockTheme.cs
index 2734dc6..80ba18e 100644
--- a/Lattice.UI.Docking/Abstractions/IDockTheme.cs
+++ b/Lattice.UI.Docking/Abstractions/IDockTheme.cs
@@ -77,31 +77,37 @@ public interface IDockTheme
}
///
-/// Аргументы события показа контекстного меню.
+/// Предоставляет данные для события показа контекстного меню.
///
public class ContextMenuShownEventArgs : EventArgs
{
///
- /// Элемент, для которого показано меню.
+ /// Получает элемент, для которого показано меню.
///
public IDockControl Target { get; }
///
- /// Координата X меню.
+ /// Получает координату X меню.
///
public double X { get; }
///
- /// Координата Y меню.
+ /// Получает координату Y меню.
///
public double Y { get; }
///
/// Инициализирует новый экземпляр класса .
///
+ /// Элемент, для которого показано меню.
+ /// Координата X меню.
+ /// Координата Y меню.
+ ///
+ /// Выбрасывается, если равен null.
+ ///
public ContextMenuShownEventArgs(IDockControl target, double x, double y)
{
- Target = target;
+ Target = target ?? throw new ArgumentNullException(nameof(target));
X = x;
Y = y;
}
diff --git a/Lattice.UI.Docking/Abstractions/IDockUIService.cs b/Lattice.UI.Docking/Abstractions/IDockUIService.cs
index fd8c3b0..85505c5 100644
--- a/Lattice.UI.Docking/Abstractions/IDockUIService.cs
+++ b/Lattice.UI.Docking/Abstractions/IDockUIService.cs
@@ -22,12 +22,8 @@ public interface IDockUIService
/// Платформенно-зависимый объект окна, который можно отобразить.
///
///
- /// Выбрасывается, если равен null.
+ /// Выбрасывается, когда равен null.
///
- ///
- /// Реализация должна создавать окно с соответствующими стилями и поведением
- /// для целевой платформы, настроенное для работы с док-системой.
- ///
object CreateMainWindow(IDockHost host);
///
@@ -42,12 +38,8 @@ public interface IDockUIService
/// null - диалог был закрыт без выбора.
///
///
- /// Выбрасывается, если или равны null.
+ /// Выбрасывается, когда или равны null.
///
- ///
- /// Реализация должна блокировать взаимодействие с родительским окном
- /// до закрытия диалога.
- ///
bool? ShowDialog(string title, object content);
///
@@ -56,12 +48,8 @@ public interface IDockUIService
/// Текст сообщения.
/// Заголовок окна сообщения.
///
- /// Выбрасывается, если или равны null.
+ /// Выбрасывается, когда или равны null.
///
- ///
- /// Реализация должна использовать стандартные диалоги платформы
- /// или создавать кастомные окна сообщений.
- ///
void ShowMessage(string message, string caption);
///
@@ -73,12 +61,8 @@ public interface IDockUIService
/// true, если пользователь выбрал "Yes"; false, если пользователь выбрал "No".
///
///
- /// Выбрасывается, если или равны null.
+ /// Выбрасывается, когда или равны null.
///
- ///
- /// Используется для получения подтверждения от пользователя перед выполнением
- /// критических операций (закрытие вкладок, сброс настроек и т.д.).
- ///
bool Confirm(string message, string caption);
///
@@ -90,12 +74,8 @@ public interface IDockUIService
/// Введенный пользователем текст или null, если диалог был отменен.
///
///
- /// Выбрасывается, если равен null.
+ /// Выбрасывается, когда равен null.
///
- ///
- /// Реализация должна предоставлять однострочное поле ввода текста
- /// с возможностью отмены операции.
- ///
string? Prompt(string prompt, string? defaultValue = null);
///
@@ -103,12 +83,7 @@ public interface IDockUIService
///
/// Действие для выполнения.
///
- /// Выбрасывается, если равен null.
+ /// Выбрасывается, когда равен null.
///
- ///
- /// Этот метод гарантирует, что действие будет выполнено в потоке,
- /// связанном с пользовательским интерфейсом, что необходимо для
- /// безопасного обновления UI-элементов.
- ///
void InvokeOnUIThread(Action action);
}
\ No newline at end of file
diff --git a/Lattice.UI.Docking/Abstractions/IFloatingWindowControl.cs b/Lattice.UI.Docking/Abstractions/IFloatingWindowControl.cs
index 045fc26..a2041c6 100644
--- a/Lattice.UI.Docking/Abstractions/IFloatingWindowControl.cs
+++ b/Lattice.UI.Docking/Abstractions/IFloatingWindowControl.cs
@@ -53,7 +53,7 @@ public interface IFloatingWindowControl : IDockControl
/// Получает или задает признак того, что окно можно изменять.
///
///
- /// true, если размеры окна можно изменять; в противном случае — false.
+ /// true, если размеры окна можно изменять; в противном случае false.
///
bool CanResize { get; set; }
@@ -61,7 +61,7 @@ public interface IFloatingWindowControl : IDockControl
/// Получает или задает признак того, что окно можно перемещать.
///
///
- /// true, если окно можно перемещать; в противном случае — false.
+ /// true, если окно можно перемещать; в противном случае false.
///
bool CanMove { get; set; }
@@ -69,7 +69,7 @@ public interface IFloatingWindowControl : IDockControl
/// Получает или задает признак того, что окно всегда поверх других окон.
///
///
- /// true, если окно всегда поверх; в противном случае — false.
+ /// true, если окно всегда поверх; в противном случае false.
///
bool AlwaysOnTop { get; set; }
@@ -94,17 +94,17 @@ public interface IFloatingWindowControl : IDockControl
void Activate();
///
- /// Событие, возникающее при закрытии окна.
+ /// Происходит при закрытии окна.
///
event EventHandler Closing;
///
- /// Событие, возникающее при изменении положения окна.
+ /// Происходит при изменении положения окна.
///
event EventHandler LocationChanged;
///
- /// Событие, возникающее при изменении размера окна.
+ /// Происходит при изменении размера окна.
///
event EventHandler SizeChanged;
}
\ No newline at end of file
diff --git a/Lattice.UI.Docking/Commands/DockCommandBase.cs b/Lattice.UI.Docking/Commands/DockCommandBase.cs
index e9d2f13..b9d0228 100644
--- a/Lattice.UI.Docking/Commands/DockCommandBase.cs
+++ b/Lattice.UI.Docking/Commands/DockCommandBase.cs
@@ -1,33 +1,85 @@
using Lattice.UI.Docking.Abstractions;
+using System.ComponentModel;
+using System.Runtime.CompilerServices;
namespace Lattice.UI.Docking.Commands;
///
-/// Базовая реализация команды док-системы.
+/// Предоставляет базовую реализацию команды док-системы.
+/// Реализует интерфейс и
+/// для поддержки уведомлений об изменении свойств.
///
-public abstract class DockCommandBase : IDockCommand
+///
+/// Этот класс предоставляет общую логику для команд, включая управление состоянием
+/// возможности выполнения, уведомление об изменениях и реализацию интерфейса ICommand.
+///
+public abstract class DockCommandBase : IDockCommand, INotifyPropertyChanged
{
+ ///
+ /// Происходит при изменении возможности выполнения команды.
+ ///
+ public event EventHandler? CanExecuteChanged;
+
+ ///
+ /// Происходит при изменении значения свойства.
+ ///
+ public event PropertyChangedEventHandler? PropertyChanged;
+
private bool _canExecute = true;
- ///
+ ///
+ /// Получает уникальный идентификатор команды.
+ ///
+ ///
+ /// Строковый идентификатор команды. Должен быть уникальным в пределах системы.
+ ///
public abstract string Id { get; }
- ///
+ ///
+ /// Получает отображаемое имя команды.
+ ///
+ ///
+ /// Имя команды, отображаемое в пользовательском интерфейсе.
+ ///
public abstract string Name { get; }
- ///
+ ///
+ /// Получает описание команды.
+ ///
+ ///
+ /// Текстовое описание функциональности команды.
+ /// Реализация по умолчанию возвращает пустую строку.
+ ///
public virtual string Description => string.Empty;
- ///
+ ///
+ /// Получает идентификатор ресурса для иконки команды.
+ ///
+ ///
+ /// Имя ресурса иконки или путь к файлу иконки.
+ /// Реализация по умолчанию возвращает пустую строку.
+ ///
public virtual string Icon => string.Empty;
- ///
+ ///
+ /// Получает комбинацию клавиш для быстрого вызова команды.
+ ///
+ ///
+ /// Строковое представление горячей клавиши.
+ /// Реализация по умолчанию возвращает пустую строку.
+ ///
public virtual string Shortcut => string.Empty;
///
- /// Получает или задает признак возможности выполнения команды.
+ /// Получает или задает значение, указывающее, можно ли выполнить команду.
///
- public bool CanExecute
+ ///
+ /// true, если команду можно выполнить; в противном случае false.
+ ///
+ ///
+ /// При изменении этого свойства генерируется событие .
+ ///
+ protected bool CanExecuteValue
{
get => _canExecute;
set
@@ -36,42 +88,131 @@ public abstract class DockCommandBase : IDockCommand
{
_canExecute = value;
OnCanExecuteChanged();
+ OnPropertyChanged();
}
}
}
- ///
- public event EventHandler? CanExecuteChanged;
-
- ///
+ ///
+ /// Определяет, можно ли выполнить команду в текущем контексте.
+ ///
+ /// Параметр команды.
+ ///
+ /// true, если команду можно выполнить; в противном случае false.
+ ///
+ ///
+ /// Реализация по умолчанию возвращает значение свойства .
+ ///
public virtual bool CanExecute(object? parameter)
{
return _canExecute;
}
- ///
+ ///
+ /// Выполняет команду.
+ ///
+ /// Параметр команды.
+ ///
+ /// Этот метод должен быть реализован в производных классах.
+ ///
public abstract void Execute(object? parameter);
///
/// Вызывает событие изменения возможности выполнения команды.
///
+ ///
+ /// Этот метод может быть переопределен в производных классах для добавления
+ /// дополнительной логики перед вызовом события.
+ ///
protected virtual void OnCanExecuteChanged()
{
CanExecuteChanged?.Invoke(this, EventArgs.Empty);
}
+
+ ///
+ /// Вызывает событие .
+ ///
+ ///
+ /// Имя изменившегося свойства. Если не указано, определяется автоматически.
+ ///
+ protected virtual void OnPropertyChanged([CallerMemberName] string? propertyName = null)
+ {
+ PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
+ }
}
///
-/// Базовая команда для закрытия контента.
+/// Предоставляет команду для закрытия контента (вкладки).
///
+///
+/// Команда закрывает активную вкладку в указанном контроле листа.
+///
public class CloseContentCommand : DockCommandBase
{
+ ///
+ /// Получает уникальный идентификатор команды.
+ ///
+ ///
+ /// Идентификатор "CloseContent".
+ ///
public override string Id => "CloseContent";
+
+ ///
+ /// Получает отображаемое имя команды.
+ ///
+ ///
+ /// Имя "Close".
+ ///
public override string Name => "Close";
+
+ ///
+ /// Получает описание команды.
+ ///
+ ///
+ /// Описание "Close the current tab".
+ ///
public override string Description => "Close the current tab";
+
+ ///
+ /// Получает идентификатор ресурса для иконки команды.
+ ///
+ ///
+ /// Идентификатор "Close".
+ ///
public override string Icon => "Close";
+
+ ///
+ /// Получает комбинацию клавиш для быстрого вызова команды.
+ ///
+ ///
+ /// Горячая клавиша "Ctrl+F4".
+ ///
public override string Shortcut => "Ctrl+F4";
+ ///
+ /// Определяет, можно ли выполнить команду в текущем контексте.
+ ///
+ /// Параметр команды.
+ ///
+ /// true, если команду можно выполнить; в противном случае false.
+ ///
+ ///
+ /// Команда доступна только если параметр является контролом листа
+ /// и содержит активный контент.
+ ///
+ public override bool CanExecute(object? parameter)
+ {
+ return parameter is Abstractions.IDockLeafControl leafControl &&
+ leafControl.ActiveContent != null;
+ }
+
+ ///
+ /// Выполняет команду закрытия контента.
+ ///
+ /// Параметр команды. Ожидается .
+ ///
+ /// Команда закрывает активную вкладку в указанном контроле листа.
+ ///
public override void Execute(object? parameter)
{
if (parameter is Abstractions.IDockLeafControl leafControl &&
@@ -83,33 +224,137 @@ public class CloseContentCommand : DockCommandBase
}
///
-/// Базовая команда для создания плавающего окна.
+/// Предоставляет команду для создания плавающего окна из элемента.
///
public class FloatWindowCommand : DockCommandBase
{
+ ///
+ /// Получает уникальный идентификатор команды.
+ ///
+ ///
+ /// Идентификатор "FloatWindow".
+ ///
public override string Id => "FloatWindow";
+
+ ///
+ /// Получает отображаемое имя команды.
+ ///
+ ///
+ /// Имя "Float".
+ ///
public override string Name => "Float";
+
+ ///
+ /// Получает описание команды.
+ ///
+ ///
+ /// Описание "Float the window as a separate window".
+ ///
public override string Description => "Float the window as a separate window";
+
+ ///
+ /// Получает идентификатор ресурса для иконки команды.
+ ///
+ ///
+ /// Идентификатор "Float".
+ ///
public override string Icon => "Float";
+ ///
+ /// Определяет, можно ли выполнить команду в текущем контексте.
+ ///
+ /// Параметр команды.
+ ///
+ /// true, если команду можно выполнить; в противном случае false.
+ ///
+ ///
+ /// Команда доступна только если параметр является элементом док-системы,
+ /// который может быть преобразован в плавающее окно.
+ ///
+ public override bool CanExecute(object? parameter)
+ {
+ return parameter is Core.Docking.Abstractions.IDockElement;
+ }
+
+ ///
+ /// Выполняет команду создания плавающего окна.
+ ///
+ /// Параметр команды. Ожидается .
+ ///
+ /// Реализация зависит от конкретного UI и должна быть предоставлена в производных классах.
+ /// Базовая реализация не выполняет никаких действий.
+ ///
public override void Execute(object? parameter)
{
// Реализация зависит от конкретного UI
+ // В базовом классе метод не выполняет никаких действий
}
}
///
-/// Базовая команда для закрепления окна.
+/// Предоставляет команду для закрепления плавающего окна.
///
public class DockWindowCommand : DockCommandBase
{
+ ///
+ /// Получает уникальный идентификатор команды.
+ ///
+ ///
+ /// Идентификатор "DockWindow".
+ ///
public override string Id => "DockWindow";
+
+ ///
+ /// Получает отображаемое имя команды.
+ ///
+ ///
+ /// Имя "Dock".
+ ///
public override string Name => "Dock";
+
+ ///
+ /// Получает описание команды.
+ ///
+ ///
+ /// Описание "Dock the window to the main window".
+ ///
public override string Description => "Dock the window to the main window";
+
+ ///
+ /// Получает идентификатор ресурса для иконки команды.
+ ///
+ ///
+ /// Идентификатор "Dock".
+ ///
public override string Icon => "Dock";
+ ///
+ /// Определяет, можно ли выполнить команду в текущем контексте.
+ ///
+ /// Параметр команды.
+ ///
+ /// true, если команду можно выполнить; в противном случае false.
+ ///
+ ///
+ /// Команда доступна только если параметр является плавающим окном,
+ /// которое может быть закреплено в основном окне.
+ ///
+ public override bool CanExecute(object? parameter)
+ {
+ return parameter is Abstractions.IFloatingWindowControl;
+ }
+
+ ///
+ /// Выполняет команду закрепления окна.
+ ///
+ /// Параметр команды. Ожидается .
+ ///
+ /// Реализация зависит от конкретного UI и должна быть предоставлена в производных классах.
+ /// Базовая реализация не выполняет никаких действий.
+ ///
public override void Execute(object? parameter)
{
// Реализация зависит от конкретного UI
+ // В базовом классе метод не выполняет никаких действий
}
}
\ No newline at end of file
diff --git a/Lattice.UI.Docking/Factories/DockControlFactoryBase.cs b/Lattice.UI.Docking/Factories/DockControlFactoryBase.cs
index 93ddf4d..38c672c 100644
--- a/Lattice.UI.Docking/Factories/DockControlFactoryBase.cs
+++ b/Lattice.UI.Docking/Factories/DockControlFactoryBase.cs
@@ -5,57 +5,136 @@ using Lattice.UI.Docking.Abstractions;
namespace Lattice.UI.Docking.Factories;
///
-/// Базовая фабрика для создания UI-контролов док-системы.
+/// Предоставляет базовую реализацию фабрики для создания UI-контролов док-системы.
///
+///
+/// Этот класс реализует общую логику для фабрик контролов, включая настройку
+/// общих свойств и создание контролов для произвольных элементов.
+///
public abstract class DockControlFactoryBase : IDockControlFactory
{
- ///
- /// Получает или задает сервис перетаскивания для создаваемых контролов.
- ///
- public Services.IDockDragDropService? DragDropService { get; set; }
-
///
/// Получает или задает менеджер контекста для создаваемых контролов.
///
- public Services.IDockContextManager? ContextManager { get; set; }
-
- ///
- public abstract IDockGroupControl CreateGroupControl(DockGroup group);
-
- ///
- public abstract IDockLeafControl CreateLeafControl(DockLeaf leaf);
-
- ///
- public abstract IFloatingWindowControl CreateFloatingWindowControl(DockWindow window);
-
- ///
- public abstract IAutoHidePanelControl CreateAutoHidePanelControl(AutoHidePanel panel);
-
- ///
- public abstract IDockSplitterControl CreateSplitterControl(SplitDirection orientation);
+ ///
+ /// Экземпляр или null, если не установлен.
+ ///
+ public IDockContextManager? ContextManager { get; set; }
///
- /// Создает контрол для произвольного элемента док-системы.
+ /// Создает контрол для группы разделения.
///
+ /// Модель группы.
+ ///
+ /// Созданный контрол группы.
+ ///
+ ///
+ /// Выбрасывается, если равен null.
+ ///
+ ///
+ /// Этот метод должен быть реализован в производных классах.
+ ///
+ public abstract IDockGroupControl CreateGroupControl(DockGroup group);
+
+ ///
+ /// Создает контрол для контейнера вкладок.
+ ///
+ /// Модель листа.
+ ///
+ /// Созданный контрол листа.
+ ///
+ ///
+ /// Выбрасывается, если равен null.
+ ///
+ ///
+ /// Этот метод должен быть реализован в производных классах.
+ ///
+ public abstract IDockLeafControl CreateLeafControl(DockLeaf leaf);
+
+ ///
+ /// Создает контрол для плавающего окна.
+ ///
+ /// Модель окна.
+ ///
+ /// Созданный контрол окна.
+ ///
+ ///
+ /// Выбрасывается, если равен null.
+ ///
+ ///
+ /// Этот метод должен быть реализован в производных классах.
+ ///
+ public abstract IFloatingWindowControl CreateFloatingWindowControl(DockWindow window);
+
+ ///
+ /// Создает контрол для автоскрываемой панели.
+ ///
+ /// Модель панели.
+ ///
+ /// Созданный контрол панели.
+ ///
+ ///
+ /// Выбрасывается, если равен null.
+ ///
+ ///
+ /// Этот метод должен быть реализован в производных классах.
+ ///
+ public abstract IAutoHidePanelControl CreateAutoHidePanelControl(AutoHidePanel panel);
+
+ ///
+ /// Создает контрол для разделителя.
+ ///
+ /// Ориентация разделителя.
+ ///
+ /// Созданный контрол разделителя.
+ ///
+ ///
+ /// Этот метод должен быть реализован в производных классах.
+ ///
+ public abstract IDockSplitterControl CreateSplitterControl(SplitDirection orientation);
+
+ ///
public virtual IDockControl? CreateControlForElement(IDockElement element)
{
- return element switch
+ if (element == null) throw new ArgumentNullException(nameof(element));
+
+ IDockControl? control = null;
+
+ switch (element)
{
- DockGroup group => CreateGroupControl(group),
- DockLeaf leaf => CreateLeafControl(leaf),
- _ => null
- };
+ case DockGroup group:
+ control = CreateGroupControl(group);
+ break;
+ case DockLeaf leaf:
+ control = CreateLeafControl(leaf);
+ break;
+ default:
+ control = null;
+ break;
+ }
+
+ if (control != null)
+ {
+ ConfigureControl(control);
+ }
+
+ return control;
}
///
/// Настраивает общие свойства контрола.
///
+ /// Контрол для настройки.
+ ///
+ /// Выбрасывается, если равен null.
+ ///
+ ///
+ /// Устанавливает общие свойства, такие как контекстный менеджер,
+ /// для всех создаваемых контролов.
+ ///
protected virtual void ConfigureControl(IDockControl control)
{
- if (DragDropService != null)
- {
- control.DragDropService = DragDropService;
- }
+ if (control == null) throw new ArgumentNullException(nameof(control));
if (ContextManager != null)
{
diff --git a/Lattice.UI.Docking/Factories/IDockControlFactory.cs b/Lattice.UI.Docking/Factories/IDockControlFactory.cs
index 2b7d8eb..0c9c8fc 100644
--- a/Lattice.UI.Docking/Factories/IDockControlFactory.cs
+++ b/Lattice.UI.Docking/Factories/IDockControlFactory.cs
@@ -1,4 +1,5 @@
-using Lattice.Core.Docking.Models;
+using Lattice.Core.Docking.Abstractions;
+using Lattice.Core.Docking.Models;
using Lattice.UI.Docking.Abstractions;
namespace Lattice.UI.Docking.Factories;
@@ -6,96 +7,82 @@ namespace Lattice.UI.Docking.Factories;
///
/// Определяет контракт для фабрики, создающей UI-контролы для элементов док-системы.
///
+///
+/// Фабрика обеспечивает абстракцию над созданием конкретных UI-контролов,
+/// что позволяет легко заменять реализации для разных платформ или тем оформления.
+///
public interface IDockControlFactory
{
///
/// Создает контрол для группы разделения.
///
/// Модель группы.
- /// Созданный контрол группы.
+ ///
+ /// Созданный контрол группы.
+ ///
+ ///
+ /// Выбрасывается, если равен null.
+ ///
IDockGroupControl CreateGroupControl(DockGroup group);
///
/// Создает контрол для контейнера вкладок.
///
/// Модель листа.
- /// Созданный контрол листа.
+ ///
+ /// Созданный контрол листа.
+ ///
+ ///
+ /// Выбрасывается, если равен null.
+ ///
IDockLeafControl CreateLeafControl(DockLeaf leaf);
///
/// Создает контрол для плавающего окна.
///
/// Модель окна.
- /// Созданный контрол окна.
+ ///
+ /// Созданный контрол окна.
+ ///
+ ///
+ /// Выбрасывается, если равен null.
+ ///
IFloatingWindowControl CreateFloatingWindowControl(DockWindow window);
///
/// Создает контрол для автоскрываемой панели.
///
/// Модель панели.
- /// Созданный контрол панели.
+ ///
+ /// Созданный контрол панели.
+ ///
+ ///
+ /// Выбрасывается, если равен null.
+ ///
IAutoHidePanelControl CreateAutoHidePanelControl(AutoHidePanel panel);
///
/// Создает контрол для разделителя.
///
/// Ориентация разделителя.
- /// Созданный контрол разделителя.
+ ///
+ /// Созданный контрол разделителя.
+ ///
IDockSplitterControl CreateSplitterControl(SplitDirection orientation);
-}
-
-///
-/// Определяет контракт для контрола разделителя.
-///
-public interface IDockSplitterControl : IDockControl
-{
- ///
- /// Получает или задает ориентацию разделителя.
- ///
- SplitDirection Orientation { get; set; }
///
- /// Получает или задает признак того, что разделитель активен.
+ /// Создает контрол для произвольного элемента док-системы.
///
- bool IsActive { get; set; }
-
- ///
- /// Событие, возникающее при начале перетаскивания разделителя.
- ///
- event EventHandler DragStarted;
-
- ///
- /// Событие, возникающее при перетаскивании разделителя.
- ///
- event EventHandler DragDelta;
-
- ///
- /// Событие, возникающее при завершении перетаскивания разделителя.
- ///
- event EventHandler DragCompleted;
-}
-
-///
-/// Аргументы события перетаскивания разделителя.
-///
-public class SplitterDraggedEventArgs : EventArgs
-{
- ///
- /// Изменение по горизонтали.
- ///
- public double HorizontalChange { get; }
-
- ///
- /// Изменение по вертикали.
- ///
- public double VerticalChange { get; }
-
- ///
- /// Инициализирует новый экземпляр класса .
- ///
- public SplitterDraggedEventArgs(double horizontalChange, double verticalChange)
- {
- HorizontalChange = horizontalChange;
- VerticalChange = verticalChange;
- }
+ /// Элемент для создания контрола.
+ ///
+ /// Созданный контрол или null, если тип элемента не поддерживается.
+ ///
+ ///
+ /// Выбрасывается, если равен null.
+ ///
+ ///
+ /// Метод использует сопоставление с шаблоном для определения типа элемента
+ /// и вызова соответствующего метода создания.
+ ///
+ IDockControl? CreateControlForElement(IDockElement element);
}
\ No newline at end of file
diff --git a/Lattice.UI.Docking/Implementations/DockControlBase.cs b/Lattice.UI.Docking/Implementations/DockControlBase.cs
new file mode 100644
index 0000000..7d4061b
--- /dev/null
+++ b/Lattice.UI.Docking/Implementations/DockControlBase.cs
@@ -0,0 +1,113 @@
+using Lattice.Core.Docking.Abstractions;
+using Lattice.Core.Docking.Engine;
+using Lattice.UI.Docking.Abstractions;
+using System.ComponentModel;
+
+namespace Lattice.UI.Docking.Implementations;
+
+///
+/// Базовая реализация контрола док-системы.
+///
+public abstract class DockControlBase : IDockControl, INotifyPropertyChanged
+{
+ ///
+ public event PropertyChangedEventHandler? PropertyChanged;
+
+ private IDockElement? _model;
+ private LayoutManager? _layoutManager;
+ private IDockContextManager? _contextManager;
+ private bool _isSelected;
+ private bool _isActive;
+
+ ///
+ public IDockElement? Model
+ {
+ get => _model;
+ set
+ {
+ if (_model != value)
+ {
+ _model = value;
+ OnPropertyChanged(nameof(Model));
+ }
+ }
+ }
+
+ ///
+ public LayoutManager? LayoutManager
+ {
+ get => _layoutManager;
+ set
+ {
+ if (_layoutManager != value)
+ {
+ _layoutManager = value;
+ OnPropertyChanged(nameof(LayoutManager));
+ }
+ }
+ }
+
+ ///
+ public IDockContextManager? ContextManager
+ {
+ get => _contextManager;
+ set
+ {
+ if (_contextManager != value)
+ {
+ _contextManager = value;
+ OnPropertyChanged(nameof(ContextManager));
+ }
+ }
+ }
+
+ ///
+ public bool IsSelected
+ {
+ get => _isSelected;
+ set
+ {
+ if (_isSelected != value)
+ {
+ _isSelected = value;
+ OnPropertyChanged(nameof(IsSelected));
+ }
+ }
+ }
+
+ ///
+ public bool IsActive
+ {
+ get => _isActive;
+ set
+ {
+ if (_isActive != value)
+ {
+ _isActive = value;
+ OnPropertyChanged(nameof(IsActive));
+ }
+ }
+ }
+
+ ///
+ public abstract void Refresh();
+
+ ///
+ public abstract void ApplyTheme(IDockTheme theme);
+
+ ///
+ public virtual void OnModelPropertyChanged(string propertyName)
+ {
+ // Базовая реализация просто обновляет весь контрол
+ Refresh();
+ }
+
+ ///
+ /// Вызывает событие .
+ ///
+ /// Имя измененного свойства.
+ protected virtual void OnPropertyChanged(string propertyName)
+ {
+ PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
+ }
+}
\ No newline at end of file
diff --git a/Lattice.UI.Docking/Lattice.UI.Docking.csproj b/Lattice.UI.Docking/Lattice.UI.Docking.csproj
index 9c29b91..e597845 100644
--- a/Lattice.UI.Docking/Lattice.UI.Docking.csproj
+++ b/Lattice.UI.Docking/Lattice.UI.Docking.csproj
@@ -9,8 +9,4 @@
-
-
-
-
diff --git a/Lattice.UI.Docking/LatticeUIFramework.cs b/Lattice.UI.Docking/LatticeUIFramework.cs
index 64c3382..fc26d27 100644
--- a/Lattice.UI.Docking/LatticeUIFramework.cs
+++ b/Lattice.UI.Docking/LatticeUIFramework.cs
@@ -1,5 +1,4 @@
-using Lattice.Core.Docking.Abstractions;
-using Lattice.Core.Docking.Engine;
+using Lattice.Core.Docking.Engine;
using Lattice.Core.Docking.Services;
using Lattice.UI.Docking.Abstractions;
using Lattice.UI.Docking.Factories;
@@ -8,14 +7,7 @@ namespace Lattice.UI.Docking;
///
/// Предоставляет статический API для инициализации и управления UI-фреймворком Lattice.
-/// Является точкой входа для интеграции док-системы в приложение и централизованным
-/// хранилищем для основных сервисов и компонентов.
///
-///
-/// Этот класс реализует шаблон Singleton для доступа к глобальным сервисам.
-/// Все компоненты инициализируются через строитель ,
-/// что обеспечивает гибкую конфигурацию и соблюдение принципа инверсии зависимостей.
-///
public static class LatticeUIFramework
{
private static bool _isInitialized;
@@ -24,22 +16,11 @@ public static class LatticeUIFramework
///
/// Получает значение, указывающее, инициализирован ли фреймворк.
///
- ///
- /// true, если фреймворк был инициализирован вызовом ;
- /// в противном случае — false.
- ///
public static bool IsInitialized => _isInitialized;
///
/// Получает текущий строитель конфигурации фреймворка.
///
- ///
- /// Экземпляр , используемый для настройки фреймворка.
- /// Возвращает null, если фреймворк не инициализирован.
- ///
- ///
- /// Выбрасывается при попытке доступа к свойству до инициализации фреймворка.
- ///
public static LatticeBuilder CurrentBuilder
{
get
@@ -53,67 +34,31 @@ public static class LatticeUIFramework
///
/// Получает менеджер макета из текущего строителя.
///
- ///
- /// Экземпляр , управляющий структурой док-системы.
- ///
public static LayoutManager? LayoutManager => _currentBuilder?.LayoutManager;
///
/// Получает реестр контента из текущего строителя.
///
- ///
- /// Экземпляр , содержащий зарегистрированные типы контента.
- ///
public static ContentRegistry? ContentRegistry => _currentBuilder?.ContentRegistry;
///
/// Получает фабрику контролов из текущего строителя.
///
- ///
- /// Экземпляр для создания UI-контролов.
- ///
public static IDockControlFactory? ControlFactory => _currentBuilder?.ControlFactory;
- ///
- /// Получает сервис перетаскивания из текущего строителя.
- ///
- ///
- /// Экземпляр для управления операциями drag-and-drop.
- ///
- public static IDockDragDropService? DragDropService => _currentBuilder?.DragDropService;
-
///
/// Получает менеджер контекстных меню из текущего строителя.
///
- ///
- /// Экземпляр для управления контекстными меню.
- ///
public static IDockContextManager? ContextManager => _currentBuilder?.ContextManager;
///
/// Получает UI-сервис из текущего строителя.
///
- ///
- /// Экземпляр для выполнения платформенно-зависимых операций.
- ///
public static IDockUIService? UIService => _currentBuilder?.UIService;
///
/// Инициализирует фреймворк Lattice с указанными параметрами.
///
- ///
- /// Настройки инициализации. Если null, используются параметры по умолчанию.
- ///
- ///
- /// Экземпляр для дальнейшей конфигурации фреймворка.
- ///
- ///
- /// Выбрасывается, если фреймворк уже инициализирован.
- ///
- ///
- /// Этот метод должен вызываться один раз при запуске приложения, перед любыми
- /// попытками использования компонентов док-системы.
- ///
public static LatticeBuilder Initialize(LatticeOptions? options = null)
{
if (_isInitialized)
@@ -121,11 +66,9 @@ public static class LatticeUIFramework
options ??= new LatticeOptions();
- // Создаем основные компоненты Core-слоя
var layoutManager = new LayoutManager();
var contentRegistry = new ContentRegistry();
- // Создаем строитель с основными компонентами
_currentBuilder = new LatticeBuilder(layoutManager, contentRegistry, options);
_isInitialized = true;
@@ -135,10 +78,6 @@ public static class LatticeUIFramework
///
/// Сбрасывает состояние фреймворка к неинициализированному.
///
- ///
- /// Используется в основном для целей тестирования. В рабочем приложении
- /// фреймворк должен инициализироваться один раз на протяжении жизненного цикла.
- ///
public static void Reset()
{
_isInitialized = false;
@@ -148,54 +87,35 @@ public static class LatticeUIFramework
///
/// Представляет настройки инициализации фреймворка Lattice.
-/// Позволяет кастомизировать поведение системы при запуске.
///
public class LatticeOptions
{
///
/// Получает или задает значение, указывающее, следует ли автоматически
- /// регистрировать стандартные команды (закрыть, сделать плавающим и т.д.).
+ /// регистрировать стандартные команды.
///
- ///
- /// true, чтобы зарегистрировать стандартные команды; в противном случае — false.
- /// Значение по умолчанию: true.
- ///
public bool RegisterDefaultCommands { get; set; } = true;
///
/// Получает или задает значение, указывающее, следует ли автоматически
/// создавать сервисы при их первом запросе.
///
- ///
- /// true, чтобы автоматически создавать сервисы; в противном случае — false.
- /// Значение по умолчанию: true.
- ///
public bool AutoCreateServices { get; set; } = true;
///
- /// Получает или задает идентификатор приложения, используемый при
- /// сериализации макета для различения конфигураций разных приложений.
+ /// Получает или задает идентификатор приложения.
///
- ///
- /// Строковый идентификатор приложения или null, если идентификатор не задан.
- ///
public string? ApplicationId { get; set; }
///
/// Получает или задает значение, указывающее, следует ли включить
/// расширенное логирование операций системы.
///
- ///
- /// true, чтобы включить подробное логирование; в противном случае — false.
- /// Значение по умолчанию: false.
- ///
public bool EnableVerboseLogging { get; set; } = false;
}
///
/// Предоставляет fluent-интерфейс для конфигурации фреймворка Lattice.
-/// Инкапсулирует процесс настройки всех компонентов системы и обеспечивает
-/// согласованное состояние после инициализации.
///
public sealed class LatticeBuilder
{
@@ -203,120 +123,59 @@ public sealed class LatticeBuilder
private readonly ContentRegistry _contentRegistry;
private readonly LatticeOptions _options;
private IDockControlFactory? _controlFactory;
- private IDockDragDropService? _dragDropService;
private IDockContextManager? _contextManager;
private IDockUIService? _uiService;
private bool _isBuilt;
///
- /// Получает менеджер макета, связанный с этим строителем.
+ /// Получает менеджер макета.
///
- ///
- /// Экземпляр для управления структурой док-системы.
- ///
public LayoutManager LayoutManager => _layoutManager;
///
- /// Получает реестр контента, связанный с этим строителем.
+ /// Получает реестр контента.
///
- ///
- /// Экземпляр для регистрации типов контента.
- ///
public ContentRegistry ContentRegistry => _contentRegistry;
///
- /// Получает фабрику контролов, связанную с этим строителем.
+ /// Получает фабрику контролов.
///
- ///
- /// Экземпляр или null, если фабрика не задана.
- ///
public IDockControlFactory? ControlFactory => _controlFactory;
///
- /// Получает сервис перетаскивания, связанный с этим строителем.
+ /// Получает менеджер контекстных меню.
///
- ///
- /// Экземпляр или null, если сервис не задан.
- ///
- public IDockDragDropService? DragDropService => _dragDropService;
-
- ///
- /// Получает менеджер контекстных меню, связанный с этим строителем.
- ///
- ///
- /// Экземпляр или null, если менеджер не задан.
- ///
public IDockContextManager? ContextManager => _contextManager;
///
- /// Получает UI-сервис, связанный с этим строителем.
+ /// Получает UI-сервис.
///
- ///
- /// Экземпляр или null, если сервис не задан.
- ///
public IDockUIService? UIService => _uiService;
///
/// Инициализирует новый экземпляр класса .
///
- /// Менеджер макета.
- /// Реестр контента.
- /// Опции инициализации.
internal LatticeBuilder(LayoutManager layoutManager, ContentRegistry contentRegistry, LatticeOptions options)
{
_layoutManager = layoutManager ?? throw new ArgumentNullException(nameof(layoutManager));
_contentRegistry = contentRegistry ?? throw new ArgumentNullException(nameof(contentRegistry));
_options = options ?? throw new ArgumentNullException(nameof(options));
+
+ _layoutManager.ContentRegistry = contentRegistry;
}
///
/// Регистрирует фабрику контролов для создания UI-элементов.
///
- ///
- /// Фабрика контролов, реализующая .
- ///
- ///
- /// Текущий экземпляр строителя для цепочки вызовов.
- ///
- ///
- /// Выбрасывается, если равен null.
- ///
public LatticeBuilder WithControlFactory(IDockControlFactory factory)
{
_controlFactory = factory ?? throw new ArgumentNullException(nameof(factory));
return this;
}
- ///
- /// Регистрирует сервис перетаскивания для управления операциями drag-and-drop.
- ///
- ///
- /// Сервис перетаскивания, реализующий .
- ///
- ///
- /// Текущий экземпляр строителя для цепочки вызовов.
- ///
- ///
- /// Выбрасывается, если равен null.
- ///
- public LatticeBuilder WithDragDropService(IDockDragDropService service)
- {
- _dragDropService = service ?? throw new ArgumentNullException(nameof(service));
- return this;
- }
-
///
/// Регистрирует менеджер контекстных меню для управления контекстными действиями.
///
- ///
- /// Менеджер контекстных меню, реализующий .
- ///
- ///
- /// Текущий экземпляр строителя для цепочки вызовов.
- ///
- ///
- /// Выбрасывается, если равен null.
- ///
public LatticeBuilder WithContextManager(IDockContextManager manager)
{
_contextManager = manager ?? throw new ArgumentNullException(nameof(manager));
@@ -326,15 +185,6 @@ public sealed class LatticeBuilder
///
/// Регистрирует UI-сервис для выполнения платформенно-зависимых операций.
///
- ///
- /// UI-сервис, реализующий .
- ///
- ///
- /// Текущий экземпляр строителя для цепочки вызовов.
- ///
- ///
- /// Выбрасывается, если равен null.
- ///
public LatticeBuilder WithUIService(IDockUIService service)
{
_uiService = service ?? throw new ArgumentNullException(nameof(service));
@@ -342,20 +192,8 @@ public sealed class LatticeBuilder
}
///
- /// Регистрирует тип контента в реестре для последующего создания экземпляров.
+ /// Регистрирует тип контента в реестре.
///
- ///
- /// Тип контента, реализующий .
- ///
- /// Уникальный идентификатор типа контента.
- /// Фабричный метод для создания экземпляров контента.
- /// Метаданные типа контента (опционально).
- ///
- /// Текущий экземпляр строителя для цепочки вызовов.
- ///
- ///
- /// Выбрасывается, если или равны null.
- ///
public LatticeBuilder RegisterContentType(string contentTypeId, Func factory, ContentMetadata? metadata = null)
where T : Core.Docking.Abstractions.IDockContent
{
@@ -367,73 +205,76 @@ public sealed class LatticeBuilder
}
///
- /// Завершает конфигурацию и создает готовый к использованию док-хост.
+ /// Завершает конфигурацию фреймворка.
///
- ///
- /// Экземпляр , настроенный в соответствии с текущей конфигурацией.
- ///
- ///
- /// Выбрасывается, если не задана фабрика контролов или метод уже был вызван.
- ///
- public IDockHost Build()
+ public ILatticeFramework Build()
{
if (_isBuilt)
- throw new InvalidOperationException("Builder has already been built.");
+ throw new InvalidOperationException("Framework has already been built.");
- if (_controlFactory == null)
- throw new InvalidOperationException("Control factory must be specified. Call WithControlFactory() first.");
-
- // Автоматически создаем отсутствующие сервисы, если включена опция
- if (_options.AutoCreateServices)
- {
- _dragDropService ??= CreateDefaultDragDropService();
- _contextManager ??= CreateDefaultContextManager();
- _uiService ??= CreateDefaultUIService();
- }
-
- // Создаем хост через фабрику
- // (предполагается, что фабрика имеет метод CreateDockHost)
- if (_controlFactory is WinUI.Factories.WinUIDockControlFactory winUIFactory)
- {
- var host = winUIFactory.CreateDockHost();
-
- // Настраиваем хост
- host.LayoutManager = _layoutManager;
- host.DragDropService = _dragDropService;
- host.ContextManager = _contextManager;
-
- _isBuilt = true;
- return host;
- }
-
- throw new NotSupportedException($"Control factory of type {_controlFactory.GetType().Name} is not supported.");
+ _isBuilt = true;
+ return new LatticeFramework(this);
}
+}
+
+///
+/// Предоставляет интерфейс для доступа к компонентам фреймворка Lattice.
+///
+public interface ILatticeFramework
+{
+ ///
+ /// Получает менеджер макета.
+ ///
+ LayoutManager LayoutManager { get; }
///
- /// Создает сервис перетаскивания по умолчанию.
+ /// Получает реестр контента.
///
- private IDockDragDropService CreateDefaultDragDropService()
- {
- // Реализация зависит от платформы
- // В реальном коде здесь должна быть проверка платформы
- return new WinUI.Services.WinUIDragDropService();
- }
+ ContentRegistry ContentRegistry { get; }
///
- /// Создает менеджер контекстных меню по умолчанию.
+ /// Получает фабрику контролов.
///
- private IDockContextManager CreateDefaultContextManager()
- {
- // Реализация зависит от платформы
- return new WinUI.Services.WinUIDockContextManager();
- }
+ IDockControlFactory? ControlFactory { get; }
///
- /// Создает UI-сервис по умолчанию.
+ /// Получает менеджер контекстных меню.
///
- private IDockUIService CreateDefaultUIService()
+ IDockContextManager? ContextManager { get; }
+
+ ///
+ /// Получает UI-сервис.
+ ///
+ IDockUIService? UIService { get; }
+}
+
+///
+/// Реализация интерфейса .
+///
+internal class LatticeFramework : ILatticeFramework
+{
+ private readonly LatticeBuilder _builder;
+
+ ///
+ /// Инициализирует новый экземпляр класса .
+ ///
+ public LatticeFramework(LatticeBuilder builder)
{
- // Реализация зависит от платформы
- return new WinUI.Services.WinUIDockUIService();
+ _builder = builder ?? throw new ArgumentNullException(nameof(builder));
}
+
+ ///
+ public LayoutManager LayoutManager => _builder.LayoutManager;
+
+ ///
+ public ContentRegistry ContentRegistry => _builder.ContentRegistry;
+
+ ///
+ public IDockControlFactory? ControlFactory => _builder.ControlFactory;
+
+ ///
+ public IDockContextManager? ContextManager => _builder.ContextManager;
+
+ ///
+ public IDockUIService? UIService => _builder.UIService;
}
\ No newline at end of file
diff --git a/Lattice.UI.Docking/Models/UiDragDropModels.cs b/Lattice.UI.Docking/Models/UiDragDropModels.cs
deleted file mode 100644
index 9ae04ad..0000000
--- a/Lattice.UI.Docking/Models/UiDragDropModels.cs
+++ /dev/null
@@ -1,148 +0,0 @@
-using Lattice.Core.Geometry;
-using Lattice.UI.Docking.Abstractions;
-
-namespace Lattice.UI.Docking.Models;
-
-///
-/// Расширенная информация о перетаскивании для UI-слоя.
-/// Добавляет визуальные аспекты и UI-контекст к базовой информации о перетаскивании.
-///
-public class UiDragInfo
-{
- ///
- /// Базовые данные перетаскивания.
- ///
- public Core.DragDrop.Models.DragInfo BaseDragInfo { get; }
-
- ///
- /// UI-контрол, который является источником перетаскивания.
- ///
- public IDockControl? SourceControl { get; }
-
- ///
- /// Визуальное представление перетаскиваемого элемента.
- ///
- public object? DragVisual { get; set; }
-
- ///
- /// Смещение курсора относительно элемента при начале перетаскивания.
- ///
- public Point VisualOffset { get; set; }
-
- ///
- /// Размер визуального представления.
- ///
- public Size VisualSize { get; set; }
-
- ///
- /// Прозрачность визуального представления.
- ///
- public double VisualOpacity { get; set; } = 0.7;
-
- ///
- /// Инициализирует новый экземпляр .
- ///
- public UiDragInfo(Core.DragDrop.Models.DragInfo baseDragInfo, IDockControl? sourceControl = null)
- {
- BaseDragInfo = baseDragInfo;
- SourceControl = sourceControl;
- }
-}
-
-///
-/// Расширенная информация о сбросе для UI-слоя.
-/// Добавляет визуальные подсказки и UI-контекст.
-///
-public class UiDropInfo
-{
- ///
- /// Базовые данные сброса.
- ///
- public Core.DragDrop.Models.DropInfo BaseDropInfo { get; }
-
- ///
- /// UI-контрол, который является целью сброса.
- ///
- public IDockControl? TargetControl { get; }
-
- ///
- /// Позиция сброса относительно элемента.
- ///
- public DropPosition DropPosition { get; set; }
-
- ///
- /// Визуальная подсказка для области сброса.
- ///
- public object? DropHintVisual { get; set; }
-
- ///
- /// Признак того, что курсор находится над допустимой областью сброса.
- ///
- public bool IsOverValidTarget { get; set; }
-
- ///
- /// Интенсивность подсветки области сброса (0.0 - 1.0).
- ///
- public double HighlightIntensity { get; set; }
-
- ///
- /// Инициализирует новый экземпляр .
- ///
- public UiDropInfo(Core.DragDrop.Models.DropInfo baseDropInfo, IDockControl? targetControl = null)
- {
- BaseDropInfo = baseDropInfo;
- TargetControl = targetControl;
- DropPosition = DropPosition.Center;
- }
-}
-
-///
-/// Определяет позицию сброса относительно элемента.
-///
-public enum DropPosition
-{
- /// Слева от элемента.
- Left,
-
- /// Справа от элемента.
- Right,
-
- /// Сверху от элемента.
- Top,
-
- /// Снизу от элемента.
- Bottom,
-
- /// В центре элемента (для объединения вкладок).
- Center,
-
- /// В виде новой вкладки.
- Tab
-}
-
-
-public class DragStartedEventArgs : EventArgs
-{
- public IDockControl? Source { get; }
- public Core.DragDrop.Models.DragInfo DragInfo { get; }
- // ... конструктор
-}
-
-public class DragUpdatedEventArgs : EventArgs
-{
- public IDockControl? Source { get; }
- public double X { get; }
- public double Y { get; }
- public Core.DragDrop.Models.DragInfo DragInfo { get; }
- // ... конструктор
-}
-
-public class DragCompletedEventArgs : EventArgs
-{
- public IDockControl? Source { get; }
- public IDockControl? Target { get; }
- public Models.DropPosition DropPosition { get; }
- public Core.DragDrop.Models.DragInfo? DragInfo { get; }
- public bool Success { get; }
- // ... конструктор
-}
\ No newline at end of file
diff --git a/Lattice.UI.Docking/Services/DockDragDropService.cs b/Lattice.UI.Docking/Services/DockDragDropService.cs
deleted file mode 100644
index 6d98fd3..0000000
--- a/Lattice.UI.Docking/Services/DockDragDropService.cs
+++ /dev/null
@@ -1,417 +0,0 @@
-using Lattice.Core.DragDrop.Abstractions;
-using Lattice.Core.DragDrop.Services;
-using Lattice.UI.Docking.Abstractions;
-using Lattice.UI.Docking.Models;
-
-namespace Lattice.UI.Docking.Services;
-
-///
-/// Реализация сервиса перетаскивания для UI-слоя док-системы.
-/// Координирует взаимодействие между базовым менеджером перетаскивания
-/// и UI-контролами, обеспечивая визуальную обратную связь.
-///
-public class DockDragDropService : IDockDragDropService
-{
- private readonly DragDropManagerEx _dragDropManager;
- private readonly Dictionary _registeredDragSources = new();
- private readonly Dictionary _registeredDropTargets = new();
- private UiDragInfo? _currentUiDragInfo;
- private UiDropInfo? _currentUiDropInfo;
- private IDropTarget? _lastDropTarget;
-
- ///
- /// Инициализирует новый экземпляр .
- ///
- public DockDragDropService()
- {
- _dragDropManager = new DragDropManagerEx();
- HookEvents();
- }
-
- ///
- /// Инициализирует новый экземпляр с указанным менеджером перетаскивания.
- ///
- public DockDragDropService(DragDropManagerEx dragDropManager)
- {
- _dragDropManager = dragDropManager;
- HookEvents();
- }
-
- private void HookEvents()
- {
- _dragDropManager.DragStarted += OnDragStarted;
- _dragDropManager.DragUpdated += OnDragUpdated;
- _dragDropManager.DragCompleted += OnDragCompleted;
- _dragDropManager.DragCancelled += OnDragCancelled;
- _dragDropManager.DropTargetChanged += OnDropTargetChanged;
- }
-
- ///
- public void RegisterDragSource(IDockControl element, IDragSource dragSource)
- {
- if (element == null) throw new ArgumentNullException(nameof(element));
- if (dragSource == null) throw new ArgumentNullException(nameof(dragSource));
-
- _registeredDragSources[element] = dragSource;
-
- // Регистрируем границы элемента в менеджере
- var bounds = CalculateBounds(element);
- _dragDropManager.RegisterDropTarget(dragSource as IDropTarget ?? new AdapterDropTarget(dragSource),
- bounds, 0, element.GetType().Name);
- }
-
- ///
- public void RegisterDropTarget(IDockControl element, IDropTarget dropTarget)
- {
- if (element == null) throw new ArgumentNullException(nameof(element));
- if (dropTarget == null) throw new ArgumentNullException(nameof(dropTarget));
-
- _registeredDropTargets[element] = dropTarget;
-
- var bounds = CalculateBounds(element);
- _dragDropManager.RegisterDropTarget(dropTarget, bounds, 0, element.GetType().Name);
- }
-
- ///
- public void UnregisterDragSource(IDockControl element)
- {
- if (element == null) throw new ArgumentNullException(nameof(element));
-
- _registeredDragSources.Remove(element);
- // TODO: Реализовать отмену регистрации в менеджере
- }
-
- ///
- public void UnregisterDropTarget(IDockControl element)
- {
- if (element == null) throw new ArgumentNullException(nameof(element));
-
- _registeredDropTargets.Remove(element);
- // TODO: Реализовать отмену регистрации в менеджере
- }
-
- ///
- public void StartDrag(IDockControl element, Core.DragDrop.Models.DragInfo dragInfo)
- {
- if (element == null) throw new ArgumentNullException(nameof(element));
- if (dragInfo == null) throw new ArgumentNullException(nameof(dragInfo));
-
- if (_registeredDragSources.TryGetValue(element, out var dragSource))
- {
- _currentUiDragInfo = new UiDragInfo(dragInfo, element);
- _dragDropManager.StartDrag(dragSource, dragInfo.StartPosition);
- }
- }
-
- ///
- public void UpdateDragVisual(double x, double y)
- {
- var position = new Core.DragDrop.Geometry.Point(x, y);
- _dragDropManager.UpdateDrag(position);
-
- if (_currentUiDragInfo != null)
- {
- // Обновляем позицию визуального представления
- OnDragVisualUpdated?.Invoke(this, new DragVisualUpdatedEventArgs(
- _currentUiDragInfo, position));
- }
- }
-
- ///
- public void EndDrag(double x, double y)
- {
- var position = new Core.DragDrop.Geometry.Point(x, y);
- _dragDropManager.EndDrag(position);
- }
-
- ///
- public void CancelDrag()
- {
- _dragDropManager.CancelDrag();
- }
-
- ///
- public void ShowDropHint(IDockControl element, DropPosition position)
- {
- if (_currentUiDropInfo != null)
- {
- _currentUiDropInfo.DropPosition = position;
- _currentUiDropInfo.IsOverValidTarget = true;
- _currentUiDropInfo.HighlightIntensity = 0.8;
-
- OnDropHintChanged?.Invoke(this, new DropHintEventArgs(
- element, position, true, _currentUiDropInfo.HighlightIntensity));
- }
- }
-
- ///
- public void HideDropHint()
- {
- if (_currentUiDropInfo != null)
- {
- _currentUiDropInfo.IsOverValidTarget = false;
- _currentUiDropInfo.HighlightIntensity = 0.0;
-
- OnDropHintChanged?.Invoke(this, new DropHintEventArgs(
- _currentUiDropInfo.TargetControl,
- _currentUiDropInfo.DropPosition,
- false,
- 0.0));
- }
- }
-
- ///
- public event EventHandler? DragStarted;
-
- ///
- public event EventHandler? DragUpdated;
-
- ///
- public event EventHandler? DragCompleted;
-
- ///
- public event EventHandler? DragCancelled;
-
- ///
- /// Событие, возникающее при обновлении визуального представления перетаскивания.
- ///
- public event EventHandler? OnDragVisualUpdated;
-
- ///
- /// Событие, возникающее при изменении визуальной подсказки сброса.
- ///
- public event EventHandler? OnDropHintChanged;
-
- private void OnDragStarted(object? sender, DragStartedEventArgs e)
- {
- // Обновляем UI-информацию
- if (_currentUiDragInfo != null)
- {
- _currentUiDragInfo.BaseDragInfo.StartPosition = e.StartPosition;
-
- // Создаем визуальное представление
- CreateDragVisual(_currentUiDragInfo);
-
- DragStarted?.Invoke(this, new DragStartedEventArgs(
- _currentUiDragInfo.SourceControl,
- _currentUiDragInfo.BaseDragInfo));
- }
- }
-
- private void OnDragUpdated(object? sender, DragUpdatedEventArgs e)
- {
- if (_currentUiDragInfo != null)
- {
- // Обновляем позицию визуального представления
- UpdateDragVisualPosition(e.Position);
-
- DragUpdated?.Invoke(this, new DragUpdatedEventArgs(
- _currentUiDragInfo.SourceControl,
- e.Position.X,
- e.Position.Y,
- _currentUiDragInfo.BaseDragInfo));
- }
- }
-
- private void OnDragCompleted(object? sender, DragCompletedEventArgs e)
- {
- var targetControl = _currentUiDropInfo?.TargetControl;
- var dropPosition = _currentUiDropInfo?.DropPosition ?? DropPosition.Center;
-
- DragCompleted?.Invoke(this, new DragCompletedEventArgs(
- _currentUiDragInfo?.SourceControl,
- targetControl,
- dropPosition,
- _currentUiDragInfo?.BaseDragInfo,
- e.Effects != Core.DragDrop.Enums.DragDropEffects.None));
-
- // Очищаем визуальные представления
- CleanupDragVisual();
- CleanupDropHint();
-
- _currentUiDragInfo = null;
- _currentUiDropInfo = null;
- _lastDropTarget = null;
- }
-
- private void OnDragCancelled(object? sender, DragCancelledEventArgs e)
- {
- DragCancelled?.Invoke(this, EventArgs.Empty);
-
- CleanupDragVisual();
- CleanupDropHint();
-
- _currentUiDragInfo = null;
- _currentUiDropInfo = null;
- _lastDropTarget = null;
- }
-
- private void OnDropTargetChanged(object? sender, DropTargetChangedEventArgs e)
- {
- var dropTarget = e.Target;
-
- // Обновляем UI-информацию о сбросе
- if (dropTarget != null)
- {
- // Находим соответствующий UI-контрол
- var targetControl = _registeredDropTargets
- .FirstOrDefault(kv => kv.Value == dropTarget)
- .Key;
-
- _currentUiDropInfo = new UiDropInfo(
- new Core.DragDrop.Models.DropInfo(
- _dragDropManager.CurrentDragInfo?.Data,
- e.TargetBounds.Center,
- _dragDropManager.CurrentDragInfo?.AllowedEffects ?? Core.DragDrop.Enums.DragDropEffects.None,
- dropTarget),
- targetControl);
-
- _currentUiDropInfo.DropPosition = CalculateDropPosition(
- _currentUiDragInfo?.BaseDragInfo.StartPosition ?? Core.DragDrop.Geometry.Point.Zero,
- e.TargetBounds);
- }
- else
- {
- _currentUiDropInfo = null;
- }
-
- // Уведомляем об изменении цели сброса
- if (_lastDropTarget != dropTarget)
- {
- if (_lastDropTarget != null)
- {
- HideDropHint();
- }
-
- if (dropTarget != null && _currentUiDropInfo != null)
- {
- ShowDropHint(_currentUiDropInfo.TargetControl, _currentUiDropInfo.DropPosition);
- }
-
- _lastDropTarget = dropTarget;
- }
- }
-
- private Core.DragDrop.Geometry.Rect CalculateBounds(IDockControl element)
- {
- // В UI-реализациях этот метод должен быть переопределен
- // для вычисления реальных границ элемента на экране
- return new Core.DragDrop.Geometry.Rect(0, 0, 100, 100);
- }
-
- private DropPosition CalculateDropPosition(Core.DragDrop.Geometry.Point cursorPos, Core.DragDrop.Geometry.Rect targetBounds)
- {
- // Простая логика определения позиции сброса
- var center = targetBounds.Center;
- var relativeX = (cursorPos.X - targetBounds.X) / targetBounds.Width;
- var relativeY = (cursorPos.Y - targetBounds.Y) / targetBounds.Height;
-
- if (relativeX < 0.25) return DropPosition.Left;
- if (relativeX > 0.75) return DropPosition.Right;
- if (relativeY < 0.25) return DropPosition.Top;
- if (relativeY > 0.75) return DropPosition.Bottom;
-
- return DropPosition.Center;
- }
-
- private void CreateDragVisual(UiDragInfo dragInfo)
- {
- // В UI-реализациях этот метод должен создавать визуальное представление
- OnDragVisualUpdated?.Invoke(this, new DragVisualUpdatedEventArgs(
- dragInfo, dragInfo.BaseDragInfo.StartPosition));
- }
-
- private void UpdateDragVisualPosition(Core.DragDrop.Geometry.Point position)
- {
- if (_currentUiDragInfo != null)
- {
- OnDragVisualUpdated?.Invoke(this, new DragVisualUpdatedEventArgs(
- _currentUiDragInfo, position));
- }
- }
-
- private void CleanupDragVisual()
- {
- // В UI-реализациях этот метод должен очищать визуальное представление
- OnDragVisualUpdated?.Invoke(this, new DragVisualUpdatedEventArgs(null, Core.DragDrop.Geometry.Point.Zero));
- }
-
- private void CleanupDropHint()
- {
- HideDropHint();
- }
-}
-
-///
-/// Адаптер для преобразования IDragSource в IDropTarget.
-///
-internal class AdapterDropTarget : IDropTarget
-{
- private readonly IDragSource _dragSource;
-
- public AdapterDropTarget(IDragSource dragSource)
- {
- _dragSource = dragSource;
- }
-
- public bool CanAcceptDrop(Core.DragDrop.Models.DropInfo dropInfo) => false;
- public void DragOver(Core.DragDrop.Models.DropInfo dropInfo) { }
- public void Drop(Core.DragDrop.Models.DropInfo dropInfo) { }
- public void DragLeave() { }
-}
-
-///
-/// Аргументы события обновления визуального представления перетаскивания.
-///
-public class DragVisualUpdatedEventArgs : EventArgs
-{
- ///
- /// Информация о перетаскивании.
- ///
- public UiDragInfo? DragInfo { get; }
-
- ///
- /// Текущая позиция.
- ///
- public Core.DragDrop.Geometry.Point Position { get; }
-
- public DragVisualUpdatedEventArgs(UiDragInfo? dragInfo, Core.DragDrop.Geometry.Point position)
- {
- DragInfo = dragInfo;
- Position = position;
- }
-}
-
-///
-/// Аргументы события изменения визуальной подсказки сброса.
-///
-public class DropHintEventArgs : EventArgs
-{
- ///
- /// Целевой элемент.
- ///
- public IDockControl? Target { get; }
-
- ///
- /// Позиция сброса.
- ///
- public DropPosition Position { get; }
-
- ///
- /// Показывает, видима ли подсказка.
- ///
- public bool IsVisible { get; }
-
- ///
- /// Интенсивность подсветки.
- ///
- public double Intensity { get; }
-
- public DropHintEventArgs(IDockControl? target, DropPosition position, bool isVisible, double intensity)
- {
- Target = target;
- Position = position;
- IsVisible = isVisible;
- Intensity = intensity;
- }
-}
\ No newline at end of file
diff --git a/Lattice.UI.Docking/Services/DockUIServiceBase.cs b/Lattice.UI.Docking/Services/DockUIServiceBase.cs
index 91cacaa..014e589 100644
--- a/Lattice.UI.Docking/Services/DockUIServiceBase.cs
+++ b/Lattice.UI.Docking/Services/DockUIServiceBase.cs
@@ -1,4 +1,5 @@
-using Lattice.UI.Docking.Abstractions;
+// Lattice.UI.Docking\Services\DockUIServiceBase.cs
+using Lattice.UI.Docking.Abstractions;
namespace Lattice.UI.Docking.Services;
@@ -7,107 +8,21 @@ namespace Lattice.UI.Docking.Services;
///
public abstract class DockUIServiceBase : IDockUIService
{
- private IDockTheme? _currentTheme;
-
///
public abstract object CreateMainWindow(IDockHost host);
///
- public virtual bool? ShowDialog(string title, object content)
- {
- // Базовая реализация - просто возвращает null
- // В производных классах должна быть реальная реализация
- return null;
- }
+ public abstract bool? ShowDialog(string title, object content);
///
- public virtual void ShowMessage(string message, string caption)
- {
- // Базовая реализация не делает ничего
- // В производных классах должна быть реальная реализация
- }
+ public abstract void ShowMessage(string message, string caption);
///
- public virtual bool Confirm(string message, string caption)
- {
- // Базовая реализация всегда возвращает true
- // В производных классах должна быть реальная реализация
- return true;
- }
+ public abstract bool Confirm(string message, string caption);
///
- public virtual string? Prompt(string prompt, string? defaultValue = null)
- {
- // Базовая реализация возвращает значение по умолчанию
- // В производных классах должна быть реальная реализация
- return defaultValue;
- }
+ public abstract string? Prompt(string prompt, string? defaultValue = null);
///
- public virtual void InvokeOnUIThread(Action action)
- {
- // Базовая реализация просто выполняет действие
- // В производных классах должна быть синхронизация с UI-потоком
- action?.Invoke();
- }
-
- ///
- public virtual IDockTheme GetCurrentTheme()
- {
- return _currentTheme ?? CreateDefaultTheme();
- }
-
- ///
- public virtual void SetTheme(IDockTheme theme)
- {
- _currentTheme = theme;
- theme.Apply();
- }
-
- ///
- /// Создает тему по умолчанию.
- ///
- protected virtual IDockTheme CreateDefaultTheme()
- {
- return new DefaultDockTheme();
- }
-}
-
-///
-/// Тема оформления по умолчанию.
-///
-public class DefaultDockTheme : IDockTheme
-{
- public string Name => "Default";
- public string BackgroundColor { get; set; } = "#1E1E1E";
- public string PanelBackgroundColor { get; set; } = "#252526";
- public string TabBackgroundColor { get; set; } = "#2D2D2D";
- public string ActiveTabBackgroundColor { get; set; } = "#3E3E3E";
- public string BorderColor { get; set; } = "#3F3F46";
- public string SplitterColor { get; set; } = "#2D2D2D";
- public string TextColor { get; set; } = "#CCCCCC";
- public string AccentColor { get; set; } = "#007ACC";
- public double CornerRadius { get; set; } = 3.0;
- public double BorderThickness { get; set; } = 1.0;
- public double SplitterWidth { get; set; } = 4.0;
-
- public void Apply()
- {
- // В UI-реализациях этот метод должен применять тему к элементам
- }
-
- public void Reset()
- {
- BackgroundColor = "#1E1E1E";
- PanelBackgroundColor = "#252526";
- TabBackgroundColor = "#2D2D2D";
- ActiveTabBackgroundColor = "#3E3E3E";
- BorderColor = "#3F3F46";
- SplitterColor = "#2D2D2D";
- TextColor = "#CCCCCC";
- AccentColor = "#007ACC";
- CornerRadius = 3.0;
- BorderThickness = 1.0;
- SplitterWidth = 4.0;
- }
+ public abstract void InvokeOnUIThread(Action action);
}
\ No newline at end of file
diff --git a/Lattice.UI.Docking/Utilities/DockUtilities.cs b/Lattice.UI.Docking/Utilities/DockUtilities.cs
index 52d0ea3..59b9c02 100644
--- a/Lattice.UI.Docking/Utilities/DockUtilities.cs
+++ b/Lattice.UI.Docking/Utilities/DockUtilities.cs
@@ -34,21 +34,16 @@ public static class DockUtilities
var control = factory.CreateControlForElement(element);
if (control == null) return null;
- // Устанавливаем родительский контрол
if (parentControl != null)
{
// Здесь может быть установка дополнительных связей
}
- // Рекурсивно создаем дочерние контролы
- if (element is DockGroup group)
+ if (element is DockGroup group && control is IDockGroupControl groupControl)
{
- if (control is IDockGroupControl groupControl)
- {
- var firstChild = CreateControlForElement(group.First, factory, control);
- var secondChild = CreateControlForElement(group.Second, factory, control);
- groupControl.SetChildren(firstChild, secondChild);
- }
+ var firstChild = CreateControlForElement(group.First, factory, control);
+ var secondChild = CreateControlForElement(group.Second, factory, control);
+ groupControl.SetChildren(firstChild, secondChild);
}
return control;
@@ -57,10 +52,9 @@ public static class DockUtilities
///
/// Находит контрол для указанного элемента в дереве контролов.
///
- public static IDockControl? FindControlForElement(IDockControl root, IDockElement element)
+ public static IDockControl? FindControlForElement(IDockControl? root, IDockElement element)
{
- if (root == null) throw new ArgumentNullException(nameof(root));
- if (element == null) throw new ArgumentNullException(nameof(element));
+ if (root == null || element == null) return null;
if (root.Model?.Id == element.Id)
return root;
@@ -88,6 +82,5 @@ public static class DockUtilities
if (factory == null) throw new ArgumentNullException(nameof(factory));
// TODO: Реализовать эффективное обновление дерева контролов
- // вместо полной перестройки
}
}
\ No newline at end of file
diff --git a/Lattice.UI.DragDrop.WinUI/Behaviors/WinUIDragSourceBehavior.cs b/Lattice.UI.DragDrop.WinUI/Behaviors/WinUIDragSourceBehavior.cs
deleted file mode 100644
index 6a35e8a..0000000
--- a/Lattice.UI.DragDrop.WinUI/Behaviors/WinUIDragSourceBehavior.cs
+++ /dev/null
@@ -1,301 +0,0 @@
-using Lattice.Core.DragDrop.Models;
-using Lattice.Core.DragDrop.Services;
-using Lattice.Core.Geometry;
-using Lattice.UI.DragDrop.Abstractions;
-using Lattice.UI.DragDrop.Behaviors;
-using Lattice.UI.DragDrop.WinUI.Services;
-using Microsoft.UI.Xaml;
-using Microsoft.UI.Xaml.Input;
-using System;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace Lattice.UI.DragDrop.WinUI.Behaviors;
-
-///
-/// Реализация поведения источника перетаскивания для элементов WinUI.
-/// Наследуется от для использования
-/// общей логики управления операциями перетаскивания и интеграции с системой .
-///
-///
-///
-/// Этот класс предоставляет конкретную реализацию для платформы WinUI, обрабатывая события
-/// указателя (мышь, тач, перо) и преобразуя их в операции перетаскивания через центральный
-/// сервис .
-///
-///
-/// Основные функции:
-///
-/// - Обработка событий PointerPressed, PointerMoved, PointerReleased
-/// - Автоматическое отслеживание порога начала перетаскивания
-/// - Создание информации о перетаскивании на основе данных элемента
-/// - Интеграция с визуальной обратной связью через
-/// - Преобразование координат между локальной системой элемента и экранными координатами
-///
-///
-///
-/// Для использования необходимо:
-///
-/// - Создать экземпляр поведения через фабрику
-/// - Прикрепить к элементу с помощью метода
-/// - Указать данные для перетаскивания (опционально, по умолчанию используется DataContext)
-///
-///
-///
-///
-/// // Создание поведения
-/// var behavior = WinUIDragDropFactory.CreateDragSourceBehavior(dragDropService, host);
-///
-/// // Прикрепление к элементу
-/// behavior.Attach(myElement, myData);
-///
-/// // Или через attached properties
-/// <Border x:Name="DragElement"
-/// local:DragDropProperties.IsDragSource="True"
-/// local:DragDropProperties.DragData="{Binding MyData}" />
-///
-///
-///
-public sealed class WinUIDragSourceBehavior : DragSourceBehaviorBase
-{
- #region Поля
-
- private readonly IDragDropHost _host;
- private object? _dragData;
-
- #endregion
-
- #region Конструктор
-
- ///
- /// Инициализирует новый экземпляр класса .
- ///
- ///
- /// Сервис управления операциями перетаскивания. Используется для координации
- /// между источниками и целями перетаскивания.
- ///
- ///
- /// Хост для управления визуальными элементами перетаскивания. Обеспечивает
- /// отображение визуальной обратной связи во время операции.
- ///
- ///
- /// Выбрасывается, если или
- /// равны null.
- ///
- ///
- /// Конструктор инициализирует базовый класс
- /// и сохраняет ссылки на необходимые сервисы для последующего использования.
- ///
- public WinUIDragSourceBehavior(
- Core.DragDrop.Services.IDragDropService dragDropService,
- WinUIDragDropHost host)
- : base(dragDropService)
- {
- _host = host ?? throw new ArgumentNullException(nameof(host));
- }
-
- #endregion
-
- #region Публичные методы
-
- ///
- /// Прикрепляет поведение к указанному элементу WinUI.
- ///
- ///
- /// Элемент , который должен стать источником перетаскивания.
- /// Не может быть null.
- ///
- ///
- /// Данные, которые будут перетаскиваться. Может быть null.
- /// Если не указано, используется или
- /// элемента.
- ///
- ///
- /// Выбрасывается, если равен null.
- ///
- ///
- ///
- /// После вызова этого метода:
- ///
- /// - Элементу автоматически подписываются обработчики событий указателя
- /// - Поведение начинает отслеживать взаимодействия с элементом
- /// - При превышении порога перетаскивания инициируется операция через
- ///
- ///
- ///
- /// Для открепления поведения используйте метод .
- ///
- ///
- public void Attach(FrameworkElement element, object? dragData = null)
- {
- if (element == null)
- throw new ArgumentNullException(nameof(element));
-
- _dragData = dragData ?? element.DataContext ?? element.Tag;
- AssociatedElement = element;
- }
-
- ///
- /// Открепляет поведение от текущего элемента.
- ///
- ///
- ///
- /// Этот метод выполняет следующие действия:
- ///
- /// - Отписывается от всех событий элемента
- /// - Сбрасывает внутреннее состояние
- /// - Освобождает ссылки на связанные объекты
- ///
- ///
- ///
- /// Если в момент вызова активна операция перетаскивания, она будет автоматически отменена.
- ///
- ///
- /// После вызова этого метода поведение может быть повторно прикреплено к другому элементу.
- ///
- ///
- public new void Detach()
- {
- base.Detach();
- _dragData = null;
- }
-
- #endregion
-
- #region Реализация абстрактных методов DragSourceBehaviorBase
-
- ///
- protected override void SubscribeToEvents(FrameworkElement element)
- {
- if (element == null) return;
-
- element.PointerPressed += OnPointerPressed;
- element.PointerMoved += OnPointerMoved;
- element.PointerReleased += OnPointerReleased;
- element.PointerCanceled += OnPointerCanceled;
- element.PointerCaptureLost += OnPointerCaptureLost;
- }
-
- ///
- protected override void UnsubscribeFromEvents(FrameworkElement element)
- {
- if (element == null) return;
-
- element.PointerPressed -= OnPointerPressed;
- element.PointerMoved -= OnPointerMoved;
- element.PointerReleased -= OnPointerReleased;
- element.PointerCanceled -= OnPointerCanceled;
- element.PointerCaptureLost -= OnPointerCaptureLost;
- }
-
- ///
- protected override Point ConvertToScreenCoordinates(Point point)
- {
- if (AssociatedElement == null)
- return point;
-
- return WinUIWindowHelper.ConvertToScreenCoordinates(AssociatedElement, point);
- }
-
- #endregion
-
- #region Реализация интерфейса IDragSource
-
- ///
- public override async Task TryStartDragAsync(
- Point startPosition,
- CancellationToken cancellationToken = default)
- {
- if (AssociatedElement == null || _dragData == null)
- return null;
-
- try
- {
- // Создаем информацию о перетаскивании
- var dragInfo = new DragInfo(
- data: _dragData,
- allowedEffects: Core.DragDrop.Enums.DragDropEffects.Copy |
- Core.DragDrop.Enums.DragDropEffects.Move |
- Core.DragDrop.Enums.DragDropEffects.Link,
- startPosition: startPosition,
- source: this
- );
-
- // Добавляем дополнительные параметры
- dragInfo.SetParameter("SourceElement", AssociatedElement);
- dragInfo.SetParameter("SourceType", AssociatedElement.GetType().Name);
-
- return await Task.FromResult(dragInfo);
- }
- catch (Exception ex)
- {
- // Логирование ошибки создания информации о перетаскивании
- System.Diagnostics.Debug.WriteLine(
- $"Ошибка создания DragInfo: {ex.Message}");
- return null;
- }
- }
-
- #endregion
-
- #region Обработчики событий WinUI
-
- private async void OnPointerPressed(object sender, PointerRoutedEventArgs e)
- {
- if (AssociatedElement == null) return;
-
- var point = e.GetCurrentPoint(AssociatedElement);
- var position = new Point(point.Position.X, point.Position.Y);
-
- await OnInteractionStarted(position);
- }
-
- private async void OnPointerMoved(object sender, PointerRoutedEventArgs e)
- {
- if (AssociatedElement == null) return;
-
- var point = e.GetCurrentPoint(AssociatedElement);
- var position = new Point(point.Position.X, point.Position.Y);
-
- await OnInteractionMoved(position);
- }
-
- private async void OnPointerReleased(object sender, PointerRoutedEventArgs e)
- {
- await OnInteractionEnded();
- }
-
- private async void OnPointerCanceled(object sender, PointerRoutedEventArgs e)
- {
- await OnInteractionCancelled();
- }
-
- private async void OnPointerCaptureLost(object sender, PointerRoutedEventArgs e)
- {
- await OnInteractionCancelled();
- }
-
- #endregion
-
- #region Переопределение виртуальных методов
-
- ///
- protected override void OnDragCompleted(DragInfo dragInfo, Core.DragDrop.Enums.DragDropEffects effects)
- {
- base.OnDragCompleted(dragInfo, effects);
-
- // Дополнительная логика для WinUI может быть добавлена здесь
- // Например, обновление состояния элемента после успешного перетаскивания
- }
-
- ///
- protected override void OnDragCancelled(DragInfo dragInfo)
- {
- base.OnDragCancelled(dragInfo);
-
- // Дополнительная логика для WinUI может быть добавлена здесь
- // Например, восстановление визуального состояния элемента после отмены
- }
-
- #endregion
-}
\ No newline at end of file
diff --git a/Lattice.UI.DragDrop.WinUI/Behaviors/WinUIDropTargetBehavior.cs b/Lattice.UI.DragDrop.WinUI/Behaviors/WinUIDropTargetBehavior.cs
deleted file mode 100644
index 902504e..0000000
--- a/Lattice.UI.DragDrop.WinUI/Behaviors/WinUIDropTargetBehavior.cs
+++ /dev/null
@@ -1,514 +0,0 @@
-using Lattice.Core.DragDrop.Models;
-using Lattice.Core.Geometry;
-using Lattice.UI.DragDrop.Abstractions;
-using Lattice.UI.DragDrop.Behaviors;
-using Microsoft.UI.Xaml;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace Lattice.UI.DragDrop.WinUI.Behaviors;
-
-///
-/// Реализация поведения цели сброса для элементов WinUI.
-/// Наследуется от для использования
-/// общей логики регистрации целей и обработки операций сброса.
-///
-///
-///
-/// Этот класс предоставляет конкретную реализацию для платформы WinUI, обрабатывая события
-/// перетаскивания WinUI и преобразуя их в вызовы методов интерфейса .
-///
-///
-/// Основные функции:
-///
-/// - Автоматическая регистрация в
при прикреплении к элементу
-/// - Обработка событий DragEnter, DragOver, DragLeave, Drop
-/// - Автоматическое обновление границ элемента при изменении размера или позиции
-/// - Поддержка фильтрации принимаемых типов данных
-/// - Интеграция с визуальной обратной связью
-///
-///
-///
-/// Для использования необходимо:
-///
-/// - Создать экземпляр поведения через фабрику
-/// - Прикрепить к элементу с помощью метода
-/// - Настроить фильтры принимаемых данных (опционально)
-///
-///
-///
-///
-/// // Создание поведения с фильтрацией типов
-/// var behavior = WinUIDragDropFactory.CreateDropTargetBehavior(dragDropService, host);
-/// behavior.AcceptTypes(typeof(MyDataModel), typeof(string));
-/// behavior.Attach(myDropArea);
-///
-/// // Или через attached properties
-/// <Border x:Name="DropArea"
-/// local:DragDropProperties.IsDropTarget="True" />
-///
-///
-///
-public sealed class WinUIDropTargetBehavior : DropTargetBehaviorBase
-{
- #region Поля
-
- private readonly IDragDropHost _host;
- private readonly List _acceptedTypes = new();
- private readonly HashSet _acceptedFormats = new();
-
- #endregion
-
- #region Конструктор
-
- ///
- /// Инициализирует новый экземпляр класса .
- ///
- ///
- /// Сервис управления операциями перетаскивания. Используется для регистрации
- /// цели и координации операций сброса.
- ///
- ///
- /// Хост для управления визуальной обратной связью. Обеспечивает отображение
- /// индикаторов возможности сброса.
- ///
- ///
- /// Выбрасывается, если или
- /// равны null.
- ///
- ///
- /// Конструктор инициализирует базовый класс и сохраняет ссылки на сервисы.
- /// По умолчанию цель принимает все типы данных. Для настройки фильтрации
- /// используйте методы и .
- ///
- public WinUIDropTargetBehavior(
- Core.DragDrop.Services.IDragDropService dragDropService,
- IDragDropHost host)
- : base(dragDropService)
- {
- _host = host ?? throw new ArgumentNullException(nameof(host));
- }
-
- #endregion
-
- #region Публичные методы
-
- ///
- /// Прикрепляет поведение к указанному элементу WinUI.
- ///
- ///
- /// Элемент , который должен стать целью сброса.
- /// Не может быть null.
- ///
- ///
- /// Выбрасывается, если равен null.
- ///
- ///
- ///
- /// После вызова этого метода:
- ///
- /// - Элементу устанавливается свойство
= true
- /// - Поведение подписывается на события перетаскивания WinUI
- /// - Элемент регистрируется в системе перетаскивания с текущими границами
- /// - Начинается отслеживание изменений размера и позиции элемента
- ///
- ///
- ///
- /// Для открепления поведения используйте метод .
- ///
- ///
- public void Attach(FrameworkElement element)
- {
- if (element == null)
- throw new ArgumentNullException(nameof(element));
-
- element.AllowDrop = true;
- AssociatedElement = element;
- }
-
- ///
- /// Настраивает поведение для приема только указанных типов данных.
- ///
- ///
- /// Типы данных, которые может принимать цель. Если пусто, принимаются все типы.
- ///
- ///
- ///
- /// Этот метод позволяет ограничить типы данных, которые могут быть сброшены на цель.
- /// Проверка выполняется в методе путем сравнения
- /// типа сбрасываемых данных с указанными типами.
- ///
- ///
- /// Если метод не вызывался или передан пустой список, цель будет принимать данные любого типа.
- ///
- ///
- ///
- /// // Принимать только строки и объекты MyModel
- /// behavior.AcceptTypes(typeof(string), typeof(MyModel));
- ///
- ///
- ///
- public void AcceptTypes(params Type[] types)
- {
- _acceptedTypes.Clear();
- if (types != null && types.Length > 0)
- {
- _acceptedTypes.AddRange(types);
- }
- }
-
- ///
- /// Настраивает поведение для приема только указанных форматов данных.
- ///
- ///
- /// Форматы данных (например, "Text", "Bitmap", "FileDrop"), которые может принимать цель.
- /// Если пусто, формат не проверяется.
- ///
- ///
- ///
- /// Этот метод позволяет ограничить форматы данных, которые могут быть сброшены на цель.
- /// Актуально для межпроцессного перетаскивания или работы с системными форматами.
- ///
- ///
- /// Если метод не вызывался или передан пустой список, проверка формата не выполняется.
- ///
- ///
- public void AcceptFormats(params string[] formats)
- {
- _acceptedFormats.Clear();
- if (formats != null && formats.Length > 0)
- {
- foreach (var format in formats)
- {
- _acceptedFormats.Add(format);
- }
- }
- }
-
- ///
- /// Открепляет поведение от текущего элемента.
- ///
- ///
- ///
- /// Этот метод выполняет следующие действия:
- ///
- /// - Отписывается от всех событий элемента
- /// - Отменяет регистрацию цели в системе перетаскивания
- /// - Сбрасывает свойство
= false
- /// - Освобождает ссылки на связанные объекты
- ///
- ///
- ///
- /// После вызова этого метода поведение может быть повторно прикреплено к другому элементу.
- ///
- ///
- public new void Detach()
- {
- if (AssociatedElement != null)
- {
- AssociatedElement.AllowDrop = false;
- }
- base.Detach();
- }
-
- #endregion
-
- #region Реализация абстрактных методов DropTargetBehaviorBase
-
- ///
- protected override void SubscribeToEvents(FrameworkElement element)
- {
- if (element == null) return;
-
- element.DragEnter += OnDragEnter;
- element.DragOver += OnDragOver;
- element.DragLeave += OnDragLeave;
- element.Drop += OnDrop;
- element.SizeChanged += OnSizeChanged;
- element.LayoutUpdated += OnLayoutUpdated;
- }
-
- ///
- protected override void UnsubscribeFromEvents(FrameworkElement element)
- {
- if (element == null) return;
-
- element.DragEnter -= OnDragEnter;
- element.DragOver -= OnDragOver;
- element.DragLeave -= OnDragLeave;
- element.Drop -= OnDrop;
- element.SizeChanged -= OnSizeChanged;
- element.LayoutUpdated -= OnLayoutUpdated;
- }
-
- ///
- protected override Rect GetScreenBounds(FrameworkElement element)
- {
- if (element == null || !element.IsLoaded)
- return Rect.Empty;
-
- try
- {
- var window = Window.Current;
- if (window?.Content == null)
- return Rect.Empty;
-
- // Преобразуем локальные координаты элемента в координаты окна
- var transform = element.TransformToVisual(window.Content);
- var position = transform.TransformPoint(new Windows.Foundation.Point(0, 0));
-
- return new Rect(
- position.X,
- position.Y,
- element.ActualWidth,
- element.ActualHeight
- );
- }
- catch (Exception ex)
- {
- System.Diagnostics.Debug.WriteLine(
- $"Ошибка получения границ элемента: {ex.Message}");
- return Rect.Empty;
- }
- }
-
- #endregion
-
- #region Реализация интерфейса IDropTarget
-
- ///
- public override async Task CanAcceptDropAsync(
- DropInfo dropInfo,
- CancellationToken cancellationToken = default)
- {
- // Проверяем, есть ли данные для сброса
- if (dropInfo.Data == null)
- return false;
-
- // Проверяем фильтр по типам
- if (_acceptedTypes.Count > 0)
- {
- var dataType = dropInfo.Data.GetType();
- if (!_acceptedTypes.Any(t => t.IsAssignableFrom(dataType)))
- {
- return false;
- }
- }
-
- // Проверяем фильтр по форматам (если данные предоставляют информацию о формате)
- if (_acceptedFormats.Count > 0 && dropInfo.Data is Windows.ApplicationModel.DataTransfer.DataPackageView dataView)
- {
- var availableFormats = dataView.AvailableFormats;
- if (!_acceptedFormats.Any(f => availableFormats.Contains(f)))
- {
- return false;
- }
- }
-
- // Дополнительная проверка может быть добавлена в производных классах
- return await Task.FromResult(true);
- }
-
- ///
- public override async Task OnDragOverAsync(
- DropInfo dropInfo,
- CancellationToken cancellationToken = default)
- {
- await base.OnDragOverAsync(dropInfo, cancellationToken);
-
- // Дополнительная логика для WinUI может быть добавлена здесь
- // Например, обновление визуальной обратной связи через хост
- }
-
- ///
- public override async Task OnDropAsync(
- DropInfo dropInfo,
- CancellationToken cancellationToken = default)
- {
- // Базовая реализация вызывает CanAcceptDropAsync и помечает как обработанное
- if (await CanAcceptDropAsync(dropInfo, cancellationToken))
- {
- dropInfo.MarkAsHandled();
-
- // Здесь может быть добавлена логика обработки сброшенных данных
- // Например, вызов события или обновление модели данных
- }
- }
-
- ///
- public override async Task OnDragLeaveAsync(CancellationToken cancellationToken = default)
- {
- await base.OnDragLeaveAsync(cancellationToken);
-
- // Дополнительная логика для WinUI может быть добавлена здесь
- // Например, скрытие визуальной обратной связи
- }
-
- #endregion
-
- #region Обработчики событий WinUI
-
- private async void OnDragEnter(object sender, DragEventArgs e)
- {
- if (AssociatedElement == null) return;
-
- try
- {
- var position = e.GetPosition(AssociatedElement);
- var dropInfo = CreateDropInfo(e, new Point(position.X, position.Y));
-
- if (await CanAcceptDropAsync(dropInfo))
- {
- e.AcceptedOperation = Windows.ApplicationModel.DataTransfer.DataPackageOperation.Copy;
- e.Handled = true;
- }
- }
- catch (Exception ex)
- {
- System.Diagnostics.Debug.WriteLine($"Ошибка в OnDragEnter: {ex.Message}");
- }
- }
-
- private async void OnDragOver(object sender, DragEventArgs e)
- {
- if (AssociatedElement == null) return;
-
- try
- {
- var position = e.GetPosition(AssociatedElement);
- var dropInfo = CreateDropInfo(e, new Point(position.X, position.Y));
-
- if (await CanAcceptDropAsync(dropInfo))
- {
- e.AcceptedOperation = Windows.ApplicationModel.DataTransfer.DataPackageOperation.Copy;
- await OnDragOverAsync(dropInfo);
- e.Handled = true;
- }
- }
- catch (Exception ex)
- {
- System.Diagnostics.Debug.WriteLine($"Ошибка в OnDragOver: {ex.Message}");
- }
- }
-
- private async void OnDragLeave(object sender, DragEventArgs e)
- {
- await OnDragLeaveAsync();
- }
-
- private async void OnDrop(object sender, DragEventArgs e)
- {
- if (AssociatedElement == null) return;
-
- try
- {
- var position = e.GetPosition(AssociatedElement);
- var dropInfo = CreateDropInfo(e, new Point(position.X, position.Y));
-
- if (await CanAcceptDropAsync(dropInfo))
- {
- await OnDropAsync(dropInfo);
- e.Handled = true;
- }
- }
- catch (Exception ex)
- {
- System.Diagnostics.Debug.WriteLine($"Ошибка в OnDrop: {ex.Message}");
- }
- }
-
- private void OnSizeChanged(object sender, SizeChangedEventArgs e)
- {
- OnElementLayoutChanged();
- }
-
- private void OnLayoutUpdated(object sender, object e)
- {
- OnElementLayoutChanged();
- }
-
- #endregion
-
- #region Вспомогательные методы
-
- ///
- /// Создает объект на основе события перетаскивания WinUI.
- ///
- /// Аргументы события перетаскивания WinUI.
- /// Локальная позиция курсора относительно элемента.
- ///
- /// Экземпляр , содержащий информацию о потенциальном сбросе.
- ///
- ///
- ///
- /// Этот метод извлекает данные из события перетаскивания и преобразует их
- /// в формат, понятный системе .
- ///
- ///
- /// Поддерживаются как пользовательские данные (через свойство "DragData"),
- /// так и стандартные форматы данных WinUI.
- ///
- ///
- private DropInfo CreateDropInfo(DragEventArgs e, Point position)
- {
- object? data = null;
-
- // Пытаемся получить пользовательские данные
- if (e.DataView.Properties.TryGetValue("DragData", out var dragData))
- {
- data = dragData;
- }
- // Или получаем данные из DataPackage
- else if (e.DataView.Contains(Windows.ApplicationModel.DataTransfer.StandardDataFormats.Text))
- {
- // Для текстовых данных можем установить асинхронную загрузку
- data = new AsyncDataProvider(async () =>
- {
- return await e.DataView.GetTextAsync();
- });
- }
-
- // Определяем разрешенные эффекты на основе модификаторов клавиатуры
- var allowedEffects = Core.DragDrop.Enums.DragDropEffects.None;
- if (e.AllowedOperations.HasFlag(Windows.ApplicationModel.DataTransfer.DataPackageOperation.Copy))
- allowedEffects |= Core.DragDrop.Enums.DragDropEffects.Copy;
- if (e.AllowedOperations.HasFlag(Windows.ApplicationModel.DataTransfer.DataPackageOperation.Move))
- allowedEffects |= Core.DragDrop.Enums.DragDropEffects.Move;
- if (e.AllowedOperations.HasFlag(Windows.ApplicationModel.DataTransfer.DataPackageOperation.Link))
- allowedEffects |= Core.DragDrop.Enums.DragDropEffects.Link;
-
- return new DropInfo(
- data: data,
- position: position,
- allowedEffects: allowedEffects,
- target: this
- );
- }
-
- #endregion
-}
-
-///
-/// Предоставляет асинхронный доступ к данным перетаскивания.
-///
-///
-/// Этот класс используется для отложенной загрузки данных перетаскивания,
-/// что особенно важно для больших данных или данных, требующих обработки.
-///
-internal class AsyncDataProvider
-{
- private readonly Func> _dataLoader;
-
- public AsyncDataProvider(Func> dataLoader)
- {
- _dataLoader = dataLoader;
- }
-
- public async Task GetDataAsync()
- {
- return await _dataLoader();
- }
-}
\ No newline at end of file
diff --git a/Lattice.UI.DragDrop.WinUI/Controls/DragAdorner.cs b/Lattice.UI.DragDrop.WinUI/Controls/DragAdorner.cs
deleted file mode 100644
index 6fbc446..0000000
--- a/Lattice.UI.DragDrop.WinUI/Controls/DragAdorner.cs
+++ /dev/null
@@ -1,235 +0,0 @@
-using Lattice.Core.Geometry;
-using Microsoft.UI.Xaml;
-using Microsoft.UI.Xaml.Controls;
-using Microsoft.UI.Xaml.Media;
-using System;
-
-namespace Lattice.UI.DragDrop.WinUI.Controls;
-
-///
-/// Визуальный элемент, отображаемый во время перетаскивания.
-///
-///
-///
-/// Этот элемент отображает репрезентативное представление перетаскиваемых данных
-/// и следует за курсором мыши во время операции перетаскивания.
-///
-///
-/// Элемент поддерживает настройку прозрачности, смещения и угла поворота,
-/// а также анимированное появление и скрытие.
-///
-///
-public class DragAdorner : Control
-{
- ///
- /// Идентификатор свойства для данных перетаскивания.
- ///
- public static readonly DependencyProperty DragDataProperty =
- DependencyProperty.Register(
- "DragData",
- typeof(object),
- typeof(DragAdorner),
- new PropertyMetadata(null, OnDragDataChanged));
-
- ///
- /// Идентификатор свойства для смещения относительно курсора.
- ///
- public static readonly DependencyProperty OffsetProperty =
- DependencyProperty.Register(
- "Offset",
- typeof(Point),
- typeof(DragAdorner),
- new PropertyMetadata(new Point(0, 0)));
-
- ///
- /// Идентификатор свойства для угла поворота.
- ///
- public static readonly DependencyProperty RotationAngleProperty =
- DependencyProperty.Register(
- "RotationAngle",
- typeof(double),
- typeof(DragAdorner),
- new PropertyMetadata(0.0));
-
- ///
- /// Идентификатор свойства для прозрачности.
- ///
- public static readonly DependencyProperty OpacityLevelProperty =
- DependencyProperty.Register(
- "OpacityLevel",
- typeof(double),
- typeof(DragAdorner),
- new PropertyMetadata(0.7));
-
- private ContentPresenter? _contentPresenter;
-
- ///
- /// Инициализирует новый экземпляр класса .
- ///
- public DragAdorner()
- {
- DefaultStyleKey = typeof(DragAdorner);
-
- // Устанавливаем свойства для корректного отображения поверх других элементов
- IsHitTestVisible = false;
- UseSystemFocusVisuals = false;
- }
-
- ///
- /// Получает или задает данные, которые отображаются в визуальном элементе.
- ///
- ///
- /// Объект данных для отображения. Обычно это те же данные, которые перетаскиваются.
- ///
- public object DragData
- {
- get => GetValue(DragDataProperty);
- set => SetValue(DragDataProperty, value);
- }
-
- ///
- /// Получает или задает смещение элемента относительно позиции курсора.
- ///
- ///
- /// Смещение по осям X и Y. Используется для позиционирования элемента так,
- /// чтобы он не перекрывал курсор. Значение по умолчанию вычисляется автоматически
- /// на основе размера элемента.
- ///
- public Point Offset
- {
- get => (Point)GetValue(OffsetProperty);
- set => SetValue(OffsetProperty, value);
- }
-
- ///
- /// Получает или задает угол поворота.
- ///
- public double RotationAngle
- {
- get => (double)GetValue(RotationAngleProperty);
- set => SetValue(RotationAngleProperty, value);
- }
-
- ///
- /// Получает или задает уровень прозрачности.
- ///
- public double OpacityLevel
- {
- get => (double)GetValue(OpacityLevelProperty);
- set => SetValue(OpacityLevelProperty, value);
- }
-
- ///
- protected override void OnApplyTemplate()
- {
- base.OnApplyTemplate();
-
- _contentPresenter = GetTemplateChild("PART_ContentPresenter") as ContentPresenter;
- UpdateContent();
- }
-
- ///
- /// Обновляет позицию элемента в соответствии с позицией курсора.
- ///
- /// Текущая позиция курсора в экранных координатах.
- ///
- /// Метод применяет трансформации для позиционирования элемента с учетом
- /// заданного смещения и угла поворота.
- ///
- public void UpdatePosition(Point cursorPosition)
- {
- var transform = new TranslateTransform
- {
- X = cursorPosition.X + Offset.X,
- Y = cursorPosition.Y + Offset.Y
- };
-
- RenderTransform = new TransformGroup
- {
- Children =
- {
- transform,
- new RotateTransform { Angle = RotationAngle }
- }
- };
- }
-
- ///
- /// Показывает элемент с анимацией.
- ///
- public void Show()
- {
- Visibility = Visibility.Visible;
-
- // Анимация появления
- var animation = new Microsoft.UI.Xaml.Media.Animation.DoubleAnimation
- {
- From = 0,
- To = OpacityLevel,
- Duration = TimeSpan.FromMilliseconds(150),
- EasingFunction = new Microsoft.UI.Xaml.Media.Animation.CubicEase
- {
- EasingMode = Microsoft.UI.Xaml.Media.Animation.EasingMode.EaseOut
- }
- };
-
- var storyboard = new Microsoft.UI.Xaml.Media.Animation.Storyboard();
- storyboard.Children.Add(animation);
- Microsoft.UI.Xaml.Media.Animation.Storyboard.SetTarget(animation, this);
- Microsoft.UI.Xaml.Media.Animation.Storyboard.SetTargetProperty(animation, "Opacity");
-
- storyboard.Begin();
- }
-
- ///
- /// Скрывает элемент с анимацией.
- ///
- public void Hide()
- {
- var animation = new Microsoft.UI.Xaml.Media.Animation.DoubleAnimation
- {
- From = Opacity,
- To = 0,
- Duration = TimeSpan.FromMilliseconds(100),
- EasingFunction = new Microsoft.UI.Xaml.Media.Animation.CubicEase
- {
- EasingMode = Microsoft.UI.Xaml.Media.Animation.EasingMode.EaseIn
- }
- };
-
- animation.Completed += (s, e) =>
- {
- Visibility = Visibility.Collapsed;
- };
-
- var storyboard = new Microsoft.UI.Xaml.Media.Animation.Storyboard();
- storyboard.Children.Add(animation);
- Microsoft.UI.Xaml.Media.Animation.Storyboard.SetTarget(animation, this);
- Microsoft.UI.Xaml.Media.Animation.Storyboard.SetTargetProperty(animation, "Opacity");
-
- storyboard.Begin();
- }
-
- private static void OnDragDataChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
- {
- if (d is DragAdorner adorner)
- {
- adorner.UpdateContent();
- }
- }
-
- private void UpdateContent()
- {
- if (_contentPresenter != null)
- {
- // Можно добавить DataTemplateSelector для разных типов данных
- _contentPresenter.Content = DragData;
-
- // Автоматически вычисляем смещение для приятного вида
- if (DragData is FrameworkElement element)
- {
- Offset = new Point(-element.ActualWidth / 2, -element.ActualHeight / 2);
- }
- }
- }
-}
\ No newline at end of file
diff --git a/Lattice.UI.DragDrop.WinUI/Controls/DragDropOverlay.cs b/Lattice.UI.DragDrop.WinUI/Controls/DragDropOverlay.cs
deleted file mode 100644
index 8d7182b..0000000
--- a/Lattice.UI.DragDrop.WinUI/Controls/DragDropOverlay.cs
+++ /dev/null
@@ -1,167 +0,0 @@
-using Microsoft.UI.Xaml;
-using Microsoft.UI.Xaml.Controls;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-
-namespace Lattice.UI.DragDrop.WinUI.Controls;
-
-///
-/// Оверлейный слой для отображения всех визуальных элементов перетаскивания.
-///
-///
-///
-/// Этот элемент добавляется поверх всего содержимого окна и содержит:
-/// - Drag-визуализации (элементы, следующие за курсором)
-/// - Drop-превью (подсветка областей сброса)
-///
-///
-/// Элемент имеет = false, чтобы не перехватывать
-/// пользовательский ввод во время операций перетаскивания.
-///
-///
-public class DragDropOverlay : Canvas
-{
- private readonly List _dragVisuals = new();
- private readonly List _dropAdorners = new();
-
- ///
- /// Инициализирует новый экземпляр класса .
- ///
- public DragDropOverlay()
- {
- IsHitTestVisible = false;
- Background = null;
-
- // Устанавливаем высокий Z-Index, чтобы быть поверх всего
- Canvas.SetZIndex(this, 10000);
- }
-
- ///
- /// Показывает визуальное представление перетаскивания.
- ///
- /// Визуальное представление.
- /// Начальная позиция X.
- /// Начальная позиция Y.
- public void ShowDragVisual(UIElement dragVisual, double initialX, double initialY)
- {
- if (!Children.Contains(dragVisual))
- {
- Children.Add(dragVisual);
- _dragVisuals.Add(dragVisual);
- }
-
- SetLeft(dragVisual, initialX);
- SetTop(dragVisual, initialY);
- dragVisual.Visibility = Visibility.Visible;
- }
-
- ///
- /// Обновляет позицию визуального представления перетаскивания.
- ///
- /// Визуальное представление.
- /// Новая позиция X.
- /// Новая позиция Y.
- public void UpdateDragVisualPosition(UIElement dragVisual, double x, double y)
- {
- if (Children.Contains(dragVisual))
- {
- SetLeft(dragVisual, x);
- SetTop(dragVisual, y);
- }
- }
-
- ///
- /// Скрывает визуальное представление перетаскивания.
- ///
- /// Визуальное представление.
- public void HideDragVisual(UIElement dragVisual)
- {
- dragVisual.Visibility = Visibility.Collapsed;
- if (Children.Contains(dragVisual))
- {
- Children.Remove(dragVisual);
- _dragVisuals.Remove(dragVisual);
- }
- }
-
- ///
- /// Показывает предварительный просмотр области сброса.
- ///
- /// Границы области.
- /// Созданный элемент предварительного просмотра.
- public DropPreviewAdorner ShowDropPreview(Core.Geometry.Rect bounds)
- {
- var adorner = new DropPreviewAdorner
- {
- PreviewColor = Windows.UI.Color.FromArgb(100, 0, 120, 215),
- PreviewThickness = 2.0
- };
-
- Children.Add(adorner);
- _dropAdorners.Add(adorner);
-
- adorner.Show(bounds);
- return adorner;
- }
-
- ///
- /// Обновляет предварительный просмотр области сброса.
- ///
- /// Элемент предварительного просмотра.
- /// Новые границы.
- public void UpdateDropPreview(DropPreviewAdorner adorner, Core.Geometry.Rect bounds)
- {
- adorner.UpdatePosition(bounds);
- }
-
- ///
- /// Скрывает все предварительные просмотры областей сброса.
- ///
- public void HideAllDropPreviews()
- {
- foreach (var adorner in _dropAdorners.ToList())
- {
- adorner.Hide();
-
- var timer = new DispatcherTimer
- {
- Interval = TimeSpan.FromMilliseconds(200)
- };
-
- timer.Tick += (s, e) =>
- {
- timer.Stop();
- if (Children.Contains(adorner))
- {
- Children.Remove(adorner);
- }
- _dropAdorners.Remove(adorner);
- };
-
- timer.Start();
- }
- }
-
- ///
- /// Скрывает все визуальные элементы.
- ///
- public void ClearAllVisuals()
- {
- foreach (var visual in _dragVisuals.ToList())
- {
- HideDragVisual(visual);
- }
-
- HideAllDropPreviews();
- }
-
- ///
- /// Получает текущий элемент перетаскивания.
- ///
- /// Элемент перетаскивания или null.
- public UIElement? GetCurrentDragVisual()
- {
- return _dragVisuals.FirstOrDefault();
- }
-}
\ No newline at end of file
diff --git a/Lattice.UI.DragDrop.WinUI/Controls/DropPreviewAdorner.cs b/Lattice.UI.DragDrop.WinUI/Controls/DropPreviewAdorner.cs
deleted file mode 100644
index 7fcb552..0000000
--- a/Lattice.UI.DragDrop.WinUI/Controls/DropPreviewAdorner.cs
+++ /dev/null
@@ -1,151 +0,0 @@
-using Microsoft.UI;
-using Microsoft.UI.Xaml;
-using Microsoft.UI.Xaml.Controls;
-using Microsoft.UI.Xaml.Media;
-using System;
-using Windows.UI;
-
-namespace Lattice.UI.DragDrop.WinUI.Controls;
-
-///
-/// Визуальный элемент для подсветки области сброса.
-///
-///
-/// Этот элемент отображается вокруг целевого элемента при наведении перетаскиваемого
-/// объекта для визуального указания возможности сброса.
-///
-[TemplateVisualState(Name = "Normal", GroupName = "CommonStates")]
-[TemplateVisualState(Name = "Highlighted", GroupName = "CommonStates")]
-public class DropPreviewAdorner : Control
-{
- ///
- /// Идентификатор свойства для цвета предварительного просмотра.
- ///
- public static readonly DependencyProperty PreviewColorProperty =
- DependencyProperty.Register(
- "PreviewColor",
- typeof(Color),
- typeof(DropPreviewAdorner),
- new PropertyMetadata(Colors.DodgerBlue));
-
- ///
- /// Идентификатор свойства для толщины границы.
- ///
- public static readonly DependencyProperty PreviewThicknessProperty =
- DependencyProperty.Register(
- "PreviewThickness",
- typeof(double),
- typeof(DropPreviewAdorner),
- new PropertyMetadata(2.0));
-
- ///
- /// Идентификатор свойства для кисти границы.
- ///
- public static readonly DependencyProperty PreviewBrushProperty =
- DependencyProperty.Register(
- "PreviewBrush",
- typeof(Brush),
- typeof(DropPreviewAdorner),
- new PropertyMetadata(null));
-
- ///
- /// Инициализирует новый экземпляр класса .
- ///
- public DropPreviewAdorner()
- {
- DefaultStyleKey = typeof(DropPreviewAdorner);
- IsHitTestVisible = false;
- }
-
- ///
- /// Получает или задает цвет подсветки области сброса.
- ///
- ///
- /// Цвет границы и фона подсветки. Значение по умолчанию берется из ресурсов темы.
- ///
- public Color PreviewColor
- {
- get => (Color)GetValue(PreviewColorProperty);
- set => SetValue(PreviewColorProperty, value);
- }
-
- ///
- /// Получает или задает толщину границы.
- ///
- public double PreviewThickness
- {
- get => (double)GetValue(PreviewThicknessProperty);
- set => SetValue(PreviewThicknessProperty, value);
- }
-
- ///
- /// Получает или задает кисть границы.
- ///
- public Brush PreviewBrush
- {
- get => (Brush)GetValue(PreviewBrushProperty);
- set => SetValue(PreviewBrushProperty, value);
- }
-
- ///
- /// Показывает элемент подсветки для указанной области.
- ///
- /// Границы области для подсветки.
- ///
- /// Метод позиционирует элемент по указанным границам и запускает анимацию появления.
- ///
- public void Show(Core.Geometry.Rect bounds)
- {
- Width = bounds.Width;
- Height = bounds.Height;
-
- var translateTransform = new TranslateTransform
- {
- X = bounds.X,
- Y = bounds.Y
- };
-
- RenderTransform = translateTransform;
- Visibility = Visibility.Visible;
-
- VisualStateManager.GoToState(this, "Highlighted", true);
- }
-
- ///
- /// Скрывает элемент.
- ///
- public void Hide()
- {
- VisualStateManager.GoToState(this, "Normal", true);
-
- // Отложенное скрытие для плавной анимации
- var timer = new DispatcherTimer
- {
- Interval = TimeSpan.FromMilliseconds(150)
- };
-
- timer.Tick += (s, e) =>
- {
- timer.Stop();
- Visibility = Visibility.Collapsed;
- };
-
- timer.Start();
- }
-
- ///
- /// Обновляет позицию и размер элемента подсветки.
- ///
- /// Новые границы области для подсветки.
- public void UpdatePosition(Core.Geometry.Rect bounds)
- {
- if (RenderTransform is TranslateTransform transform)
- {
- transform.X = bounds.X;
- transform.Y = bounds.Y;
- }
-
- Width = bounds.Width;
- Height = bounds.Height;
- }
-}
\ No newline at end of file
diff --git a/Lattice.UI.DragDrop.WinUI/DragDropProperties.cs b/Lattice.UI.DragDrop.WinUI/DragDropProperties.cs
deleted file mode 100644
index 2ffac4b..0000000
--- a/Lattice.UI.DragDrop.WinUI/DragDropProperties.cs
+++ /dev/null
@@ -1,168 +0,0 @@
-using Microsoft.UI.Xaml;
-
-namespace Lattice.UI.DragDrop.WinUI;
-
-///
-/// Предоставляет attached properties для настройки drag-and-drop поведения элементов WinUI.
-///
-///
-/// Этот класс содержит attached properties, которые позволяют включать и настраивать
-/// возможности перетаскивания и сброса для любых FrameworkElement в приложении WinUI.
-///
-public static class DragDropProperties
-{
- #region Drag Source Properties
-
- ///
- /// Прикрепленное свойство для включения перетаскивания.
- ///
- public static readonly DependencyProperty IsDragSourceProperty =
- DependencyProperty.RegisterAttached(
- "IsDragSource",
- typeof(bool),
- typeof(DragDropProperties),
- new PropertyMetadata(false, OnIsDragSourceChanged));
-
- ///
- /// Прикрепленное свойство для данных перетаскивания.
- ///
- public static readonly DependencyProperty DragDataProperty =
- DependencyProperty.RegisterAttached(
- "DragData",
- typeof(object),
- typeof(DragDropProperties),
- new PropertyMetadata(null));
-
- ///
- /// Получает значение IsDragSource.
- ///
- public static bool GetIsDragSource(UIElement element) =>
- (bool)element.GetValue(IsDragSourceProperty);
-
- ///
- /// Устанавливает значение IsDragSource.
- ///
- public static void SetIsDragSource(UIElement element, bool value) =>
- element.SetValue(IsDragSourceProperty, value);
-
- ///
- /// Получает значение DragData.
- ///
- public static object? GetDragData(UIElement element) =>
- element.GetValue(DragDataProperty);
-
- ///
- /// Устанавливает значение DragData.
- ///
- public static void SetDragData(UIElement element, object? value) =>
- element.SetValue(DragDataProperty, value);
-
- private static void OnIsDragSourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
- {
- if (d is FrameworkElement element)
- {
- if ((bool)e.NewValue)
- {
- var data = GetDragData(element);
- Services.WinUIDragDropManager.Instance.MakeDragSource(element, data);
- }
- else
- {
- Services.WinUIDragDropManager.Instance.RemoveDragSource(element);
- }
- }
- }
-
- #endregion
-
- #region Drop Target Properties
-
- ///
- /// Прикрепленное свойство для включения цели сброса.
- ///
- public static readonly DependencyProperty IsDropTargetProperty =
- DependencyProperty.RegisterAttached(
- "IsDropTarget",
- typeof(bool),
- typeof(DragDropProperties),
- new PropertyMetadata(false, OnIsDropTargetChanged));
-
- ///
- /// Получает значение IsDropTarget.
- ///
- public static bool GetIsDropTarget(UIElement element) =>
- (bool)element.GetValue(IsDropTargetProperty);
-
- ///
- /// Устанавливает значение IsDropTarget.
- ///
- public static void SetIsDropTarget(UIElement element, bool value) =>
- element.SetValue(IsDropTargetProperty, value);
-
- private static void OnIsDropTargetChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
- {
- if (d is FrameworkElement element)
- {
- if ((bool)e.NewValue)
- {
- Services.WinUIDragDropManager.Instance.MakeDropTarget(element);
- }
- else
- {
- Services.WinUIDragDropManager.Instance.RemoveDropTarget(element);
- }
- }
- }
-
- #endregion
-
- #region Helper Methods
-
- ///
- /// Включает перетаскивание для элемента с указанными данными.
- ///
- /// Элемент, для которого включается перетаскивание.
- /// Данные для перетаскивания. Если не указано, используются DataContext или Tag элемента.
- ///
- ///
- /// myElement.EnableDrag(item);
- ///
- ///
- public static void EnableDrag(this FrameworkElement element, object? dragData = null)
- {
- if (dragData != null)
- {
- SetDragData(element, dragData);
- }
- SetIsDragSource(element, true);
- }
-
- ///
- /// Отключает перетаскивание для элемента.
- ///
- /// Элемент, для которого отключается перетаскивание.
- public static void DisableDrag(this FrameworkElement element)
- {
- SetIsDragSource(element, false);
- }
-
- ///
- /// Включает возможность сброса для элемента.
- ///
- /// Элемент, для которого включается возможность сброса.
- public static void EnableDrop(this FrameworkElement element)
- {
- SetIsDropTarget(element, true);
- }
-
- ///
- /// Отключает возможность сброса для элемента.
- ///
- /// Элемент, для которого отключается возможность сброса.
- public static void DisableDrop(this FrameworkElement element)
- {
- SetIsDropTarget(element, false);
- }
-
- #endregion
-}
\ No newline at end of file
diff --git a/Lattice.UI.DragDrop.WinUI/Factories/WinUIDragDropFactory.cs b/Lattice.UI.DragDrop.WinUI/Factories/WinUIDragDropFactory.cs
deleted file mode 100644
index 2dd071a..0000000
--- a/Lattice.UI.DragDrop.WinUI/Factories/WinUIDragDropFactory.cs
+++ /dev/null
@@ -1,950 +0,0 @@
-using Lattice.Core.DragDrop.Services;
-using Lattice.UI.DragDrop.WinUI.Behaviors;
-using Lattice.UI.DragDrop.WinUI.Controls;
-using Lattice.UI.DragDrop.WinUI.Services;
-using Microsoft.UI.Xaml;
-using Microsoft.UI.Xaml.Controls;
-using System;
-
-namespace Lattice.UI.DragDrop.WinUI.Factories;
-
-///
-/// Фабрика для создания и настройки компонентов системы перетаскивания WinUI.
-/// Предоставляет удобные методы для быстрой интеграции drag-and-drop функциональности
-/// в приложениях WinUI с поддержкой различных сценариев использования.
-///
-///
-///
-/// служит высокоуровневым API для работы с системой
-/// перетаскивания, инкапсулируя сложность инициализации и настройки компонентов.
-///
-///
-/// Основные возможности фабрики:
-///
-/// - Создание и инициализация менеджера перетаскивания
-/// - Генерация поведений для источников и целей перетаскивания
-/// - Создание визуальных элементов для обратной связи
-/// - Предварительные конфигурации для типовых сценариев
-/// - Вспомогательные методы для работы с XAML
-///
-///
-///
-/// Фабрика поддерживает два подхода к использованию:
-///
-/// Императивный подход - создание компонентов в коде C#
-/// Декларативный подход - использование attached properties в XAML
-///
-///
-///
-///
-/// // Императивный подход
-/// var manager = WinUIDragDropFactory.CreateManager(window);
-/// manager.MakeDragSource(element, data);
-/// manager.MakeDropTarget(dropArea);
-///
-/// // Декларативный подход (в XAML)
-/// <Border local:DragDropProperties.IsDragSource="True"
-/// local:DragDropProperties.DragData="{Binding Item}" />
-/// <Border local:DragDropProperties.IsDropTarget="True" />
-///
-///
-///
-public static class WinUIDragDropFactory
-{
- #region Основные компоненты
-
- ///
- /// Создает и инициализирует менеджер перетаскивания для указанного окна WinUI.
- ///
- ///
- /// Окно WinUI, для которого создается менеджер перетаскивания.
- /// Не может быть null.
- ///
- ///
- /// Инициализированный экземпляр .
- ///
- ///
- /// Выбрасывается, когда равен null.
- ///
- ///
- ///
- /// Этот метод является основным способом получения менеджера перетаскивания.
- /// Он создает (или возвращает существующий) экземпляр менеджера и инициализирует
- /// его для работы с указанным окном.
- ///
- ///
- /// Метод следует вызывать один раз при запуске приложения, обычно в конструкторе
- /// главного окна или в методе .
- ///
- ///
- ///
- /// public partial class MainWindow : Window
- /// {
- /// public MainWindow()
- /// {
- /// InitializeComponent();
- /// var manager = WinUIDragDropFactory.CreateManager(this);
- /// }
- /// }
- ///
- ///
- ///
- public static WinUIDragDropManager CreateManager(Window window)
- {
- if (window == null)
- throw new ArgumentNullException(nameof(window));
-
- var manager = WinUIDragDropManager.Instance;
- if (!manager.IsInitialized)
- {
- manager.Initialize(window);
- }
- return manager;
- }
-
- ///
- /// Создает и инициализирует менеджер перетаскивания с пользовательскими настройками.
- ///
- ///
- /// Окно WinUI, для которого создается менеджер перетаскивания.
- ///
- ///
- /// Делегат для настройки параметров менеджера перед инициализацией.
- /// Передает экземпляр для конфигурации.
- ///
- ///
- /// Инициализированный экземпляр .
- ///
- ///
- /// Выбрасывается, когда равен null.
- ///
- ///
- ///
- /// Этот метод позволяет настроить параметры менеджера перед его инициализацией,
- /// что полезно для тонкой настройки поведения системы перетаскивания.
- ///
- ///
- /// Доступные для настройки параметры включают:
- ///
- /// - смещение визуального элемента
- ///
- ///
- ///
- ///
- /// var manager = WinUIDragDropFactory.CreateManager(window, m =>
- /// {
- /// m.DragVisualOffset = new Point(-15, -15); // Ближе к курсору
- /// });
- ///
- ///
- ///
- public static WinUIDragDropManager CreateManager(Window window, Action configure)
- {
- if (window == null)
- throw new ArgumentNullException(nameof(window));
-
- var manager = WinUIDragDropManager.Instance;
-
- // Применяем настройки перед инициализацией
- configure?.Invoke(manager);
-
- if (!manager.IsInitialized)
- {
- manager.Initialize(window);
- }
-
- return manager;
- }
-
- ///
- /// Создает хост для управления визуальными элементами перетаскивания.
- ///
- ///
- /// Окно, к которому будет привязан хост.
- ///
- ///
- /// Экземпляр , готовый к использованию.
- ///
- ///
- /// Выбрасывается, если равен null.
- ///
- ///
- ///
- /// Хост управляет отображением визуальных элементов во время операций перетаскивания,
- /// включая drag-визуализации (элементы, следующие за курсором) и drop-превью
- /// (подсветка областей сброса).
- ///
- ///
- /// В большинстве случаев хост создается автоматически менеджером перетаскивания.
- /// Этот метод полезен для продвинутых сценариев, когда требуется прямой контроль
- /// над визуальной обратной связью.
- ///
- ///
- ///
- /// var host = WinUIDragDropFactory.CreateHost(window);
- /// // Настройка кастомной визуализации
- ///
- ///
- ///
- public static WinUIDragDropHost CreateHost(Window window)
- {
- if (window == null)
- throw new ArgumentNullException(nameof(window));
-
- var host = new WinUIDragDropHost();
- host.Initialize(window);
- return host;
- }
-
- #endregion
-
- #region Поведения (Behaviors)
-
- ///
- /// Создает поведение источника перетаскивания для элемента WinUI.
- ///
- ///
- /// Сервис перетаскивания, который будет управлять операциями.
- ///
- ///
- /// Хост для отображения визуальных элементов.
- ///
- ///
- /// Экземпляр , готовый к прикреплению к элементу.
- ///
- ///
- /// Выбрасывается, если любой из параметров равен null.
- ///
- ///
- ///
- /// Созданное поведение необходимо прикрепить к элементу с помощью метода
- /// .
- ///
- ///
- /// Этот метод полезен для продвинутых сценариев, когда требуется создавать поведения
- /// вручную, например, при динамическом создании элементов интерфейса.
- ///
- ///
- ///
- /// // Создание поведения вручную
- /// var behavior = WinUIDragDropFactory.CreateDragSourceBehavior(service, host);
- /// behavior.Attach(element, data);
- ///
- ///
- ///
- public static WinUIDragSourceBehavior CreateDragSourceBehavior(
- IDragDropService dragDropService,
- WinUIDragDropHost host)
- {
- if (dragDropService == null)
- throw new ArgumentNullException(nameof(dragDropService));
- if (host == null)
- throw new ArgumentNullException(nameof(host));
-
- return new WinUIDragSourceBehavior(dragDropService, host);
- }
-
- ///
- /// Создает поведение цели сброса для элемента WinUI.
- ///
- ///
- /// Сервис перетаскивания, который будет управлять операциями.
- ///
- ///
- /// Хост для отображения визуальных элементов.
- ///
- ///
- /// Экземпляр , готовый к прикреплению к элементу.
- ///
- ///
- /// Выбрасывается, если любой из параметров равен null.
- ///
- ///
- ///
- /// Созданное поведение необходимо прикрепить к элементу с помощью метода
- /// .
- ///
- ///
- /// Поведение можно дополнительно настроить с помощью методов
- /// и
- /// для фильтрации
- /// принимаемых данных.
- ///
- ///
- ///
- /// // Создание поведения вручную
- /// var behavior = WinUIDragDropFactory.CreateDropTargetBehavior(service, host);
- /// behavior.AcceptTypes(typeof(string), typeof(MyModel));
- /// behavior.Attach(dropArea);
- ///
- ///
- ///
- public static WinUIDropTargetBehavior CreateDropTargetBehavior(
- IDragDropService dragDropService,
- WinUIDragDropHost host)
- {
- if (dragDropService == null)
- throw new ArgumentNullException(nameof(dragDropService));
- if (host == null)
- throw new ArgumentNullException(nameof(host));
-
- return new WinUIDropTargetBehavior(dragDropService, host);
- }
-
- ///
- /// Создает поведение источника перетаскивания, используя сервисы из менеджера по умолчанию.
- ///
- ///
- /// Экземпляр , готовый к прикреплению к элементу.
- ///
- ///
- /// Выбрасывается, если менеджер не инициализирован.
- ///
- ///
- ///
- /// Этот метод использует для получения
- /// сервиса перетаскивания и хоста, что упрощает создание поведений в контексте
- /// уже инициализированной системы.
- ///
- ///
- /// Перед использованием убедитесь, что менеджер инициализирован через метод
- /// .
- ///
- ///
- ///
- /// // Инициализация менеджера
- /// WinUIDragDropFactory.CreateManager(window);
- ///
- /// // Создание поведения с использованием менеджера
- /// var behavior = WinUIDragDropFactory.CreateDragSourceBehavior();
- /// behavior.Attach(element, data);
- ///
- ///
- ///
- public static WinUIDragSourceBehavior CreateDragSourceBehavior()
- {
- var manager = WinUIDragDropManager.Instance;
- if (!manager.IsInitialized)
- {
- throw new InvalidOperationException(
- "Менеджер не инициализирован. Вызовите CreateManager перед созданием поведений.");
- }
-
- return new WinUIDragSourceBehavior(manager.DragDropService, manager.Host);
- }
-
- ///
- /// Создает поведение цели сброса, используя сервисы из менеджера по умолчанию.
- ///
- ///
- /// Экземпляр , готовый к прикреплению к элементу.
- ///
- ///
- /// Выбрасывается, если менеджер не инициализирован.
- ///
- ///
- ///
- /// Этот метод использует для получения
- /// сервиса перетаскивания и хоста, что упрощает создание поведений в контексте
- /// уже инициализированной системы.
- ///
- ///
- /// Поведение можно дополнительно настроить с помощью методов
- /// и
- /// для фильтрации
- /// принимаемых данных.
- ///
- ///
- ///
- /// // Инициализация менеджера
- /// WinUIDragDropFactory.CreateManager(window);
- ///
- /// // Создание поведения с использованием менеджера
- /// var behavior = WinUIDragDropFactory.CreateDropTargetBehavior();
- /// behavior.AcceptTypes(typeof(string));
- /// behavior.Attach(dropArea);
- ///
- ///
- ///
- public static WinUIDropTargetBehavior CreateDropTargetBehavior()
- {
- var manager = WinUIDragDropManager.Instance;
- if (!manager.IsInitialized)
- {
- throw new InvalidOperationException(
- "Менеджер не инициализирован. Вызовите CreateManager перед созданием поведений.");
- }
-
- return new WinUIDropTargetBehavior(manager.DragDropService, manager.Host);
- }
-
- #endregion
-
- #region Визуальные элементы
-
- ///
- /// Создает визуальный элемент для отображения во время перетаскивания.
- ///
- ///
- /// Данные, которые будут отображены в визуальном элементе.
- /// Могут быть любого типа, поддерживаемого системой перетаскивания.
- ///
- ///
- /// Прозрачность элемента в диапазоне от 0.0 (полностью прозрачный) до 1.0 (полностью непрозрачный).
- /// Значение по умолчанию: 0.8.
- ///
- ///
- /// Экземпляр , настроенный для отображения указанных данных.
- ///
- ///
- ///
- /// Созданный элемент можно использовать с методом
- /// для отображения во время операции перетаскивания.
- ///
- ///
- /// Элемент автоматически адаптирует отображение в зависимости от типа данных:
- ///
- /// - Для строк отображается текстовое представление
- /// - Для изображений отображается миниатюра
- /// - Для пользовательских объектов используется DataTemplate или ToString()
- ///
- ///
- ///
- ///
- /// // Создание визуального элемента
- /// var adorner = WinUIDragDropFactory.CreateDragAdorner("Текст для перетаскивания");
- ///
- /// // Отображение во время перетаскивания
- /// host.ShowDragVisual(adorner, position);
- ///
- ///
- ///
- public static DragAdorner CreateDragAdorner(object dragData, double opacity = 0.8)
- {
- return new DragAdorner
- {
- DragData = dragData ?? throw new ArgumentNullException(nameof(dragData)),
- Opacity = Math.Clamp(opacity, 0.0, 1.0)
- };
- }
-
- ///
- /// Создает элемент предварительного просмотра для области сброса.
- ///
- ///
- /// Цвет подсветки области. Если не указан, используется системный цвет акцента.
- ///
- ///
- /// Толщина границы подсветки в пикселях.
- /// Значение по умолчанию: 2.0.
- ///
- ///
- /// Экземпляр , готовый к отображению.
- ///
- ///
- ///
- /// Этот элемент используется для визуального указания области, на которую можно
- /// сбросить данные. Он отображается как подсветка границ целевого элемента с
- /// поддержкой анимации появления и скрытия.
- ///
- ///
- /// Элемент автоматически адаптирует свой внешний вид в зависимости от состояния:
- ///
- /// Normal - стандартное состояние
- /// Highlighted - подсветка при наведении
- ///
- ///
- ///
- ///
- /// // Создание элемента подсветки
- /// var preview = WinUIDragDropFactory.CreateDropPreviewAdorner(
- /// Colors.Blue, // Цвет
- /// 3.0 // Толщина границы
- /// );
- ///
- /// // Отображение подсветки
- /// preview.Show(bounds);
- ///
- ///
- ///
- public static DropPreviewAdorner CreateDropPreviewAdorner(
- Windows.UI.Color? color = null,
- double thickness = 2.0)
- {
- var adorner = new DropPreviewAdorner
- {
- PreviewThickness = Math.Max(thickness, 0.0)
- };
-
- if (color.HasValue)
- {
- adorner.PreviewColor = color.Value;
- }
-
- return adorner;
- }
-
- #endregion
-
- #region Готовые конфигурации для типовых сценариев
-
- ///
- /// Создает полную систему перетаскивания для WinUI приложения.
- ///
- ///
- /// Главное окно приложения.
- ///
- ///
- /// Кортеж, содержащий менеджер перетаскивания и сервис перетаскивания.
- ///
- ///
- ///
- /// Этот метод создает все необходимые компоненты для работы перетаскивания в приложении
- /// и возвращает их для дальнейшего использования.
- ///
- ///
- /// Возвращаемый сервис можно использовать для создания дополнительных источников и целей
- /// или для низкоуровневого управления операциями перетаскивания.
- ///
- ///
- ///
- /// // Создание полной системы
- /// var (manager, service) = WinUIDragDropFactory.CreateCompleteSystem(window);
- ///
- /// // Использование менеджера для настройки элементов
- /// manager.MakeDragSource(element, data);
- ///
- /// // Использование сервиса для расширенного управления
- /// var stats = service.GetStats();
- ///
- ///
- ///
- public static (WinUIDragDropManager Manager, IDragDropService Service) CreateCompleteSystem(Window window)
- {
- var manager = CreateManager(window);
- return (manager, manager.DragDropService);
- }
-
- ///
- /// Создает систему перетаскивания, оптимизированную для переупорядочивания элементов в списках.
- ///
- ///
- /// Главное окно приложения.
- ///
- ///
- /// Менеджер перетаскивания, настроенный для переупорядочивания элементов в списках.
- ///
- ///
- ///
- /// Эта конфигурация устанавливает оптимальные параметры для перетаскивания элементов
- /// внутри списков и коллекций, таких как:
- ///
- /// - Изменение порядка элементов в ListView
- /// - Перемещение элементов между ItemsControl
- /// - Сортировка элементов в коллекциях
- ///
- ///
- ///
- /// Особенности конфигурации:
- ///
- /// - Уменьшенный порог начала перетаскивания для более быстрого отклика
- /// - Смещение визуального элемента для лучшего визуального выравнивания
- /// - Оптимизированная визуальная обратная связь
- ///
- ///
- ///
- ///
- /// // Создание системы для переупорядочивания списков
- /// var manager = WinUIDragDropFactory.CreateListReorderSystem(window);
- ///
- /// // Настройка ListView для переупорядочивания
- /// foreach (var item in myListView.Items)
- /// {
- /// if (item is FrameworkElement element)
- /// {
- /// manager.MakeDragSource(element, element.DataContext);
- /// }
- /// }
- /// manager.MakeDropTarget(myListView);
- ///
- ///
- ///
- public static WinUIDragDropManager CreateListReorderSystem(Window window)
- {
- var manager = CreateManager(window, m =>
- {
- // Уменьшенное смещение для лучшего визуального выравнивания в списках
- m.DragVisualOffset = new Core.Geometry.Point(-15, -15);
- });
-
- return manager;
- }
-
- ///
- /// Создает систему перетаскивания для работы с файлами и документами.
- ///
- ///
- /// Главное окно приложения.
- ///
- ///
- /// Менеджер перетаскивания, настроенный для работы с файлами.
- ///
- ///
- ///
- /// Эта конфигурация оптимизирована для сценариев работы с файлами:
- ///
- /// - Перетаскивание файлов из проводника в приложение
- /// - Перемещение файлов между элементами интерфейса
- /// - Работа с большими объемами данных
- ///
- ///
- ///
- /// Особенности конфигурации:
- ///
- /// - Увеличенный порог перетаскивания для предотвращения случайных операций
- /// - Специальные визуальные эффекты, характерные для файловых операций
- /// - Оптимизация для работы с внешними источниками данных
- ///
- ///
- ///
- ///
- /// // Создание системы для работы с файлами
- /// var manager = WinUIDragDropFactory.CreateFileDragDropSystem(window);
- ///
- /// // Настройка области для приема файлов
- /// manager.MakeDropTarget(fileDropArea);
- ///
- ///
- ///
- public static WinUIDragDropManager CreateFileDragDropSystem(Window window)
- {
- var manager = CreateManager(window, m =>
- {
- // Увеличенное смещение для файлов (имитация "переноса" файла)
- m.DragVisualOffset = new Core.Geometry.Point(-25, -25);
- });
-
- return manager;
- }
-
- ///
- /// Создает систему перетаскивания для графических редакторов и инструментов дизайна.
- ///
- ///
- /// Главное окно приложения.
- ///
- ///
- /// Менеджер перетаскивания, настроенный для точного позиционирования.
- ///
- ///
- ///
- /// Эта конфигурация оптимизирована для приложений, требующих высокой точности
- /// позиционирования, таких как:
- ///
- /// - Графические редакторы (Photoshop, Figma)
- /// - Инструменты проектирования интерфейсов
- /// - CAD-системы и приложения для 3D-моделирования
- ///
- ///
- ///
- /// Особенности конфигурации:
- ///
- /// - Минимальный порог начала перетаскивания для максимальной точности
- /// - Минималистичная визуализация для уменьшения визуального шума
- /// - Оптимизация для работы с высокоточными устройствами ввода (графические планшеты)
- ///
- ///
- ///
- ///
- /// // Создание системы для графического редактора
- /// var manager = WinUIDragDropFactory.CreateDesignToolSystem(window);
- ///
- /// // Настройка инструментов для перетаскивания
- /// manager.MakeDragSource(toolIcon, toolData);
- /// manager.MakeDropTarget(canvas);
- ///
- ///
- ///
- public static WinUIDragDropManager CreateDesignToolSystem(Window window)
- {
- var manager = CreateManager(window, m =>
- {
- // Минимальное смещение для точного позиционирования
- m.DragVisualOffset = new Core.Geometry.Point(-10, -10);
- });
-
- return manager;
- }
-
- #endregion
-
- #region Вспомогательные методы
-
- ///
- /// Настраивает элемент как источник перетаскивания с использованием указанного менеджера.
- ///
- ///
- /// Менеджер перетаскивания, который будет управлять операцией.
- ///
- ///
- /// Элемент WinUI, который должен стать источником перетаскивания.
- ///
- ///
- /// Данные для перетаскивания. Если не указаны, будут использованы DataContext или Tag элемента.
- ///
- ///
- /// Выбрасывается, если или равны null.
- ///
- ///
- ///
- /// Этот метод является удобной оберткой вокруг ,
- /// предоставляющей более лаконичный синтаксис.
- ///
- ///
- ///
- /// // Использование вспомогательного метода
- /// WinUIDragDropFactory.SetupAsDragSource(manager, myElement, myData);
- ///
- /// // Эквивалентно:
- /// manager.MakeDragSource(myElement, myData);
- ///
- ///
- ///
- public static void SetupAsDragSource(WinUIDragDropManager manager, FrameworkElement element, object dragData = null)
- {
- if (manager == null)
- throw new ArgumentNullException(nameof(manager));
- if (element == null)
- throw new ArgumentNullException(nameof(element));
-
- manager.MakeDragSource(element, dragData);
- }
-
- ///
- /// Настраивает элемент как цель сброса с использованием указанного менеджера.
- ///
- ///
- /// Менеджер перетаскивания, который будет управлять операцией.
- ///
- ///
- /// Элемент WinUI, который должен стать целью сброса.
- ///
- ///
- /// Выбрасывается, если или равны null.
- ///
- ///
- ///
- /// Этот метод является удобной оберткой вокруг ,
- /// предоставляющей более лаконичный синтаксис.
- ///
- ///
- ///
- /// // Использование вспомогательного метода
- /// WinUIDragDropFactory.SetupAsDropTarget(manager, myDropArea);
- ///
- /// // Эквивалентно:
- /// manager.MakeDropTarget(myDropArea);
- ///
- ///
- ///
- public static void SetupAsDropTarget(WinUIDragDropManager manager, FrameworkElement element)
- {
- if (manager == null)
- throw new ArgumentNullException(nameof(manager));
- if (element == null)
- throw new ArgumentNullException(nameof(element));
-
- manager.MakeDropTarget(element);
- }
-
- ///
- /// Настраивает контейнер для поддержки переупорядочивания дочерних элементов.
- ///
- ///
- /// Менеджер перетаскивания.
- ///
- ///
- /// Контейнер (например, StackPanel, Grid или ItemsControl), дочерние элементы которого можно переупорядочивать.
- ///
- ///
- /// Функция для получения данных перетаскивания из дочернего элемента.
- /// Если не указана, используются DataContext дочерних элементов.
- ///
- ///
- /// Выбрасывается, если или равны null.
- ///
- ///
- ///
- /// Этот метод автоматически настраивает все дочерние элементы контейнера как источники перетаскивания,
- /// а сам контейнер — как цель сброса, создавая функциональность переупорядочивания элементов.
- ///
- ///
- /// Поддерживаемые типы контейнеров:
- ///
- /// и его производные (StackPanel, Grid, Canvas)
- /// и его производные (ListView, ListBox)
- /// - Любые другие контейнеры с коллекцией дочерних элементов
- ///
- ///
- ///
- ///
- /// // Настройка StackPanel для переупорядочивания дочерних элементов
- /// WinUIDragDropFactory.SetupReorderContainer(manager, myStackPanel);
- ///
- /// // С кастомным селектором данных
- /// WinUIDragDropFactory.SetupReorderContainer(manager, myListView,
- /// element => ((FrameworkElement)element).DataContext);
- ///
- ///
- ///
- public static void SetupReorderContainer(
- WinUIDragDropManager manager,
- FrameworkElement container,
- Func childSelector = null)
- {
- if (manager == null)
- throw new ArgumentNullException(nameof(manager));
- if (container == null)
- throw new ArgumentNullException(nameof(container));
-
- // Настраиваем контейнер как цель сброса
- manager.MakeDropTarget(container);
-
- // Настраиваем дочерние элементы как источники перетаскивания
- if (container is Panel panel)
- {
- SetupPanelChildren(manager, panel, childSelector);
- }
- else if (container is ItemsControl itemsControl)
- {
- SetupItemsControlChildren(manager, itemsControl, childSelector);
- }
- }
-
- ///
- /// Настраивает дочерние элементы Panel как источники перетаскивания.
- ///
- private static void SetupPanelChildren(
- WinUIDragDropManager manager,
- Panel panel,
- Func childSelector)
- {
- foreach (var child in panel.Children)
- {
- if (child is FrameworkElement element)
- {
- var data = childSelector?.Invoke(element) ?? element.DataContext ?? element.Tag;
- manager.MakeDragSource(element, data);
- }
- }
- }
-
- ///
- /// Настраивает элементы ItemsControl как источники перетаскивания.
- ///
- private static void SetupItemsControlChildren(
- WinUIDragDropManager manager,
- ItemsControl itemsControl,
- Func childSelector)
- {
- // Для ItemsControl нам нужно работать с ItemContainerGenerator
- // В реальной реализации здесь должна быть более сложная логика
- // для обработки виртуализации и динамических элементов
-
- foreach (var item in itemsControl.Items)
- {
- if (item is FrameworkElement element)
- {
- var data = childSelector?.Invoke(element) ?? element.DataContext ?? element.Tag;
- manager.MakeDragSource(element, data);
- }
- }
- }
-
- #endregion
-
- #region Методы для работы с XAML
-
- ///
- /// Настраивает attached properties для элемента источника перетаскивания.
- ///
- ///
- /// Элемент, который должен стать источником перетаскивания.
- ///
- ///
- /// Данные для перетаскивания.
- ///
- ///
- /// Выбрасывается, если равен null.
- ///
- ///
- ///
- /// Этот метод устанавливает attached properties, которые активируют поведение перетаскивания
- /// при использовании в XAML. Эквивалентно установке свойств IsDragSource и DragData в XAML.
- ///
- ///
- /// Метод полезен для динамической настройки элементов в коде C# при сохранении
- /// декларативного стиля программирования.
- ///
- ///
- ///
- /// // Настройка элемента в коде C#
- /// WinUIDragDropFactory.SetupDragSourceInXaml(myElement, myData);
- ///
- /// // Эквивалентно в XAML:
- /// <Border local:DragDropProperties.IsDragSource="True"
- /// local:DragDropProperties.DragData="{Binding MyData}" />
- ///
- ///
- ///
- public static void SetupDragSourceInXaml(FrameworkElement element, object dragData)
- {
- if (element == null)
- throw new ArgumentNullException(nameof(element));
-
- // Устанавливаем attached properties
- element.SetValue(DragDropProperties.IsDragSourceProperty, true);
- if (dragData != null)
- {
- element.SetValue(DragDropProperties.DragDataProperty, dragData);
- }
- }
-
- ///
- /// Настраивает attached properties для элемента цели сброса.
- ///
- ///
- /// Элемент, который должен стать целью сброса.
- ///
- ///
- /// Выбрасывается, если равен null.
- ///
- ///
- ///
- /// Этот метод устанавливает attached properties, которые активируют поведение цели сброса
- /// при использовании в XAML. Эквивалентно установке свойства IsDropTarget в XAML.
- ///
- ///
- /// Метод полезен для динамической настройки элементов в коде C# при сохранении
- /// декларативного стиля программирования.
- ///
- ///
- ///
- /// // Настройка элемента в коде C#
- /// WinUIDragDropFactory.SetupDropTargetInXaml(myDropArea);
- ///
- /// // Эквивалентно в XAML:
- /// <Border local:DragDropProperties.IsDropTarget="True" />
- ///
- ///
- ///
- public static void SetupDropTargetInXaml(FrameworkElement element)
- {
- if (element == null)
- throw new ArgumentNullException(nameof(element));
-
- element.SetValue(DragDropProperties.IsDropTargetProperty, true);
- }
-
- #endregion
-}
\ No newline at end of file
diff --git a/Lattice.UI.DragDrop.WinUI/Helpers/ResourceHelper.cs b/Lattice.UI.DragDrop.WinUI/Helpers/ResourceHelper.cs
deleted file mode 100644
index 3e97232..0000000
--- a/Lattice.UI.DragDrop.WinUI/Helpers/ResourceHelper.cs
+++ /dev/null
@@ -1,102 +0,0 @@
-using Microsoft.UI.Xaml;
-using Microsoft.UI.Xaml.Media;
-using Windows.UI;
-
-namespace Lattice.UI.DragDrop.WinUI.Helpers;
-
-///
-/// Вспомогательный класс для работы с ресурсами.
-///
-public static class ResourceHelper
-{
- ///
- /// Инициализирует ресурсы перетаскивания.
- ///
- public static void InitializeDragDropResources()
- {
- // Загружаем Generic.xaml, если он еще не загружен
- var resourceDictionary = new ResourceDictionary();
-
- // В реальном приложении нужно загружать из правильного пути
- try
- {
- resourceDictionary.Source = new System.Uri("ms-appx:///Lattice.UI.DragDrop.WinUI/Themes/Generic.xaml");
- Application.Current.Resources.MergedDictionaries.Add(resourceDictionary);
- }
- catch
- {
- // Если не удалось загрузить из файла, создаем базовые ресурсы
- CreateFallbackResources();
- }
- }
-
- private static void CreateFallbackResources()
- {
- var resources = Application.Current.Resources;
-
- // Базовые кисти для визуальной обратной связи
- if (!resources.ContainsKey("DragOverBackgroundBrush"))
- {
- resources["DragOverBackgroundBrush"] = new SolidColorBrush(
- Color.FromArgb(76, 0, 120, 215)); // 30% opacity
- }
-
- if (!resources.ContainsKey("DragOverBorderBrush"))
- {
- resources["DragOverBorderBrush"] = new SolidColorBrush(
- Color.FromArgb(255, 0, 120, 215));
- }
-
- if (!resources.ContainsKey("DropValidBrush"))
- {
- resources["DropValidBrush"] = new SolidColorBrush(
- Color.FromArgb(255, 0, 204, 0));
- }
-
- if (!resources.ContainsKey("DropInvalidBrush"))
- {
- resources["DropInvalidBrush"] = new SolidColorBrush(
- Color.FromArgb(255, 255, 0, 0));
- }
- }
-
- ///
- /// Получает стиль из ресурсов.
- ///
- public static Style? GetStyle(string styleKey)
- {
- if (Application.Current.Resources.TryGetValue(styleKey, out var style) && style is Style)
- {
- return style as Style;
- }
- return null;
- }
-
- ///
- /// Получает кисть из ресурсов.
- ///
- public static Brush? GetBrush(string brushKey)
- {
- if (Application.Current.Resources.TryGetValue(brushKey, out var brush) && brush is Brush)
- {
- return brush as Brush;
- }
- return null;
- }
-
- ///
- /// Добавляет или обновляет ресурс.
- ///
- public static void SetResource(string key, object value)
- {
- Application.Current.Resources[key] = value;
- }
-
- ///
- /// Проверяет существование ресурса.
- ///
- public static bool HasResource(string key)
- {
- return Application.Current.Resources.ContainsKey(key);
- }
-}
\ No newline at end of file
diff --git a/Lattice.UI.DragDrop.WinUI/Helpers/WinUIWindowHelper.cs b/Lattice.UI.DragDrop.WinUI/Helpers/WinUIWindowHelper.cs
deleted file mode 100644
index 9ea1d4c..0000000
--- a/Lattice.UI.DragDrop.WinUI/Helpers/WinUIWindowHelper.cs
+++ /dev/null
@@ -1,89 +0,0 @@
-using Lattice.Core.Geometry;
-using Microsoft.UI.Xaml;
-using System;
-using System.Runtime.InteropServices;
-
-///
-/// Вспомогательный класс для получения экранных координат в WinUI 3.
-/// Использует P/Invoke для доступа к нативным API Windows.
-///
-internal static class WinUIWindowHelper
-{
- [StructLayout(LayoutKind.Sequential)]
- public struct POINT
- {
- public int X;
- public int Y;
-
- public POINT(int x, int y)
- {
- X = x;
- Y = y;
- }
- }
-
- [DllImport("user32.dll")]
- [return: MarshalAs(UnmanagedType.Bool)]
- public static extern bool ClientToScreen(IntPtr hWnd, ref POINT lpPoint);
-
- [DllImport("user32.dll")]
- public static extern IntPtr WindowFromPoint(POINT point);
-
- [DllImport("user32.dll", SetLastError = true)]
- public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
-
- ///
- /// Преобразует координаты элемента в экранные координаты.
- ///
- public static Point ConvertToScreenCoordinates(FrameworkElement element, Point localPoint)
- {
- if (element == null || !element.IsLoaded)
- return localPoint;
-
- try
- {
- var window = Window.Current;
- if (window == null)
- return localPoint;
-
- // Получаем хэндл окна
- var hwnd = GetWindowHandle(window);
- if (hwnd == IntPtr.Zero)
- return localPoint;
-
- // Преобразуем координаты элемента в координаты окна
- var transform = element.TransformToVisual(window.Content);
- var windowPoint = transform.TransformPoint(
- new Windows.Foundation.Point(localPoint.X, localPoint.Y));
-
- // Преобразуем в POINT для P/Invoke
- var point = new POINT(
- (int)Math.Round(windowPoint.X),
- (int)Math.Round(windowPoint.Y));
-
- // Преобразуем клиентские координаты в экранные
- if (ClientToScreen(hwnd, ref point))
- {
- return new Point(point.X, point.Y);
- }
-
- return localPoint;
- }
- catch (Exception ex)
- {
- System.Diagnostics.Debug.WriteLine($"Ошибка преобразования координат: {ex.Message}");
- return localPoint;
- }
- }
-
- ///
- /// Получает хэндл окна WinUI.
- ///
- private static IntPtr GetWindowHandle(Window window)
- {
- // В WinUI 3 можно использовать WinRT API для получения хэндла
- // или альтернативные методы в зависимости от контекста
- var hwnd = WinRT.Interop.WindowNative.GetWindowHandle(window);
- return hwnd;
- }
-}
\ No newline at end of file
diff --git a/Lattice.UI.DragDrop.WinUI/Lattice.UI.DragDrop.WinUI.csproj b/Lattice.UI.DragDrop.WinUI/Lattice.UI.DragDrop.WinUI.csproj
deleted file mode 100644
index 9037dde..0000000
--- a/Lattice.UI.DragDrop.WinUI/Lattice.UI.DragDrop.WinUI.csproj
+++ /dev/null
@@ -1,25 +0,0 @@
-
-
- net8.0-windows10.0.19041.0;net9.0-windows10.0.19041.0;net10.0-windows10.0.19041.0
- 10.0.17763.0
- Lattice.UI.DragDrop.WinUI
- win-x86;win-x64;win-arm64
- true
- false
- true
- enable
- latest
- true
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/Lattice.UI.DragDrop.WinUI/README.md b/Lattice.UI.DragDrop.WinUI/README.md
deleted file mode 100644
index 25661af..0000000
--- a/Lattice.UI.DragDrop.WinUI/README.md
+++ /dev/null
@@ -1,313 +0,0 @@
-# Lattice.Core.DragDrop
-
-Библиотека для реализации drag-and-drop (перетаскивания) в приложениях на .NET.
-
-## 📋 Обзор
-
-Lattice.Core.DragDrop предоставляет полнофункциональную, асинхронную и потокобезопасную систему для реализации операций перетаскивания в пользовательских интерфейсах. Библиотека построена на принципах разделения ответственности и поддерживает сложные сценарии перетаскивания с минимальными усилиями со стороны разработчика.
-
-### ✨ Основные возможности
-
-- ✅ **Полностью асинхронный API** - все операции поддерживают async/await
-- ✅ **Потокобезопасность** - безопасная работа в многопоточных средах
-- ✅ **Расширяемая архитектура** - легко добавлять новые типы источников и целей
-- ✅ **Подробные события** - полный контроль над жизненным циклом операций
-- ✅ **Статистика и мониторинг** - встроенный сбор метрик использования
-- ✅ **Поддержка CancellationToken** - корректная отмена длительных операций
-- ✅ **Независимость от UI-фреймворков** - может использоваться с любым представлением
-
-## 🏗️ Архитектура
-
-### Основные компоненты
-
-#### 1. **IDragSource**
-Интерфейс для объектов, которые могут быть источником данных при перетаскивании. Определяет:
-- Возможность начала перетаскивания
-- Подготовку данных для передачи
-- Реакцию на завершение или отмену операции
-
-#### 2. **IDropTarget**
-Интерфейс для объектов, которые могут принимать сброшенные данные. Определяет:
-- Проверку совместимости данных
-- Визуальную обратную связь при наведении
-- Обработку сброшенных данных
-
-#### 3. **DragDropService**
-Центральный сервис, координирующий все операции. Отвечает за:
-- Регистрацию и управление целями сброса
-- Оркестрацию жизненного цикла операций
-- Распространение событий между компонентами
-- Сбор статистики и обработку ошибок
-
-#### 4. **Модели данных**
-- **DragInfo** - информация о начале перетаскивания
-- **DropInfo** - информация о потенциальном сбросе
-- **DragDropEffects** - перечисление возможных эффектов
-
-## 🚀 Быстрый старт
-
-### 1. Установка
-
-```csharp
-// Пример регистрации в DI-контейнере
-services.AddSingleton();
-```
-
-### 2. Создание источника перетаскивания
-
-```csharp
-public class ItemDragSource : IDragSource
-{
- private readonly Item _item;
-
- public ItemDragSource(Item item)
- {
- _item = item;
- }
-
- public async Task TryStartDragAsync(Point startPosition, CancellationToken ct)
- {
- // Проверяем, можно ли начать перетаскивание
- if (!_item.CanBeDragged)
- return null;
-
- // Создаем информацию о перетаскивании
- return new DragInfo(
- data: _item,
- allowedEffects: DragDropEffects.Copy | DragDropEffects.Move,
- startPosition: startPosition,
- source: this
- );
- }
-
- public async Task OnDragCompletedAsync(DragInfo dragInfo, DragDropEffects effects, CancellationToken ct)
- {
- if (effects == DragDropEffects.Move)
- {
- // Удаляем элемент при перемещении
- await _repository.DeleteAsync(_item.Id, ct);
- }
- }
-
- public Task OnDragCancelledAsync(DragInfo dragInfo, CancellationToken ct)
- {
- // Очистка ресурсов при отмене
- return Task.CompletedTask;
- }
-}
-```
-
-### 3. Создание цели сброса
-
-```csharp
-public class ContainerDropTarget : IDropTarget
-{
- private readonly ObservableCollection- _items;
-
- public async Task
CanAcceptDropAsync(DropInfo dropInfo, CancellationToken ct)
- {
- // Проверяем тип данных
- if (dropInfo.Data is not Item item)
- return false;
-
- // Проверяем бизнес-правила
- return await _validator.CanAddItemAsync(item, ct);
- }
-
- public async Task OnDragOverAsync(DropInfo dropInfo, CancellationToken ct)
- {
- // Обновляем визуальную обратную связь
- dropInfo.SuggestedEffects = DragDropEffects.Move;
- dropInfo.ShowVisualFeedback = true;
- }
-
- public async Task OnDropAsync(DropInfo dropInfo, CancellationToken ct)
- {
- var item = (Item)dropInfo.Data;
- await _items.AddAsync(item, ct);
-
- // Помечаем как обработанное
- dropInfo.MarkAsHandled();
- }
-
- public Task OnDragLeaveAsync(CancellationToken ct)
- {
- // Очищаем визуальную обратную связь
- return Task.CompletedTask;
- }
-}
-```
-
-### 4. Регистрация и использование сервиса
-
-```csharp
-public class MainViewModel
-{
- private readonly IDragDropService _dragDropService;
-
- public MainViewModel(IDragDropService dragDropService)
- {
- _dragDropService = dragDropService;
-
- // Регистрация цели сброса
- _targetId = _dragDropService.RegisterDropTarget(
- target: new ContainerDropTarget(_items),
- bounds: new Rect(0, 0, 400, 300),
- priority: 0,
- group: "main-container"
- );
-
- // Подписка на события
- _dragDropService.DragStarted += OnDragStarted;
- _dragDropService.DragCompleted += OnDragCompleted;
- _dragDropService.ErrorOccurred += OnError;
- }
-
- // Обработка мышиных событий
- public async Task OnMouseDown(Point position)
- {
- var source = new ItemDragSource(selectedItem);
- await _dragDropService.StartDragAsync(source, position);
- }
-
- public async Task OnMouseMove(Point position)
- {
- await _dragDropService.UpdateDragAsync(position);
- }
-
- public async Task OnMouseUp(Point position)
- {
- var effects = await _dragDropService.EndDragAsync(position);
- // Обработка результатов
- }
-}
-```
-
-## 📊 Статистика и мониторинг
-
-```csharp
-// Получение статистики использования
-var stats = _dragDropService.GetStats();
-
-Console.WriteLine($"Всего операций: {stats.TotalDragOperations}");
-Console.WriteLine($"Успешных сбросов: {stats.SuccessfulDrops}");
-Console.WriteLine($"Отменено операций: {stats.CancelledOperations}");
-Console.WriteLine($"Среднее время операции: {stats.AverageOperationTime}");
-```
-
-## ⚙️ Конфигурация
-
-### Параметры сервиса
-
-```csharp
-// Настройка через свойства сервиса
-_dragDropService.DragStartThreshold = 5.0; // Порог в пикселях
-_dragDropService.EnableAsyncOperations = true;
-_dragDropService.AsyncOperationTimeout = 3000; // 3 секунды
-```
-
-### Константы по умолчанию
-
-Все значения по умолчанию определены в классе `DragDropConstants`:
-- `DefaultDragThreshold`: 3.0 пикселей
-- `DefaultAsyncTimeout`: 5000 миллисекунд
-- `TargetLifetimeMinutes`: 10 минут
-
-## 🔧 Расширенные сценарии
-
-### Группировка целей сброса
-
-```csharp
-// Регистрация группы целей
-_dragDropService.RegisterDropTarget(target1, bounds1, group: "panel");
-_dragDropService.RegisterDropTarget(target2, bounds2, group: "panel");
-
-// Массовая отмена регистрации
-_dragDropService.UnregisterDropTargetsInGroup("panel");
-```
-
-### Обработка ошибок
-
-```csharp
-private void OnError(object sender, DragDropErrorEventArgs e)
-{
- _logger.LogError(e.Exception,
- "Ошибка в операции {Operation}",
- e.Operation);
-
- // Уведомление пользователя
- _notificationService.ShowError("Ошибка при перетаскивании");
-}
-```
-
-### Кастомизация эффектов
-
-```csharp
-// Использование расширений для работы с эффектами
-if (effects.CanCopy())
-{
- // Логика для копирования
-}
-
-if (effects.CanMove())
-{
- // Логика для перемещения
-}
-
-// Определение эффекта по модификаторам клавиш
-var effect = DragDropEffectsExtensions.GetEffectFromKeys(
- controlKey: Keyboard.IsControlDown,
- shiftKey: Keyboard.IsShiftDown,
- altKey: Keyboard.IsAltDown
-);
-```
-
-## 📝 Best Practices
-
-1. **Производительность**
- - Методы `CanAcceptDropAsync` и `OnDragOverAsync` вызываются часто, оптимизируйте их
- - Избегайте синхронных операций в обработчиках
- - Используйте кэширование при проверке типов данных
-
-2. **Безопасность**
- - Всегда проверяйте тип данных в `CanAcceptDropAsync`
- - Валидируйте бизнес-правила перед обработкой сброса
- - Используйте CancellationToken для отмены длительных операций
-
-3. **Пользовательский опыт**
- - Предоставляйте визуальную обратную связь через `DropInfo.ShowVisualFeedback`
- - Используйте `DropInfo.VisualFeedbackData` для кастомизации отображения
- - Обрабатывайте отмену операций для очистки временных данных
-
-## 🔄 Миграция
-
-### С версии 1.x на 2.0
-
-1. **Интерфейсы переименованы:**
- - `CanStartDragAsync` → `TryStartDragAsync`
- - `DragCompletedAsync` → `OnDragCompletedAsync`
- - `DragCancelledAsync` → `OnDragCancelledAsync`
-
-2. **Классы EventArgs объединены:**
- - Все события наследуются от `DragEventArgs`
- - Упрощена иерархия классов событий
-
-3. **Удалены устаревшие компоненты:**
- - `AsyncDragDropUtilities` удален
- - `ServiceCollectionExtensions` удален
-
-## 📄 Лицензия
-
-Библиотека распространяется под лицензией MIT. Подробности см. в файле LICENSE.
-
-## 🤝 Вклад в разработку
-
-Мы приветствуем вклад в развитие библиотеки. Перед отправкой pull request ознакомьтесь с руководством по контрибьютингу.
-
-## 🐛 Отчеты об ошибках
-
-Для сообщения об ошибках используйте Issues на GitHub. Пожалуйста, указывайте:
-- Версию библиотеки
-- Шаги для воспроизведения
-- Ожидаемое и фактическое поведение
-- Пример кода для демонстрации проблемы
\ No newline at end of file
diff --git a/Lattice.UI.DragDrop.WinUI/Services/WinUIDragDropHost.cs b/Lattice.UI.DragDrop.WinUI/Services/WinUIDragDropHost.cs
deleted file mode 100644
index ac96d44..0000000
--- a/Lattice.UI.DragDrop.WinUI/Services/WinUIDragDropHost.cs
+++ /dev/null
@@ -1,160 +0,0 @@
-using Lattice.Core.Geometry;
-using Lattice.UI.DragDrop.Abstractions;
-using Lattice.UI.DragDrop.WinUI.Controls;
-using Microsoft.UI.Xaml;
-using Microsoft.UI.Xaml.Controls;
-using System;
-
-namespace Lattice.UI.DragDrop.WinUI.Services;
-
-///
-/// Хост для управления визуальными элементами перетаскивания в окне WinUI.
-///
-///
-///
-/// Этот класс отвечает за отображение и управление визуальными элементами
-/// во время операций перетаскивания, включая:
-/// - Drag-визуализацию (элемент, следующий за курсором)
-/// - Drop-превью (подсветка областей сброса)
-///
-///
-/// Хост создает оверлейный слой поверх всего содержимого окна для корректного
-/// отображения визуальных элементов поверх других UI-компонентов.
-///
-///
-public sealed class WinUIDragDropHost : IDragDropHost, IDisposable
-{
- private DragDropOverlay? _overlay;
- private Window? _window;
- private bool _disposed;
-
- ///
- /// Инициализирует хост для работы с указанным окном.
- ///
- /// Окно, в котором будет работать перетаскивание.
- ///
- /// Выбрасывается, если равен null.
- ///
- ///
- /// Выбрасывается, если хост уже был удален.
- ///
- ///
- ///
- /// Этот метод создает оверлейный слой и добавляет его в визуальное дерево окна.
- /// Если содержимое окна не является , создается контейнер .
- ///
- ///
- /// Метод должен быть вызван один раз перед использованием других методов хоста.
- ///
- ///
- public void Initialize(Window window)
- {
- if (_disposed) throw new ObjectDisposedException(nameof(WinUIDragDropHost));
-
- _window = window ?? throw new ArgumentNullException(nameof(window));
-
- // Создаем оверлей
- _overlay = new DragDropOverlay();
-
- // Добавляем оверлей в окно
- if (_window.Content is Panel panel)
- {
- panel.Children.Add(_overlay);
- }
- else
- {
- // Если контент не Panel, создаем Grid
- var grid = new Grid();
- grid.Children.Add(_window.Content as UIElement ?? new Grid());
- grid.Children.Add(_overlay);
- _window.Content = grid;
- }
- }
-
- ///
- /// Отображает визуальное представление перетаскиваемого элемента.
- ///
- /// Визуальный элемент для отображения.
- /// Позиция отображения в координатах экрана.
- ///
- /// Визуальный элемент будет отображен на оверлейном слое в указанной позиции
- /// и будет следовать за курсором при обновлении через .
- ///
- public void ShowDragVisual(object dragVisual, Point position)
- {
- if (_overlay == null || _disposed) return;
-
- if (dragVisual is UIElement element)
- {
- _overlay.ShowDragVisual(element, position.X, position.Y);
- }
- }
-
- ///
- public void UpdateDragVisualPosition(object dragVisual, Point position)
- {
- if (_overlay == null || _disposed) return;
-
- if (dragVisual is UIElement element)
- {
- _overlay.UpdateDragVisualPosition(element, position.X, position.Y);
- }
- }
-
- ///
- public void HideDragVisual(object dragVisual)
- {
- if (_overlay == null || _disposed) return;
-
- if (dragVisual is UIElement element)
- {
- _overlay.HideDragVisual(element);
- }
- else
- {
- // Скрываем все визуальные элементы
- var current = _overlay.GetCurrentDragVisual();
- if (current != null)
- {
- _overlay.HideDragVisual(current);
- }
- }
- }
-
- ///
- public void ShowDropAdorner(IDropVisualAdorner adorner)
- {
- if (_overlay == null || _disposed) return;
-
- if (adorner is DropPreviewAdorner dropAdorner)
- {
- // Для WinUI пока просто игнорируем
- }
- }
-
- ///
- public void HideDropAdorner(IDropVisualAdorner adorner)
- {
- if (_overlay == null || _disposed) return;
-
- _overlay.HideAllDropPreviews();
- }
-
- ///
- public void Dispose()
- {
- if (_disposed) return;
-
- if (_overlay != null && _window?.Content is Panel panel)
- {
- panel.Children.Remove(_overlay);
- _overlay.ClearAllVisuals();
- }
-
- _overlay = null;
- _window = null;
- _disposed = true;
-
- GC.SuppressFinalize(this);
- }
-}
\ No newline at end of file
diff --git a/Lattice.UI.DragDrop.WinUI/Services/WinUIDragDropManager.cs b/Lattice.UI.DragDrop.WinUI/Services/WinUIDragDropManager.cs
deleted file mode 100644
index 2ce3d99..0000000
--- a/Lattice.UI.DragDrop.WinUI/Services/WinUIDragDropManager.cs
+++ /dev/null
@@ -1,625 +0,0 @@
-using Lattice.Core.DragDrop.Services;
-using Lattice.Core.Geometry;
-using Lattice.UI.DragDrop.WinUI.Behaviors;
-using Lattice.UI.DragDrop.WinUI.Controls;
-using Microsoft.UI.Xaml;
-using System;
-using System.Collections.Generic;
-
-namespace Lattice.UI.DragDrop.WinUI.Services;
-
-///
-/// Центральный менеджер для управления операциями drag-and-drop в WinUI приложении.
-/// Координирует работу источников и целей перетаскивания, управляет визуальной обратной связью
-/// и обеспечивает согласованное взаимодействие всех компонентов системы.
-///
-///
-///
-/// реализует шаблон Singleton и служит единой точкой
-/// входа для настройки и управления операциями перетаскивания в WinUI-приложении.
-///
-///
-/// Основные функции менеджера:
-///
-/// - Инициализация системы перетаскивания для конкретного окна
-/// - Регистрация и отслеживание источников и целей перетаскивания
-/// - Управление жизненным циклом операций перетаскивания
-/// - Обеспечение визуальной обратной связи через
-/// - Координация взаимодействия между
и
-///
-///
-///
-/// Для использования менеджера необходимо:
-///
-/// - Вызвать
при создании главного окна приложения
-/// - Настроить элементы как источники или цели через методы
и
-/// - Использовать attached properties для декларативной настройки в XAML
-///
-///
-///
-///
-/// // Инициализация в коде
-/// public partial class MainWindow : Window
-/// {
-/// private WinUIDragDropManager _manager;
-///
-/// public MainWindow()
-/// {
-/// InitializeComponent();
-/// _manager = WinUIDragDropManager.Instance;
-/// _manager.Initialize(this);
-///
-/// // Настройка элементов
-/// _manager.MakeDragSource(myElement, myData);
-/// _manager.MakeDropTarget(myDropArea);
-/// }
-/// }
-///
-/// // Или через attached properties в XAML
-/// <Border x:Name="DragElement"
-/// local:DragDropProperties.IsDragSource="True"
-/// local:DragDropProperties.DragData="{Binding MyData}" />
-/// <Border x:Name="DropArea"
-/// local:DragDropProperties.IsDropTarget="True" />
-///
-///
-///
-public sealed class WinUIDragDropManager : IDisposable
-{
- #region Singleton Implementation
-
- private static WinUIDragDropManager? _instance;
- private static readonly object _lockObject = new();
-
- ///
- /// Получает единственный экземпляр .
- /// Реализует шаблон Singleton с ленивой инициализацией и потокобезопасностью.
- ///
- ///
- /// Единственный экземпляр менеджера перетаскивания для всего приложения.
- /// Если экземпляр еще не создан, он будет инициализирован при первом обращении.
- ///
- ///
- /// Использование Singleton гарантирует, что во всем приложении существует только один
- /// экземпляр менеджера, что обеспечивает согласованное управление всеми операциями
- /// перетаскивания и предотвращает конфликты между разными компонентами системы.
- ///
- public static WinUIDragDropManager Instance
- {
- get
- {
- if (_instance == null)
- {
- lock (_lockObject)
- {
- _instance ??= new WinUIDragDropManager();
- }
- }
- return _instance;
- }
- }
-
- #endregion
-
- #region Fields
-
- private readonly IDragDropService _dragDropService;
- private readonly WinUIDragDropHost _host;
- private readonly Dictionary _dragSources = new();
- private readonly Dictionary _dropTargets = new();
- private DragAdorner? _currentDragVisual;
- private bool _disposed;
- private bool _initialized;
-
- #endregion
-
- #region Properties
-
- ///
- /// Получает сервис перетаскивания, используемый менеджером для координации операций.
- ///
- ///
- /// Экземпляр , через который менеджер взаимодействует
- /// с ядром системы перетаскивания.
- ///
- ///
- /// Этот сервис предоставляет низкоуровневый API для управления операциями перетаскивания
- /// и может использоваться для расширенной настройки системы.
- ///
- public IDragDropService DragDropService => _dragDropService;
-
- ///
- /// Получает хост для управления визуальными элементами перетаскивания.
- ///
- ///
- /// Экземпляр , отвечающий за отображение и позиционирование
- /// визуальной обратной связи во время операций перетаскивания.
- ///
- public WinUIDragDropHost Host => _host;
-
- ///
- /// Получает или задает смещение визуального элемента перетаскивания относительно курсора.
- ///
- ///
- /// Точка, определяющая смещение по осям X и Y в пикселях.
- /// Значение по умолчанию: (-20, -20).
- ///
- ///
- ///
- /// Отрицательные значения смещают визуальный элемент вверх и влево относительно курсора,
- /// что является стандартным поведением в большинстве систем drag-and-drop.
- ///
- ///
- /// Настройка смещения позволяет:
- ///
- /// - Предотвратить перекрытие курсора визуальным элементом
- /// - Обеспечить лучшую видимость области под курсором
- /// - Создать более естественное визуальное восприятие
- ///
- ///
- ///
- ///
- /// // Настройка смещения через фабрику
- /// var manager = WinUIDragDropFactory.CreateManager(window, m =>
- /// {
- /// m.DragVisualOffset = new Point(-15, -15); // Более близко к курсору
- /// });
- ///
- ///
- ///
- public Point DragVisualOffset { get; set; } = new Point(-20, -20);
-
- ///
- /// Получает значение, указывающее, инициализирован ли менеджер.
- ///
- ///
- /// true, если метод был успешно вызван;
- /// в противном случае — false.
- ///
- ///
- /// Проверка этого свойства позволяет избежать повторной инициализации менеджера
- /// и гарантирует, что система перетаскивания готова к использованию.
- ///
- public bool IsInitialized => _initialized;
-
- #endregion
-
- #region Constructor
-
- ///
- /// Инициализирует новый экземпляр класса .
- /// Конструктор является приватным в соответствии с шаблоном Singleton.
- ///
- ///
- ///
- /// Внутренний конструктор создает:
- ///
- /// - Экземпляр
для управления операциями перетаскивания
- /// - Экземпляр
для визуальной обратной связи
- ///
- ///
- ///
- /// Для получения экземпляра менеджера используйте свойство .
- ///
- ///
- private WinUIDragDropManager()
- {
- _dragDropService = new DragDropService();
- _host = new WinUIDragDropHost();
- }
-
- #endregion
-
- #region Public Methods
-
- ///
- /// Инициализирует систему перетаскивания для указанного окна WinUI.
- /// Этот метод должен быть вызван один раз при запуске приложения.
- ///
- ///
- /// Главное окно приложения, для которого настраивается система перетаскивания.
- /// Не может быть null.
- ///
- ///
- /// Выбрасывается, если равен null.
- ///
- ///
- /// Выбрасывается, если менеджер уже инициализирован или был удален.
- ///
- ///
- ///
- /// Этот метод выполняет следующие действия:
- ///
- /// - Настраивает хост визуальных элементов для работы с указанным окном
- /// - Подписывается на события сервиса перетаскивания для управления визуальной обратной связью
- /// - Помечает менеджер как инициализированный
- ///
- ///
- ///
- /// Метод должен быть вызван до использования любых других методов менеджера.
- /// Рекомендуется вызывать его в конструкторе главного окна приложения.
- ///
- ///
- ///
- /// public MainWindow()
- /// {
- /// InitializeComponent();
- /// WinUIDragDropManager.Instance.Initialize(this);
- /// }
- ///
- ///
- ///
- public void Initialize(Window window)
- {
- if (_disposed)
- throw new ObjectDisposedException(nameof(WinUIDragDropManager));
-
- if (_initialized)
- throw new InvalidOperationException("Менеджер уже инициализирован.");
-
- if (window == null)
- throw new ArgumentNullException(nameof(window));
-
- // Инициализируем хост для работы с окном
- _host.Initialize(window);
-
- // Подписываемся на события сервиса перетаскивания
- _dragDropService.DragStarted += OnDragStarted;
- _dragDropService.DragUpdated += OnDragUpdated;
- _dragDropService.DragCompleted += OnDragCompleted;
- _dragDropService.DragCancelled += OnDragCancelled;
-
- _initialized = true;
- }
-
- ///
- /// Настраивает указанный элемент как источник перетаскивания.
- ///
- ///
- /// Элемент
- ///
- /// Данные, которые будут перетаскиваться. Может быть null.
- /// Если не указано, используются или
- /// элемента.
- ///
- ///
- /// Выбрасывается, если равен null.
- ///
- ///
- /// Выбрасывается, если менеджер не инициализирован или был удален.
- ///
- ///
- ///
- /// После вызова этого метода элемент приобретает следующие возможности:
- ///
- /// - Реагирует на жесты перетаскивания (удержание и перемещение указателя)
- /// - Предоставляет указанные данные для перетаскивания
- /// - Интегрируется с системой визуальной обратной связи
- ///
- ///
- ///
- /// Если элемент уже зарегистрирован как источник перетаскивания, метод не выполняет действий.
- ///
- ///
- /// Для отмены регистрации используйте метод .
- ///
- ///
- public void MakeDragSource(FrameworkElement element, object? dragData = null)
- {
- ValidateManagerState();
-
- if (element == null)
- throw new ArgumentNullException(nameof(element));
-
- // Если элемент уже зарегистрирован, ничего не делаем
- if (_dragSources.ContainsKey(element))
- return;
-
- // Создаем и настраиваем поведение
- var behavior = new WinUIDragSourceBehavior(_dragDropService, _host);
- behavior.Attach(element, dragData);
- _dragSources[element] = behavior;
- }
-
- ///
- /// Настраивает указанный элемент как цель сброса.
- ///
- ///
- /// Элемент , который должен стать целью сброса.
- /// Не может быть null.
- ///
- ///
- /// Выбрасывается, если равен null.
- ///
- ///
- /// Выбрасывается, если менеджер не инициализирован или был удален.
- ///
- ///
- ///
- /// После вызова этого метода элемент приобретает следующие возможности:
- ///
- /// - Принимает данные, сбрасываемые пользователем
- /// - Предоставляет визуальную обратную связь при наведении
- /// - Автоматически обновляет свои границы при изменении размера или позиции
- ///
- ///
- ///
- /// По умолчанию цель принимает данные любого типа. Для настройки фильтрации типов
- /// используйте методы и
- /// .
- ///
- ///
- /// Если элемент уже зарегистрирован как цель сброса, метод не выполняет действий.
- ///
- ///
- /// Для отмены регистрации используйте метод .
- ///
- ///
- public void MakeDropTarget(FrameworkElement element)
- {
- ValidateManagerState();
-
- if (element == null)
- throw new ArgumentNullException(nameof(element));
-
- // Если элемент уже зарегистрирован, ничего не делаем
- if (_dropTargets.ContainsKey(element))
- return;
-
- // Создаем и настраиваем поведение
- var behavior = new WinUIDropTargetBehavior(_dragDropService, _host);
- behavior.Attach(element);
- _dropTargets[element] = behavior;
- }
-
- ///
- /// Удаляет возможность перетаскивания у указанного элемента.
- ///
- ///
- /// Элемент, у которого нужно отключить возможность перетаскивания.
- /// Если элемент не зарегистрирован как источник перетаскивания, метод не выполняет действий.
- ///
- ///
- ///
- /// Этот метод выполняет следующие действия:
- ///
- /// - Открепляет поведение перетаскивания от элемента
- /// - Отписывается от всех событий элемента
- /// - Удаляет элемент из внутреннего словаря источников
- /// - Освобождает ресурсы, связанные с поведением
- ///
- ///
- ///
- /// Метод безопасен для вызова даже если элемент не был зарегистрирован как источник.
- ///
- ///
- public void RemoveDragSource(FrameworkElement element)
- {
- if (element == null || _disposed || !_dragSources.ContainsKey(element))
- return;
-
- if (_dragSources.Remove(element, out var behavior))
- {
- behavior.Detach();
- }
- }
-
- ///
- /// Удаляет возможность сброса у указанного элемента.
- ///
- ///
- /// Элемент, у которого нужно отключить возможность сброса.
- /// Если элемент не зарегистрирован как цель сброса, метод не выполняет действий.
- ///
- ///
- ///
- /// Этот метод выполняет следующие действия:
- ///
- /// - Открепляет поведение цели сброса от элемента
- /// - Восстанавливает свойство
= false
- /// - Удаляет элемент из внутреннего словаря целей
- /// - Освобождает ресурсы, связанные с поведением
- ///
- ///
- ///
- /// Метод безопасен для вызова даже если элемент не был зарегистрирован как цель.
- ///
- ///
- public void RemoveDropTarget(FrameworkElement element)
- {
- if (element == null || _disposed || !_dropTargets.ContainsKey(element))
- return;
-
- if (_dropTargets.Remove(element, out var behavior))
- {
- behavior.Detach();
- }
- }
-
- ///
- /// Очищает все регистрации источников и целей перетаскивания.
- ///
- ///
- ///
- /// Этот метод полезен в следующих сценариях:
- ///
- /// - При перезагрузке содержимого интерфейса
- /// - При смене контекста данных
- /// - При освобождении ресурсов перед удалением менеджера
- ///
- ///
- ///
- /// После вызова этого метода все элементы теряют возможность участвовать в операциях
- /// перетаскивания. Для восстановления функциональности необходимо повторно
- /// зарегистрировать элементы через и .
- ///
- ///
- public void Clear()
- {
- if (_disposed) return;
-
- // Открепляем все источники
- foreach (var behavior in _dragSources.Values)
- {
- behavior.Detach();
- }
- _dragSources.Clear();
-
- // Открепляем все цели
- foreach (var behavior in _dropTargets.Values)
- {
- behavior.Detach();
- }
- _dropTargets.Clear();
- }
-
- #endregion
-
- #region Event Handlers
-
- ///
- /// Обрабатывает событие начала перетаскивания.
- /// Создает и отображает визуальный элемент для обратной связи.
- ///
- private void OnDragStarted(object? sender, Core.DragDrop.Services.DragStartedEventArgs e)
- {
- // Создаем визуальное представление перетаскивания
- _currentDragVisual = new DragAdorner
- {
- DragData = e.DragInfo.Data,
- Opacity = 0.8
- };
-
- // Рассчитываем позицию с учетом смещения
- var position = new Point(
- e.Position.X + DragVisualOffset.X,
- e.Position.Y + DragVisualOffset.Y
- );
-
- // Обновляем позицию и показываем элемент
- _currentDragVisual.UpdatePosition(position);
- _host.ShowDragVisual(_currentDragVisual, position);
- }
-
- ///
- /// Обрабатывает событие обновления позиции перетаскивания.
- /// Обновляет позицию визуального элемента для следования за курсором.
- ///
- private void OnDragUpdated(object? sender, Core.DragDrop.Services.DragUpdatedEventArgs e)
- {
- if (_currentDragVisual != null)
- {
- var position = new Point(
- e.Position.X + DragVisualOffset.X,
- e.Position.Y + DragVisualOffset.Y
- );
-
- _currentDragVisual.UpdatePosition(position);
- }
- }
-
- ///
- /// Обрабатывает событие завершения перетаскивания.
- /// Очищает визуальные элементы и восстанавливает состояние.
- ///
- private void OnDragCompleted(object? sender, Core.DragDrop.Services.DragCompletedEventArgs e)
- {
- CleanupDragVisual();
- }
-
- ///
- /// Обрабатывает событие отмены перетаскивания.
- /// Очищает визуальные элементы и восстанавливает состояние.
- ///
- private void OnDragCancelled(object? sender, Core.DragDrop.Services.DragCancelledEventArgs e)
- {
- CleanupDragVisual();
- }
-
- ///
- /// Освобождает ресурсы визуального элемента перетаскивания.
- ///
- private void CleanupDragVisual()
- {
- if (_currentDragVisual != null)
- {
- _currentDragVisual.Hide();
- _currentDragVisual = null;
- }
- }
-
- #endregion
-
- #region Helper Methods
-
- ///
- /// Проверяет состояние менеджера перед выполнением операций.
- ///
- ///
- /// Выбрасывается, если менеджер был удален.
- ///
- ///
- /// Выбрасывается, если менеджер не инициализирован.
- ///
- private void ValidateManagerState()
- {
- if (_disposed)
- throw new ObjectDisposedException(nameof(WinUIDragDropManager));
-
- if (!_initialized)
- throw new InvalidOperationException(
- "Менеджер не инициализирован. Вызовите метод Initialize перед использованием.");
- }
-
- #endregion
-
- #region IDisposable Implementation
-
- ///
- /// Освобождает все ресурсы, используемые .
- ///
- ///
- ///
- /// Этот метод выполняет следующие действия:
- ///
- /// - Отписывается от всех событий сервиса перетаскивания
- /// - Очищает все зарегистрированные источники и цели
- /// - Освобождает ресурсы хоста визуальных элементов
- /// - Освобождает ресурсы сервиса перетаскивания
- ///
- ///
- ///
- /// После вызова этого метода менеджер перестает быть пригодным для использования.
- /// Попытка использовать методы менеджера после удаления приведет к исключению
- /// .
- ///
- ///
- /// Метод безопасен для многократного вызова.
- ///
- ///
- public void Dispose()
- {
- if (_disposed) return;
-
- // Отписываемся от событий
- _dragDropService.DragStarted -= OnDragStarted;
- _dragDropService.DragUpdated -= OnDragUpdated;
- _dragDropService.DragCompleted -= OnDragCompleted;
- _dragDropService.DragCancelled -= OnDragCancelled;
-
- // Очищаем все регистрации
- Clear();
-
- // Освобождаем ресурсы
- _dragDropService.Dispose();
- _host.Dispose();
-
- _disposed = true;
- _initialized = false;
- GC.SuppressFinalize(this);
- }
-
- #endregion
-}
\ No newline at end of file
diff --git a/Lattice.UI.DragDrop.WinUI/Services/WinUIDragVisualProvider.cs b/Lattice.UI.DragDrop.WinUI/Services/WinUIDragVisualProvider.cs
deleted file mode 100644
index 64baeb0..0000000
--- a/Lattice.UI.DragDrop.WinUI/Services/WinUIDragVisualProvider.cs
+++ /dev/null
@@ -1,44 +0,0 @@
-using Lattice.Core.DragDrop.Models;
-using Lattice.Core.Geometry;
-using Lattice.UI.DragDrop.Abstractions;
-using Lattice.UI.DragDrop.WinUI.Controls;
-
-namespace Lattice.UI.DragDrop.WinUI.Services;
-
-public class WinUIDragVisualProvider : IDragVisualProvider
-{
- private DragAdorner? _currentAdorner;
-
- public object CreateDragVisual(DragInfo dragInfo, Point initialPosition)
- {
- // Создаем DragAdorner на основе данных
- _currentAdorner = new DragAdorner
- {
- DragData = dragInfo.Data,
- OpacityLevel = 0.8
- };
-
- // Настраиваем начальную позицию
- _currentAdorner.UpdatePosition(initialPosition);
- _currentAdorner.Show();
-
- return _currentAdorner;
- }
-
- public void UpdateDragVisualPosition(object dragVisual, Point position)
- {
- if (dragVisual is DragAdorner adorner)
- {
- adorner.UpdatePosition(position);
- }
- }
-
- public void ReleaseDragVisual(object dragVisual)
- {
- if (dragVisual is DragAdorner adorner)
- {
- adorner.Hide();
- _currentAdorner = null;
- }
- }
-}
\ No newline at end of file
diff --git a/Lattice.UI.DragDrop.WinUI/Themes/DragAdorner.xaml b/Lattice.UI.DragDrop.WinUI/Themes/DragAdorner.xaml
deleted file mode 100644
index 1ed2404..0000000
--- a/Lattice.UI.DragDrop.WinUI/Themes/DragAdorner.xaml
+++ /dev/null
@@ -1,48 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/Lattice.UI.DragDrop.WinUI/Themes/DragDropStyles.xaml b/Lattice.UI.DragDrop.WinUI/Themes/DragDropStyles.xaml
deleted file mode 100644
index 81d8f24..0000000
--- a/Lattice.UI.DragDrop.WinUI/Themes/DragDropStyles.xaml
+++ /dev/null
@@ -1,158 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/Lattice.UI.DragDrop.WinUI/Themes/DropPreviewAdorner.xaml b/Lattice.UI.DragDrop.WinUI/Themes/DropPreviewAdorner.xaml
deleted file mode 100644
index 0642789..0000000
--- a/Lattice.UI.DragDrop.WinUI/Themes/DropPreviewAdorner.xaml
+++ /dev/null
@@ -1,56 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/Lattice.UI.DragDrop.WinUI/Themes/Generic.xaml b/Lattice.UI.DragDrop.WinUI/Themes/Generic.xaml
deleted file mode 100644
index 2cd189e..0000000
--- a/Lattice.UI.DragDrop.WinUI/Themes/Generic.xaml
+++ /dev/null
@@ -1,23 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/Lattice.UI.DragDrop/Abstractions/IDragDropHost.cs b/Lattice.UI.DragDrop/Abstractions/IDragDropHost.cs
deleted file mode 100644
index 427c590..0000000
--- a/Lattice.UI.DragDrop/Abstractions/IDragDropHost.cs
+++ /dev/null
@@ -1,81 +0,0 @@
-using Lattice.Core.Geometry;
-
-namespace Lattice.UI.DragDrop.Abstractions;
-
-///
-/// Хост для отображения визуальных элементов перетаскивания.
-///
-///
-///
-/// Интерфейс предоставляет абстракцию для управления визуальными элементами
-/// перетаскивания в различных UI-фреймворках (WPF, Avalonia, MAUI и т.д.).
-///
-///
-/// Реализация должна обеспечивать корректное отображение визуальных элементов
-/// поверх других элементов UI и их своевременное удаление при завершении операций.
-///
-///
-public interface IDragDropHost
-{
- ///
- /// Показывает визуальное представление перетаскиваемого элемента.
- ///
- /// Визуальное представление элемента для перетаскивания.
- /// Начальная позиция визуального элемента в экранных координатах.
- ///
- ///
- /// Метод должен отобразить переданный визуальный элемент в указанной позиции.
- /// Визуальный элемент должен следовать за курсором мыши при обновлении через .
- ///
- ///
- /// Визуальный элемент должен отображаться поверх всех других элементов интерфейса.
- ///
- ///
- void ShowDragVisual(object dragVisual, Point position);
-
- ///
- /// Обновляет позицию визуального представления перетаскивания.
- ///
- /// Визуальное представление, позиция которого должна быть обновлена.
- /// Новая позиция в экранных координатах.
- ///
- /// Метод должен обновлять позицию уже отображаемого визуального элемента
- /// с минимальной задержкой для плавного перемещения.
- ///
- void UpdateDragVisualPosition(object dragVisual, Point position);
-
- ///
- /// Скрывает визуальное представление перетаскивания.
- ///
- /// Визуальное представление для скрытия.
- ///
- /// После вызова этого метода визуальный элемент должен быть полностью
- /// удален из визуального дерева и его ресурсы освобождены.
- ///
- void HideDragVisual(object dragVisual);
-
- ///
- /// Показывает визуальную обратную связь для цели сброса.
- ///
- /// Элемент обратной связи для отображения.
- ///
- ///
- /// Метод должен отобразить элемент обратной связи (например, подсветку, рамку или индикатор позиции)
- /// для визуального указания возможности сброса на целевой элемент.
- ///
- ///
- /// Элемент обратной связи должен отображаться поверх целевого элемента, но под перетаскиваемым визуальным элементом.
- ///
- ///
- void ShowDropAdorner(IDropVisualAdorner adorner);
-
- ///
- /// Скрывает визуальную обратную связь для цели сброса.
- ///
- /// Элемент обратной связи для скрытия.
- ///
- /// После вызова этого метода элемент обратной связи должен быть
- /// удален из визуального дерева и его ресурсы освобождены.
- ///
- void HideDropAdorner(IDropVisualAdorner adorner);
-}
\ No newline at end of file
diff --git a/Lattice.UI.DragDrop/Abstractions/IDragVisualProvider.cs b/Lattice.UI.DragDrop/Abstractions/IDragVisualProvider.cs
deleted file mode 100644
index e307125..0000000
--- a/Lattice.UI.DragDrop/Abstractions/IDragVisualProvider.cs
+++ /dev/null
@@ -1,67 +0,0 @@
-using Lattice.Core.DragDrop.Models;
-using Lattice.Core.Geometry;
-
-namespace Lattice.UI.DragDrop.Abstractions;
-
-///
-/// Поставщик визуального представления для перетаскиваемого элемента.
-///
-///
-///
-/// Интерфейс предоставляет абстракцию для создания и управления визуальными
-/// представлениями элементов при операции перетаскивания.
-///
-///
-/// Реализации могут предоставлять различные стили визуального представления:
-/// от простого клонирования оригинального элемента до сложных анимированных представлений.
-///
-///
-public interface IDragVisualProvider
-{
- ///
- /// Создает визуальное представление для перетаскивания на основе информации о перетаскивании.
- ///
- /// Информация о перетаскивании, содержащая данные и метаданные операции.
- /// Начальная позиция в экранных координатах.
- /// Объект, представляющий визуальное отображение для перетаскивания.
- ///
- ///
- /// Созданный визуальный элемент должен:
- /// 1. Отображать репрезентативное представление перетаскиваемых данных
- /// 2. Иметь прозрачный фон или альфа-канал для плавного отображения
- /// 3. Быть легковесным для обеспечения плавной анимации
- /// 4. Поддерживать возможность изменения позиции через
- ///
- ///
- /// Метод может возвращать null, если визуальное представление не требуется.
- ///
- ///
- object? CreateDragVisual(DragInfo dragInfo, Point initialPosition);
-
- ///
- /// Обновляет позицию визуального представления перетаскивания.
- ///
- /// Визуальное представление, созданное методом .
- /// Новая позиция в экранных координатах.
- ///
- /// Метод должен обновить позицию визуального элемента максимально эффективно,
- /// так как он вызывается часто во время операции перетаскивания.
- ///
- void UpdateDragVisualPosition(object dragVisual, Point position);
-
- ///
- /// Освобождает ресурсы визуального представления.
- ///
- /// Визуальное представление для освобождения.
- ///
- ///
- /// Метод должен освободить все ресурсы, связанные с визуальным представлением,
- /// включая графические ресурсы, подписки на события и временные данные.
- ///
- ///
- /// Этот метод гарантированно вызывается после завершения операции перетаскивания,
- /// независимо от её успешности.
- ///
- ///
- void ReleaseDragVisual(object dragVisual);
-}
\ No newline at end of file
diff --git a/Lattice.UI.DragDrop/Abstractions/IDropVisualAdorner.cs b/Lattice.UI.DragDrop/Abstractions/IDropVisualAdorner.cs
deleted file mode 100644
index 49f0b47..0000000
--- a/Lattice.UI.DragDrop/Abstractions/IDropVisualAdorner.cs
+++ /dev/null
@@ -1,59 +0,0 @@
-using Lattice.Core.DragDrop.Models;
-using Lattice.Core.Geometry;
-
-namespace Lattice.UI.DragDrop.Abstractions;
-
-///
-/// Визуальный элемент, показывающий обратную связь при наведении на цель сброса.
-///
-///
-///
-/// Интерфейс предоставляет абстракцию для визуальных индикаторов, которые
-/// показывают пользователю возможность сброса данных на целевом элементе.
-///
-///
-/// Реализации могут предоставлять различные типы визуальной обратной связи:
-/// подсветку элемента, отображение индикатора позиции, изменение курсора и т.д.
-///
-///
-public interface IDropVisualAdorner
-{
- ///
- /// Показывает визуальную обратную связь для цели сброса.
- ///
- /// Информация о потенциальном сбросе, включая данные и позицию.
- /// Границы целевого элемента в экранных координатах.
- ///
- ///
- /// Метод должен отобразить визуальную обратную связь, соответствующую типу данных
- /// и контексту сброса. Обратная связь должна явно указывать на возможность
- /// сброса и ожидаемый эффект (копирование, перемещение и т.д.).
- ///
- ///
- /// Обратная связь должна учитывать свойства
- /// и для кастомизации отображения.
- ///
- ///
- void Show(DropInfo dropInfo, Rect targetBounds);
-
- ///
- /// Обновляет позицию и состояние визуальной обратной связи.
- ///
- /// Текущая информация о сбросе, включая обновленную позицию и состояние.
- ///
- /// Метод вызывается при каждом перемещении курсора над целью и должен
- /// обновлять визуальную обратную связь в соответствии с новой позицией
- /// и состоянием операции.
- ///
- void Update(DropInfo dropInfo);
-
- ///
- /// Скрывает визуальную обратную связь.
- ///
- ///
- /// Метод должен скрыть и очистить все визуальные элементы обратной связи.
- /// После вызова этого метода ресурсы могут быть освобождены или переиспользованы
- /// для следующих операций.
- ///
- void Hide();
-}
\ No newline at end of file
diff --git a/Lattice.UI.DragDrop/Behaviors/DragSourceBehaviorBase.cs b/Lattice.UI.DragDrop/Behaviors/DragSourceBehaviorBase.cs
deleted file mode 100644
index 60ec843..0000000
--- a/Lattice.UI.DragDrop/Behaviors/DragSourceBehaviorBase.cs
+++ /dev/null
@@ -1,366 +0,0 @@
-using Lattice.Core.DragDrop.Abstractions;
-using Lattice.Core.DragDrop.Models;
-using Lattice.Core.DragDrop.Services;
-using Lattice.Core.Geometry;
-using System;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace Lattice.UI.DragDrop.Behaviors;
-
-///
-/// Базовый класс поведения источника перетаскивания для UI элементов.
-///
-/// Тип UI элемента, к которому прикрепляется поведение.
-///
-///
-/// Этот класс предоставляет базовую реализацию поведения перетаскивания для UI элементов.
-/// Он обрабатывает события мыши/тач, управляет порогом начала перетаскивания и
-/// интегрируется с сервисом из ядра.
-///
-///
-/// Производные классы должны реализовать абстрактные методы для конкретной
-/// UI-платформы и предоставить логику создания информации о перетаскивании.
-///
-///
-public abstract class DragSourceBehaviorBase : IDragSource
- where TElement : class
-{
- private IDragDropService? _dragDropService;
- private Point _dragStartPosition;
- private bool _isDragging;
- private TElement? _associatedElement;
- private CancellationTokenSource? _dragCancellationTokenSource;
-
- ///
- /// Получает или задает связанный UI элемент.
- ///
- ///
- /// Элемент UI, к которому прикреплено поведение перетаскивания.
- /// При изменении значения автоматически выполняется переподключение событий.
- ///
- protected TElement? AssociatedElement
- {
- get => _associatedElement;
- set
- {
- if (_associatedElement != value)
- {
- DetachFromElement();
- _associatedElement = value;
- AttachToElement();
- }
- }
- }
-
- ///
- /// Получает сервис перетаскивания из контейнера зависимостей.
- ///
- ///
- /// Экземпляр , используемый для управления операциями перетаскивания.
- ///
- protected IDragDropService DragDropService { get; }
-
- ///
- /// Получает значение, указывающее, выполняется ли в данный момент операция перетаскивания.
- ///
- protected bool IsDragging => _isDragging;
-
- ///
- /// Инициализирует новый экземпляр класса .
- ///
- /// Сервис перетаскивания.
- ///
- /// Выбрасывается, когда равен null.
- ///
- protected DragSourceBehaviorBase(IDragDropService dragDropService)
- {
- DragDropService = dragDropService ?? throw new ArgumentNullException(nameof(dragDropService));
- }
-
- ///
- /// Вызывается при прикреплении поведения к элементу.
- ///
- ///
- /// Реализация по умолчанию подписывается на события элемента через .
- /// Производные классы могут переопределить этот метод для дополнительной инициализации.
- ///
- protected virtual void AttachToElement()
- {
- if (_associatedElement != null)
- {
- SubscribeToEvents(_associatedElement);
- }
- }
-
- ///
- /// Вызывается при откреплении поведения от элемента.
- ///
- ///
- /// Реализация по умолчанию отписывается от событий элемента через .
- /// Производные классы могут переопределить этот метод для дополнительной очистки.
- ///
- protected virtual void DetachFromElement()
- {
- if (_associatedElement != null)
- {
- UnsubscribeFromEvents(_associatedElement);
- }
- }
-
- ///
- /// Подписывается на события элемента, необходимые для отслеживания начала перетаскивания.
- ///
- /// Элемент, к событиям которого нужно подписаться.
- ///
- /// Производные классы должны реализовать этот метод для подписки на события конкретной
- /// UI-платформы (например, MouseDown для WPF, PointerPressed для Avalonia).
- ///
- protected abstract void SubscribeToEvents(TElement element);
-
- ///
- /// Отписывается от событий элемента.
- ///
- /// Элемент, от событий которого нужно отписаться.
- ///
- /// Производные классы должны реализовать этот метод для корректной отписки
- /// от событий, на которые была выполнена подписка в .
- ///
- protected abstract void UnsubscribeFromEvents(TElement element);
-
- ///
- /// Обрабатывает начало взаимодействия с элементом (например, нажатие кнопки мыши).
- ///
- /// Позиция взаимодействия в координатах элемента.
- /// Задача, представляющая асинхронную операцию.
- ///
- ///
- /// Этот метод вызывается из обработчиков событий UI-платформы при начале
- /// взаимодействия, которое может привести к перетаскиванию.
- ///
- ///
- /// Реализация по умолчанию сохраняет начальную позицию для последующей
- /// проверки порога перетаскивания.
- ///
- ///
- protected virtual Task OnInteractionStarted(Point position)
- {
- if (_isDragging)
- return Task.CompletedTask;
-
- _dragStartPosition = position;
- _dragCancellationTokenSource = new CancellationTokenSource();
- return Task.CompletedTask;
- }
-
- ///
- /// Обрабатывает перемещение во время взаимодействия с элементом.
- ///
- /// Текущая позиция взаимодействия в координатах элемента.
- /// Задача, представляющая асинхронную операцию.
- ///
- ///
- /// Этот метод вызывается при перемещении курсора/тач-точки во время удержания
- /// взаимодействия (например, перемещение мыши с нажатой кнопкой).
- ///
- ///
- /// Реализация по умолчанию проверяет, превышено ли расстояние от начальной
- /// точки порога перетаскивания, и если да - начинает операцию перетаскивания.
- ///
- ///
- protected virtual async Task OnInteractionMoved(Point position)
- {
- if (_isDragging || AssociatedElement == null)
- return;
-
- var distance = CalculateDistance(_dragStartPosition, position);
- if (distance > DragDropService.DragStartThreshold)
- {
- await StartDragOperation();
- }
- }
-
- ///
- /// Обрабатывает завершение взаимодействия с элементом.
- ///
- /// Задача, представляющая асинхронную операцию.
- ///
- ///
- /// Этот метод вызывается при завершении взаимодействия (например, отпускании кнопки мыши).
- ///
- ///
- /// Реализация по умолчанию сбрасывает состояние поведения, если перетаскивание не было начато.
- ///
- ///
- protected virtual Task OnInteractionEnded()
- {
- // Сброс состояния, если перетаскивание не началось
- if (!_isDragging)
- {
- Reset();
- }
- return Task.CompletedTask;
- }
-
- ///
- /// Обрабатывает отмену взаимодействия с элементом.
- ///
- /// Задача, представляющая асинхронную операцию.
- ///
- ///
- /// Этот метод вызывается при отмене взаимодействия (например, нажатии клавиши Escape
- /// или выходе за пределы допустимой области).
- ///
- ///
- /// Реализация по умолчанию отменяет текущую операцию перетаскивания, если она активна,
- /// и сбрасывает состояние поведения.
- ///
- ///
- protected virtual async Task OnInteractionCancelled()
- {
- if (_isDragging)
- {
- await DragDropService.CancelDragAsync();
- }
- Reset();
- }
-
- ///
- /// Начинает операцию перетаскивания.
- ///
- /// Задача, представляющая асинхронную операцию.
- ///
- ///
- /// Этот метод преобразует начальную позицию в экранные координаты и вызывает
- /// сервис перетаскивания для начала операции.
- ///
- ///
- /// Операция начинается только если поведение прикреплено к элементу и
- /// не выполняется другая операция перетаскивания.
- ///
- ///
- protected virtual async Task StartDragOperation()
- {
- if (_isDragging || AssociatedElement == null || _dragCancellationTokenSource == null)
- return;
-
- // Получаем начальную позицию в экранных координатах
- var screenPosition = ConvertToScreenCoordinates(_dragStartPosition);
-
- // Начинаем перетаскивание
- try
- {
- _isDragging = await DragDropService.StartDragAsync(this, screenPosition);
- }
- catch (OperationCanceledException)
- {
- // Операция была отменена
- Reset();
- }
- }
-
- ///
- /// Преобразует координаты элемента в экранные координаты.
- ///
- /// Точка в координатах элемента.
- /// Точка в экранных координатах.
- ///
- /// Производные классы должны реализовать этот метод для преобразования
- /// координат в соответствии с конкретной UI-платформой.
- ///
- protected abstract Point ConvertToScreenCoordinates(Point point);
-
- ///
- /// Вычисляет расстояние между двумя точками.
- ///
- /// Первая точка.
- /// Вторая точка.
- /// Расстояние между точками.
- protected virtual double CalculateDistance(Point p1, Point p2)
- {
- var dx = p2.X - p1.X;
- var dy = p2.Y - p1.Y;
- return Math.Sqrt(dx * dx + dy * dy);
- }
-
- ///
- /// Сбрасывает состояние поведения.
- ///
- ///
- /// Этот метод очищает все временные данные и отменяет токены отмены,
- /// связанные с текущей операцией перетаскивания.
- ///
- protected virtual void Reset()
- {
- _isDragging = false;
- _dragStartPosition = default;
-
- _dragCancellationTokenSource?.Dispose();
- _dragCancellationTokenSource = null;
- }
-
- #region IDragSource Implementation
-
- ///
- public abstract Task TryStartDragAsync(Point startPosition, CancellationToken cancellationToken = default);
-
- ///
- public async Task OnDragCompletedAsync(DragInfo dragInfo, Lattice.Core.DragDrop.Enums.DragDropEffects effects, CancellationToken cancellationToken = default)
- {
- _isDragging = false;
- OnDragCompleted(dragInfo, effects);
- }
-
- ///
- public async Task OnDragCancelledAsync(DragInfo dragInfo, CancellationToken cancellationToken = default)
- {
- _isDragging = false;
- OnDragCancelled(dragInfo);
- }
-
- #endregion
-
- #region Virtual Methods for Derived Classes
-
- ///
- /// Вызывается при успешном завершении операции перетаскивания.
- ///
- /// Информация о перетаскивании, использованная в операции.
- /// Эффекты, примененные при завершении операции.
- ///
- /// Производные классы могут переопределить этот метод для выполнения
- /// дополнительных действий после успешного завершения перетаскивания,
- /// например, удаления исходного элемента при перемещении.
- ///
- protected virtual void OnDragCompleted(DragInfo dragInfo, Lattice.Core.DragDrop.Enums.DragDropEffects effects)
- {
- }
-
- ///
- /// Вызывается при отмене операции перетаскивания.
- ///
- /// Информация о перетаскивании, использованная в операции.
- ///
- /// Производные классы могут переопределить этот метод для выполнения
- /// действий по восстановлению состояния после отмены перетаскивания.
- ///
- protected virtual void OnDragCancelled(DragInfo dragInfo)
- {
- }
-
- #endregion
-
- ///
- /// Открепляет поведение от элемента и освобождает ресурсы.
- ///
- ///
- /// После вызова этого метода поведение больше не будет обрабатывать события
- /// элемента и может быть безопасно удалено.
- ///
- public virtual void Detach()
- {
- DetachFromElement();
- _associatedElement = null;
- Reset();
- }
-}
\ No newline at end of file
diff --git a/Lattice.UI.DragDrop/Behaviors/DropTargetBehaviorBase.cs b/Lattice.UI.DragDrop/Behaviors/DropTargetBehaviorBase.cs
deleted file mode 100644
index 66a7399..0000000
--- a/Lattice.UI.DragDrop/Behaviors/DropTargetBehaviorBase.cs
+++ /dev/null
@@ -1,332 +0,0 @@
-using Lattice.Core.DragDrop.Abstractions;
-using Lattice.Core.DragDrop.Models;
-using Lattice.Core.DragDrop.Services;
-using Lattice.Core.Geometry;
-using System;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace Lattice.UI.DragDrop.Behaviors;
-
-///
-/// Базовый класс поведения цели сброса для UI элементов.
-///
-/// Тип UI элемента, к которому прикрепляется поведение.
-///
-///
-/// Этот класс предоставляет базовую реализацию поведения цели сброса для UI элементов.
-/// Он автоматически регистрирует элемент в сервисе перетаскивания, обновляет его границы
-/// при изменении размера или позиции и предоставляет методы для обработки событий сброса.
-///
-///
-/// Производные классы должны реализовать абстрактные методы для конкретной
-/// UI-платформы и предоставить логику проверки и обработки сбрасываемых данных.
-///
-///
-public abstract class DropTargetBehaviorBase : IDropTarget
- where TElement : class
-{
- private IDragDropService? _dragDropService;
- private string? _registrationId;
- private TElement? _associatedElement;
- private Rect _currentBounds;
-
- ///
- /// Получает или задает связанный UI элемент.
- ///
- ///
- /// Элемент UI, к которому прикреплено поведение цели сброса.
- /// При изменении значения автоматически выполняется перерегистрация в сервисе перетаскивания.
- ///
- protected TElement? AssociatedElement
- {
- get => _associatedElement;
- set
- {
- if (_associatedElement != value)
- {
- UnregisterFromService();
- _associatedElement = value;
- RegisterToService();
- }
- }
- }
-
- ///
- /// Получает или задает приоритет цели сброса.
- ///
- ///
- /// Цели с более высоким приоритетом проверяются первыми при нахождении курсора
- /// в области нескольких целей. Значение по умолчанию: 0.
- ///
- public int Priority { get; set; }
-
- ///
- /// Получает или задает группу цели сброса.
- ///
- ///
- /// Имя группы для группового управления целями сброса. Может использоваться
- /// для массовой отмены регистрации целей или применения общих настроек.
- ///
- public string? Group { get; set; }
-
- ///
- /// Получает сервис перетаскивания из контейнера зависимостей.
- ///
- ///
- /// Экземпляр , используемый для регистрации цели сброса.
- ///
- protected IDragDropService DragDropService { get; }
-
- ///
- /// Получает текущие границы элемента в экранных координатах.
- ///
- ///
- /// Прямоугольник, описывающий границы элемента в экранных координатах.
- /// Значение автоматически обновляется при изменении размера или позиции элемента.
- ///
- protected Rect CurrentBounds => _currentBounds;
-
- ///
- /// Получает уникальный идентификатор регистрации цели в сервисе перетаскивания.
- ///
- ///
- /// Идентификатор, возвращенный методом ,
- /// или null, если цель не зарегистрирована.
- ///
- protected string? RegistrationId => _registrationId;
-
- ///
- /// Инициализирует новый экземпляр класса .
- ///
- /// Сервис перетаскивания.
- ///
- /// Выбрасывается, когда равен null.
- ///
- protected DropTargetBehaviorBase(IDragDropService dragDropService)
- {
- DragDropService = dragDropService ?? throw new ArgumentNullException(nameof(dragDropService));
- }
-
- ///
- /// Вызывается при прикреплении поведения к элементу.
- ///
- ///
- /// Реализация по умолчанию подписывается на события элемента, обновляет границы
- /// и регистрирует цель в сервисе перетаскивания.
- ///
- protected virtual void AttachToElement()
- {
- if (_associatedElement != null)
- {
- SubscribeToEvents(_associatedElement);
- UpdateBounds();
- RegisterToService();
- }
- }
-
- ///
- /// Вызывается при откреплении поведения от элемента.
- ///
- ///
- /// Реализация по умолчанию отписывается от событий элемента и отменяет
- /// регистрацию цели в сервисе перетаскивания.
- ///
- protected virtual void DetachFromElement()
- {
- if (_associatedElement != null)
- {
- UnsubscribeFromEvents(_associatedElement);
- UnregisterFromService();
- }
- }
-
- ///
- /// Подписывается на события элемента, необходимые для отслеживания изменений размера и позиции.
- ///
- /// Элемент, к событиям которого нужно подписаться.
- ///
- /// Производные классы должны реализовать этот метод для подписки на события конкретной
- /// UI-платформы (например, SizeChanged, LayoutUpdated для WPF).
- ///
- protected abstract void SubscribeToEvents(TElement element);
-
- ///
- /// Отписывается от событий элемента.
- ///
- /// Элемент, от событий которого нужно отписаться.
- ///
- /// Производные классы должны реализовать этот метод для корректной отписки
- /// от событий, на которые была выполнена подписка в .
- ///
- protected abstract void UnsubscribeFromEvents(TElement element);
-
- ///
- /// Обновляет границы элемента в экранных координатах.
- ///
- ///
- /// Этот метод вызывается при изменении размера или позиции элемента для
- /// обновления области, в которой цель может принимать сбрасываемые данные.
- ///
- protected virtual void UpdateBounds()
- {
- if (_associatedElement != null)
- {
- _currentBounds = GetScreenBounds(_associatedElement);
-
- // Обновляем регистрацию в сервисе
- if (_registrationId != null)
- {
- DragDropService.UpdateDropTargetBounds(_registrationId, _currentBounds);
- }
- }
- }
-
- ///
- /// Получает границы элемента в экранных координатах.
- ///
- /// Элемент, границы которого нужно получить.
- /// Границы элемента в экранных координатах.
- ///
- /// Производные классы должны реализовать этот метод для получения границ
- /// элемента в соответствии с конкретной UI-платформой.
- ///
- protected abstract Rect GetScreenBounds(TElement element);
-
- ///
- /// Регистрирует цель в сервисе перетаскивания.
- ///
- ///
- ///
- /// Этот метод регистрирует текущий объект как цель сброса в сервисе перетаскивания
- /// с указанными приоритетом и группой.
- ///
- ///
- /// Регистрация выполняется только если поведение прикреплено к элементу и
- /// цель еще не зарегистрирована.
- ///
- ///
- protected virtual void RegisterToService()
- {
- if (_associatedElement != null && _registrationId == null)
- {
- UpdateBounds();
- _registrationId = DragDropService.RegisterDropTarget(this, _currentBounds, Priority, Group);
- }
- }
-
- ///
- /// Отменяет регистрацию цели в сервисе перетаскивания.
- ///
- ///
- /// Этот метод отменяет регистрацию цели сброса, освобождая ресурсы
- /// в сервисе перетаскивания и предотвращая дальнейшую обработку событий.
- ///
- protected virtual void UnregisterFromService()
- {
- if (_registrationId != null)
- {
- DragDropService.UnregisterDropTarget(_registrationId);
- _registrationId = null;
- }
- }
-
- ///
- /// Вызывается при изменении размера или позиции элемента.
- ///
- ///
- /// Производные классы должны вызывать этот метод из обработчиков событий
- /// изменения размера или позиции элемента для обновления границ цели.
- ///
- protected virtual void OnElementLayoutChanged()
- {
- UpdateBounds();
- }
-
- #region IDropTarget Implementation
-
- ///
- public abstract Task CanAcceptDropAsync(DropInfo dropInfo, CancellationToken ct = default);
-
- ///
- public virtual async Task OnDragOverAsync(DropInfo dropInfo, CancellationToken ct = default)
- {
- // Базовая реализация устанавливает эффект по умолчанию
- if (await CanAcceptDropAsync(dropInfo))
- {
- // Установить эффект по умолчанию, если он разрешен
- if (dropInfo.CanAcceptEffect(Core.DragDrop.Enums.DragDropEffects.Move))
- {
- dropInfo.SuggestedEffects = Core.DragDrop.Enums.DragDropEffects.Move;
- }
- else if (dropInfo.CanAcceptEffect(Core.DragDrop.Enums.DragDropEffects.Copy))
- {
- dropInfo.SuggestedEffects = Core.DragDrop.Enums.DragDropEffects.Copy;
- }
- else if (dropInfo.CanAcceptEffect(Core.DragDrop.Enums.DragDropEffects.Link))
- {
- dropInfo.SuggestedEffects = Core.DragDrop.Enums.DragDropEffects.Link;
- }
- }
- else
- {
- dropInfo.SuggestedEffects = Core.DragDrop.Enums.DragDropEffects.None;
- }
- }
-
- ///
- public abstract Task OnDropAsync(DropInfo dropInfo, CancellationToken cancellationToken = default);
-
- ///
- public virtual Task OnDragLeaveAsync(CancellationToken cancellationToken = default)
- {
- // Базовая реализация не выполняет действий при выходе курсора из области цели
- return Task.CompletedTask;
- }
-
- #endregion
-
- ///
- /// Открепляет поведение от элемента и освобождает все связанные ресурсы.
- ///
- ///
- ///
- /// Этот метод выполняет следующие действия:
- ///
- ///
- /// - Отписывается от всех событий связанного элемента
- /// - Отменяет регистрацию цели в сервисе перетаскивания
- /// - Освобождает ссылку на связанный элемент
- /// - Очищает все временные данные и состояние
- ///
- ///
- /// После вызова этого метода поведение больше не будет обрабатывать события
- /// элемента и не будет реагировать на операции перетаскивания. Поведение
- /// можно безопасно удалить после вызова этого метода.
- ///
- ///
- /// Важно: Этот метод должен быть вызван перед удалением
- /// элемента из визуального дерева или перед заменой поведения, чтобы
- /// предотвратить утечки памяти и непредсказуемое поведение системы.
- ///
- ///
- ///
- /// // Пример использования
- /// var dropBehavior = new MyDropTargetBehavior(serviceProvider);
- /// dropBehavior.AssociatedElement = myElement;
- ///
- /// // ... использование поведения ...
- ///
- /// // Перед удалением элемента или поведения
- /// dropBehavior.Detach();
- ///
- ///
- ///
- public virtual void Detach()
- {
- DetachFromElement();
- _associatedElement = null;
- _registrationId = null;
- _currentBounds = default;
- }
-}
\ No newline at end of file
diff --git a/Lattice.UI.DragDrop/Lattice.UI.DragDrop.csproj b/Lattice.UI.DragDrop/Lattice.UI.DragDrop.csproj
deleted file mode 100644
index 969f0d7..0000000
--- a/Lattice.UI.DragDrop/Lattice.UI.DragDrop.csproj
+++ /dev/null
@@ -1,16 +0,0 @@
-
-
-
- net8.0;net9.0;net10.0
- enable
- latest
- true
- true
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/Lattice.UI.DragDrop/README.md b/Lattice.UI.DragDrop/README.md
deleted file mode 100644
index eb2039d..0000000
--- a/Lattice.UI.DragDrop/README.md
+++ /dev/null
@@ -1,378 +0,0 @@
-# Lattice.UI.DragDrop
-
-UI-слой библиотеки перетаскивания (drag-and-drop) для платформ .NET.
-
-## 📋 Обзор
-
-Lattice.UI.DragDrop предоставляет абстракции и базовые реализации для интеграции системы перетаскивания Lattice.Core.DragDrop с различными UI-фреймворками (WPF, Avalonia, MAUI и т.д.).
-
-### ✨ Основные возможности
-
-- ✅ **Кросс-платформенные абстракции** - единый API для всех UI-фреймворков
-- ✅ **Готовые базовые классы** для быстрой реализации поведения перетаскивания
-- ✅ **Поддержка визуальной обратной связи** через плагинную архитектуру
-- ✅ **Интеграция с DI-контейнерами** через Microsoft.Extensions.DependencyInjection
-- ✅ **Безопасные реализации по умолчанию** для упрощения разработки
-- ✅ **Расширяемость** через наследование и переопределение
-
-## 🏗️ Архитектура
-
-### Основные компоненты
-
-#### 1. **IDragDropHost**
-Абстракция для управления визуальными элементами перетаскивания на уровне платформы.
-
-#### 2. **IDragVisualProvider**
-Поставщик визуального представления для перетаскиваемых элементов.
-
-#### 3. **IDropVisualAdorner**
-Элемент визуальной обратной связи при наведении на цель сброса.
-
-#### 4. **DragSourceBehaviorBase\**
-Базовый класс для реализации поведения источника перетаскивания.
-
-#### 5. **DropTargetBehaviorBase\**
-Базовый класс для реализации поведения цели сброса.
-
-## 🚀 Быстрый старт
-
-### 1. Установка
-
-```csharp
-// В методе ConfigureServices вашего приложения
-services.AddLatticeDragDrop();
-```
-
-### 2. Регистрация платформенных реализаций
-
-```csharp
-// Для WPF
-services.AddDragVisualProvider();
-services.AddDropVisualAdorner();
-services.AddDragDropHost();
-
-// Для Avalonia
-services.AddDragVisualProvider();
-services.AddDropVisualAdorner();
-services.AddDragDropHost();
-```
-
-### 3. Создание поведения источника перетаскивания
-
-```csharp
-public class MyDragSourceBehavior : DragSourceBehaviorBase
-{
- public MyDragSourceBehavior(IServiceProvider serviceProvider)
- : base(serviceProvider)
- {
- }
-
- protected override void SubscribeToEvents(Control element)
- {
- element.MouseDown += OnMouseDown;
- element.MouseMove += OnMouseMove;
- element.MouseUp += OnMouseUp;
- }
-
- protected override void UnsubscribeFromEvents(Control element)
- {
- element.MouseDown -= OnMouseDown;
- element.MouseMove -= OnMouseMove;
- element.MouseUp -= OnMouseUp;
- }
-
- public override async Task TryStartDragAsync(Point startPosition, CancellationToken ct)
- {
- // Проверяем, можно ли начать перетаскивание
- if (!CanDrag())
- return null;
-
- // Создаем информацию о перетаскивании
- return new DragInfo(
- data: GetDragData(),
- allowedEffects: DragDropEffects.Copy | DragDropEffects.Move,
- startPosition: startPosition,
- source: this
- );
- }
-
- // ... остальная реализация
-}
-```
-
-### 4. Создание поведения цели сброса
-
-```csharp
-public class MyDropTargetBehavior : DropTargetBehaviorBase
-{
- public MyDropTargetBehavior(IServiceProvider serviceProvider)
- : base(serviceProvider)
- {
- }
-
- protected override void SubscribeToEvents(Panel element)
- {
- element.SizeChanged += OnSizeChanged;
- element.LayoutUpdated += OnLayoutUpdated;
- }
-
- protected override void UnsubscribeFromEvents(Panel element)
- {
- element.SizeChanged -= OnSizeChanged;
- element.LayoutUpdated -= OnLayoutUpdated;
- }
-
- public override async Task CanAcceptDropAsync(DropInfo dropInfo, CancellationToken ct)
- {
- // Проверяем тип данных
- if (dropInfo.Data is not MyDataType data)
- return false;
-
- // Проверяем бизнес-правила
- return await ValidateDropAsync(data, ct);
- }
-
- public override async Task OnDropAsync(DropInfo dropInfo, CancellationToken ct)
- {
- var data = (MyDataType)dropInfo.Data;
- await ProcessDropAsync(data, ct);
- dropInfo.MarkAsHandled();
- }
-
- // ... остальная реализация
-}
-```
-
-## 🔌 Интеграция с UI-фреймворками
-
-### WPF
-
-```csharp
-public static class WpfDragDropExtensions
-{
- public static IServiceCollection AddWpfDragDrop(this IServiceCollection services)
- {
- return services
- .AddLatticeDragDrop()
- .AddDragVisualProvider()
- .AddDropVisualAdorner()
- .AddDragDropHost();
- }
-}
-```
-
-### Avalonia
-
-```csharp
-public static class AvaloniaDragDropExtensions
-{
- public static IServiceCollection AddAvaloniaDragDrop(this IServiceCollection services)
- {
- return services
- .AddLatticeDragDrop()
- .AddDragVisualProvider()
- .AddDropVisualAdorner()
- .AddDragDropHost();
- }
-}
-```
-
-## 🎨 Визуальная обратная связь
-
-### Создание кастомного визуального представления
-
-```csharp
-public class CustomDragVisualProvider : IDragVisualProvider
-{
- public object CreateDragVisual(DragInfo dragInfo, Point initialPosition)
- {
- var border = new Border
- {
- Background = new SolidColorBrush(Colors.LightBlue),
- BorderBrush = new SolidColorBrush(Colors.Blue),
- BorderThickness = new Thickness(1),
- CornerRadius = new CornerRadius(4),
- Padding = new Thickness(8),
- Child = new TextBlock
- {
- Text = $"Dragging: {dragInfo.Data}",
- Foreground = new SolidColorBrush(Colors.Black)
- },
- Opacity = 0.8
- };
-
- return border;
- }
-
- public void UpdateDragVisualPosition(object dragVisual, Point position)
- {
- if (dragVisual is FrameworkElement element)
- {
- element.SetValue(Canvas.LeftProperty, position.X);
- element.SetValue(Canvas.TopProperty, position.Y);
- }
- }
-
- public void ReleaseDragVisual(object dragVisual)
- {
- if (dragVisual is IDisposable disposable)
- disposable.Dispose();
- }
-}
-```
-
-### Создание кастомной обратной связи
-
-```csharp
-public class CustomDropVisualAdorner : IDropVisualAdorner
-{
- private Border? _adorner;
-
- public void Show(DropInfo dropInfo, Rect targetBounds)
- {
- _adorner = new Border
- {
- Background = new SolidColorBrush(Colors.Transparent),
- BorderBrush = new SolidColorBrush(Colors.Green),
- BorderThickness = new Thickness(2),
- CornerRadius = new CornerRadius(4)
- };
-
- // Установка позиции и размера
- Canvas.SetLeft(_adorner, targetBounds.X);
- Canvas.SetTop(_adorner, targetBounds.Y);
- _adorner.Width = targetBounds.Width;
- _adorner.Height = targetBounds.Height;
-
- // Добавление в визуальное дерево
- AdornerLayer.GetAdornerLayer(targetElement)?.Add(_adorner);
- }
-
- public void Update(DropInfo dropInfo)
- {
- if (_adorner != null)
- {
- // Обновление стиля в зависимости от эффекта
- var brush = dropInfo.SuggestedEffects.HasFlag(DragDropEffects.Move)
- ? Colors.Green
- : Colors.Blue;
-
- _adorner.BorderBrush = new SolidColorBrush(brush);
- }
- }
-
- public void Hide()
- {
- if (_adorner != null)
- {
- var layer = AdornerLayer.GetAdornerLayer(_adorner);
- layer?.Remove(_adorner);
- _adorner = null;
- }
- }
-}
-```
-
-## ⚙️ Конфигурация
-
-### Настройка сервисов
-
-```csharp
-public void ConfigureServices(IServiceCollection services)
-{
- // Базовая регистрация
- services.AddLatticeDragDrop();
-
- // Платформенные реализации
- services.AddDragVisualProvider();
- services.AddDropVisualAdorner();
- services.AddDragDropHost();
-
- // Регистрация поведений
- services.AddTransient, MyDragSourceBehavior>();
- services.AddTransient, MyDropTargetBehavior>();
-}
-```
-
-### Использование с ViewModel
-
-```csharp
-public class MainViewModel
-{
- private readonly IServiceProvider _serviceProvider;
-
- public MainViewModel(IServiceProvider serviceProvider)
- {
- _serviceProvider = serviceProvider;
- }
-
- public void AttachDragBehavior(UIElement element)
- {
- var behavior = _serviceProvider.GetRequiredService>();
- behavior.AssociatedElement = element;
- }
-
- public void AttachDropBehavior(Panel panel)
- {
- var behavior = _serviceProvider.GetRequiredService>();
- behavior.AssociatedElement = panel;
- }
-}
-```
-
-## 📝 Best Practices
-
-### 1. **Эффективность визуального представления**
-- Создавайте легковесные визуальные элементы для плавной анимации
-- Используйте кэширование для часто используемых представлений
-- Избегайте сложных преобразований и эффектов
-
-### 2. **Обработка событий**
-- Всегда отписывайтесь от событий при откреплении поведения
-- Используйте слабые ссылки для предотвращения утечек памяти
-- Обрабатывайте исключения в обработчиках событий
-
-### 3. **Ресурсы**
-- Освобождайте графические ресурсы в методах Release/Dispose
-- Используйте пулы объектов для часто создаваемых элементов
-- Мониторьте использование памяти при активном перетаскивании
-
-### 4. **Пользовательский опыт**
-- Предоставляйте понятную визуальную обратную связь
-- Поддерживайте настраиваемые стили и темы
-- Обеспечивайте плавность анимации даже на слабых устройствах
-
-## 🔄 Миграция
-
-### С версии 1.x на 2.0
-
-1. **Обновление интерфейсов**:
- - Методы переименованы в соответствии с Core
- - Добавлена поддержка CancellationToken
-
-2. **Изменения в базовых классах**:
- - `DragSourceBehaviorBase` теперь реализует новый интерфейс `IDragSource`
- - `DropTargetBehaviorBase` теперь реализует новый интерфейс `IDropTarget`
-
-3. **Регистрация сервисов**:
- - Метод `AddLatticeDragDrop` теперь регистрирует только базовые сервисы
- - Для платформенных реализаций используйте методы `AddDragVisualProvider`, `AddDropVisualAdorner`, `AddDragDropHost`
-
-## 📄 Лицензия
-
-Библиотека распространяется под лицензией MIT. Подробности см. в файле LICENSE.
-
-## 🤝 Вклад в разработку
-
-Для интеграции с новой UI-платформой необходимо:
-1. Реализовать интерфейсы `IDragDropHost`, `IDragVisualProvider`, `IDropVisualAdorner`
-2. Создать производные классы от `DragSourceBehaviorBase` и `DropTargetBehaviorBase`
-3. Предоставить метод расширения для регистрации всех компонентов
-
-## 🐛 Отчеты об ошибках
-
-Для сообщения об ошибках используйте Issues на GitHub. Пожалуйста, указывайте:
-- Платформу и версию UI-фреймворка
-- Шаги для воспроизведения
-- Ожидаемое и фактическое поведение
-- Пример кода для демонстрации проблемы
\ No newline at end of file
diff --git a/Lattice.slnx b/Lattice.slnx
index e20d9ef..73e279c 100644
--- a/Lattice.slnx
+++ b/Lattice.slnx
@@ -1,10 +1,5 @@
-
-
-
-
-
-
+
@@ -16,19 +11,12 @@
-
-
-
-
-
-
-
-
+