diff --git a/Lattice.Core.DragDrop/Factories/DragDropFactory.cs b/Lattice.Core.DragDrop/Factories/DragDropFactory.cs
new file mode 100644
index 0000000..df48263
--- /dev/null
+++ b/Lattice.Core.DragDrop/Factories/DragDropFactory.cs
@@ -0,0 +1,581 @@
+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.UI.DragDrop.WinUI/Factories/WinUIDragDropFactory.cs b/Lattice.UI.DragDrop.WinUI/Factories/WinUIDragDropFactory.cs
new file mode 100644
index 0000000..91cde9a
--- /dev/null
+++ b/Lattice.UI.DragDrop.WinUI/Factories/WinUIDragDropFactory.cs
@@ -0,0 +1,474 @@
+using Lattice.Core.DragDrop.Factories;
+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 System;
+
+namespace Lattice.UI.DragDrop.WinUI.Factories;
+
+///
+/// Фабрика для создания компонентов системы перетаскивания WinUI.
+/// Предоставляет методы для быстрой настройки drag-and-drop в приложениях WinUI.
+///
+///
+/// Эта фабрика упрощает интеграцию системы перетаскивания в WinUI приложения,
+/// предоставляя готовые методы для наиболее распространенных сценариев использования.
+///
+public static class WinUIDragDropFactory
+{
+ #region Основные компоненты
+
+ ///
+ /// Создает и инициализирует менеджер перетаскивания для WinUI окна.
+ ///
+ ///
+ /// Окно WinUI, для которого создается менеджер перетаскивания.
+ ///
+ ///
+ /// Инициализированный экземпляр .
+ ///
+ ///
+ /// Выбрасывается, когда равен null.
+ ///
+ ///
+ /// Этот метод создает менеджер перетаскивания и настраивает его для работы с указанным окном.
+ /// Менеджер автоматически создает оверлей для визуальных элементов и подписывается на события.
+ ///
+ ///
+ ///
+ /// public partial class MainWindow : Window
+ /// {
+ /// private WinUIDragDropManager _dragDropManager;
+ ///
+ /// public MainWindow()
+ /// {
+ /// InitializeComponent();
+ /// _dragDropManager = WinUIDragDropFactory.CreateManager(this);
+ /// }
+ /// }
+ ///
+ ///
+ public static WinUIDragDropManager CreateManager(Window window)
+ {
+ if (window == null)
+ throw new ArgumentNullException(nameof(window));
+
+ var manager = WinUIDragDropManager.Instance;
+ manager.Initialize(window);
+ return manager;
+ }
+
+ ///
+ /// Создает и инициализирует менеджер перетаскивания с пользовательскими настройками.
+ ///
+ ///
+ /// Окно WinUI, для которого создается менеджер перетаскивания.
+ ///
+ ///
+ /// Делегат для настройки менеджера перед инициализацией.
+ ///
+ ///
+ /// Инициализированный экземпляр .
+ ///
+ ///
+ /// Этот метод позволяет настроить параметры менеджера (например, смещение визуального элемента)
+ /// перед его инициализацией.
+ ///
+ public static WinUIDragDropManager CreateManager(Window window, Action configure)
+ {
+ if (window == null)
+ throw new ArgumentNullException(nameof(window));
+
+ var manager = WinUIDragDropManager.Instance;
+ configure?.Invoke(manager);
+ manager.Initialize(window);
+ return manager;
+ }
+
+ ///
+ /// Создает хост для визуальных элементов перетаскивания.
+ ///
+ ///
+ /// Окно, к которому будет привязан хост.
+ ///
+ ///
+ /// Экземпляр , готовый к использованию.
+ ///
+ ///
+ /// Хост управляет отображением визуальных элементов (drag-визуализация, drop-превью)
+ /// на оверлейном слое поверх основного содержимого окна.
+ ///
+ 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.
+ ///
+ ///
+ /// Созданное поведение необходимо прикрепить к элементу с помощью метода .
+ ///
+ 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.
+ ///
+ ///
+ /// Созданное поведение необходимо прикрепить к элементу с помощью метода .
+ ///
+ 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);
+ }
+
+ #endregion
+
+ #region Визуальные элементы
+
+ ///
+ /// Создает визуальный элемент для отображения во время перетаскивания.
+ ///
+ ///
+ /// Данные, которые будут отображены в визуальном элементе.
+ ///
+ ///
+ /// Прозрачность элемента (от 0.0 до 1.0).
+ ///
+ ///
+ /// Экземпляр , настроенный для отображения указанных данных.
+ ///
+ ///
+ /// Созданный элемент можно использовать с методом
+ /// для отображения во время операции перетаскивания.
+ ///
+ public static DragAdorner CreateDragAdorner(object dragData, double opacity = 0.8)
+ {
+ return new DragAdorner
+ {
+ DragData = dragData,
+ Opacity = opacity
+ };
+ }
+
+ ///
+ /// Создает элемент предварительного просмотра для области сброса.
+ ///
+ ///
+ /// Цвет подсветки области. Если не указан, используется цвет из ресурсов темы.
+ ///
+ ///
+ /// Толщина границы подсветки.
+ ///
+ ///
+ /// Экземпляр , готовый к отображению.
+ ///
+ ///
+ /// Этот элемент используется для визуального указания области, на которую можно сбросить данные.
+ ///
+ public static DropPreviewAdorner CreateDropPreviewAdorner(
+ Windows.UI.Color? color = null,
+ double thickness = 2.0)
+ {
+ var adorner = new DropPreviewAdorner
+ {
+ PreviewThickness = thickness
+ };
+
+ if (color.HasValue)
+ {
+ adorner.PreviewColor = color.Value;
+ }
+
+ return adorner;
+ }
+
+ #endregion
+
+ #region Готовые конфигурации для типовых сценариев
+
+ ///
+ /// Создает полную систему перетаскивания для WinUI приложения.
+ ///
+ ///
+ /// Главное окно приложения.
+ ///
+ ///
+ /// Кортеж, содержащий менеджер перетаскивания и сервис перетаскивания.
+ ///
+ ///
+ /// Этот метод создает все необходимые компоненты для работы перетаскивания в приложении.
+ /// Возвращаемый сервис можно использовать для создания дополнительных источников и целей.
+ ///
+ public static (WinUIDragDropManager Manager, IDragDropService Service) CreateCompleteSystem(Window window)
+ {
+ var service = DragDropFactory.CreateDragDropService();
+ var manager = CreateManager(window);
+ return (manager, service);
+ }
+
+ ///
+ /// Создает систему перетаскивания, оптимизированную для списков и коллекций.
+ ///
+ ///
+ /// Главное окно приложения.
+ ///
+ ///
+ /// Менеджер перетаскивания, настроенный для переупорядочивания элементов в списках.
+ ///
+ ///
+ /// Эта конфигурация устанавливает оптимальные параметры для перетаскивания элементов
+ /// внутри списков (например, для изменения порядка элементов в ListView или ItemsControl).
+ ///
+ public static WinUIDragDropManager CreateListReorderSystem(Window window)
+ {
+ var manager = CreateManager(window, m =>
+ {
+ m.DragVisualOffset = new Core.Geometry.Point(-15, -15);
+ });
+
+ return manager;
+ }
+
+ ///
+ /// Создает систему перетаскивания для файлов и документов.
+ ///
+ ///
+ /// Главное окно приложения.
+ ///
+ ///
+ /// Менеджер перетаскивания, настроенный для работы с файлами.
+ ///
+ ///
+ /// Эта конфигурация устанавливает увеличенный порог перетаскивания и специальные
+ /// визуальные эффекты, характерные для операций с файлами.
+ ///
+ public static WinUIDragDropManager CreateFileDragDropSystem(Window window)
+ {
+ var manager = CreateManager(window, m =>
+ {
+ m.DragVisualOffset = new Core.Geometry.Point(-25, -25);
+ });
+
+ return manager;
+ }
+
+ ///
+ /// Создает систему перетаскивания для графических редакторов и инструментов дизайна.
+ ///
+ ///
+ /// Главное окно приложения.
+ ///
+ ///
+ /// Менеджер перетаскивания, настроенный для точного позиционирования.
+ ///
+ ///
+ /// Эта конфигурация уменьшает порог начала перетаскивания для более точного контроля
+ /// и устанавливает минималистичную визуализацию.
+ ///
+ 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 элемента.
+ ///
+ ///
+ /// Этот метод является оберткой вокруг ,
+ /// предоставляя более удобный API.
+ ///
+ public static void SetupAsDragSource(WinUIDragDropManager manager, Microsoft.UI.Xaml.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, который должен стать целью сброса.
+ ///
+ ///
+ /// Этот метод является оберткой вокруг ,
+ /// предоставляя более удобный API.
+ ///
+ public static void SetupAsDropTarget(WinUIDragDropManager manager, Microsoft.UI.Xaml.FrameworkElement element)
+ {
+ if (manager == null)
+ throw new ArgumentNullException(nameof(manager));
+ if (element == null)
+ throw new ArgumentNullException(nameof(element));
+
+ manager.MakeDropTarget(element);
+ }
+
+ ///
+ /// Настраивает контейнер для поддержки переупорядочивания дочерних элементов.
+ ///
+ ///
+ /// Менеджер перетаскивания.
+ ///
+ ///
+ /// Контейнер (например, StackPanel или Grid), дочерние элементы которого можно переупорядочивать.
+ ///
+ ///
+ /// Функция для получения данных перетаскивания из дочернего элемента.
+ /// Если не указана, используются DataContext дочерних элементов.
+ ///
+ ///
+ /// Этот метод автоматически настраивает все дочерние элементы контейнера как источники перетаскивания,
+ /// а сам контейнер — как цель сброса, создавая функциональность переупорядочивания.
+ ///
+ public static void SetupReorderContainer(
+ WinUIDragDropManager manager,
+ Microsoft.UI.Xaml.Controls.Panel container,
+ Func childSelector = null)
+ {
+ if (manager == null)
+ throw new ArgumentNullException(nameof(manager));
+ if (container == null)
+ throw new ArgumentNullException(nameof(container));
+
+ // Настраиваем контейнер как цель сброса
+ manager.MakeDropTarget(container);
+
+ // Настраиваем все дочерние элементы как источники перетаскивания
+ foreach (var child in container.Children)
+ {
+ if (child is Microsoft.UI.Xaml.FrameworkElement element)
+ {
+ var data = childSelector?.Invoke(element) ?? element.DataContext ?? element.Tag;
+ manager.MakeDragSource(element, data);
+ }
+ }
+ }
+
+ #endregion
+
+ #region Методы для работы с XAML
+
+ ///
+ /// Настраивает прикрепленные свойства для элемента источника перетаскивания.
+ ///
+ ///
+ /// Элемент, который должен стать источником перетаскивания.
+ ///
+ ///
+ /// Данные для перетаскивания.
+ ///
+ ///
+ /// Этот метод устанавливает attached properties, которые активируют поведение перетаскивания
+ /// при использовании в XAML. Эквивалентно установке свойств IsDragSource и DragData в XAML.
+ ///
+ public static void SetupDragSourceInXaml(Microsoft.UI.Xaml.FrameworkElement element, object dragData)
+ {
+ if (element == null)
+ throw new ArgumentNullException(nameof(element));
+
+ // Устанавливаем attached properties
+ element.SetValue(Behaviors.WinUIDragSourceBehavior.IsEnabledProperty, true);
+ if (dragData != null)
+ {
+ element.SetValue(Behaviors.WinUIDragSourceBehavior.DragDataProperty, dragData);
+ }
+ }
+
+ ///
+ /// Настраивает прикрепленные свойства для элемента цели сброса.
+ ///
+ ///
+ /// Элемент, который должен стать целью сброса.
+ ///
+ ///
+ /// Этот метод устанавливает attached properties, которые активируют поведение цели сброса
+ /// при использовании в XAML. Эквивалентно установке свойства IsDropTarget в XAML.
+ ///
+ public static void SetupDropTargetInXaml(Microsoft.UI.Xaml.FrameworkElement element)
+ {
+ if (element == null)
+ throw new ArgumentNullException(nameof(element));
+
+ element.SetValue(Behaviors.WinUIDropTargetBehavior.IsEnabledProperty, true);
+ }
+
+ #endregion
+}
\ No newline at end of file
diff --git a/Lattice.UI.DragDrop/Behaviors/DragSourceBehaviorBase.cs b/Lattice.UI.DragDrop/Behaviors/DragSourceBehaviorBase.cs
index 4a2c91f..60ec843 100644
--- a/Lattice.UI.DragDrop/Behaviors/DragSourceBehaviorBase.cs
+++ b/Lattice.UI.DragDrop/Behaviors/DragSourceBehaviorBase.cs
@@ -2,7 +2,6 @@
using Lattice.Core.DragDrop.Models;
using Lattice.Core.DragDrop.Services;
using Lattice.Core.Geometry;
-using Microsoft.Extensions.DependencyInjection;
using System;
using System.Threading;
using System.Threading.Tasks;
@@ -59,24 +58,8 @@ public abstract class DragSourceBehaviorBase : IDragSource
///
///
/// Экземпляр , используемый для управления операциями перетаскивания.
- /// При первом обращении выполняется получение сервиса из .
///
- protected IDragDropService DragDropService
- {
- get
- {
- if (_dragDropService == null)
- {
- _dragDropService = ServiceProvider.GetRequiredService();
- }
- return _dragDropService;
- }
- }
-
- ///
- /// Получает провайдер сервисов для разрешения зависимостей.
- ///
- protected IServiceProvider ServiceProvider { get; }
+ protected IDragDropService DragDropService { get; }
///
/// Получает значение, указывающее, выполняется ли в данный момент операция перетаскивания.
@@ -86,13 +69,13 @@ public abstract class DragSourceBehaviorBase : IDragSource
///
/// Инициализирует новый экземпляр класса .
///
- /// Провайдер сервисов для разрешения зависимостей.
+ /// Сервис перетаскивания.
///
- /// Выбрасывается, когда равен null.
+ /// Выбрасывается, когда равен null.
///
- protected DragSourceBehaviorBase(IServiceProvider serviceProvider)
+ protected DragSourceBehaviorBase(IDragDropService dragDropService)
{
- ServiceProvider = serviceProvider ?? throw new ArgumentNullException(nameof(serviceProvider));
+ DragDropService = dragDropService ?? throw new ArgumentNullException(nameof(dragDropService));
}
///
diff --git a/Lattice.UI.DragDrop/Behaviors/DropTargetBehaviorBase.cs b/Lattice.UI.DragDrop/Behaviors/DropTargetBehaviorBase.cs
index 135f7b1..66a7399 100644
--- a/Lattice.UI.DragDrop/Behaviors/DropTargetBehaviorBase.cs
+++ b/Lattice.UI.DragDrop/Behaviors/DropTargetBehaviorBase.cs
@@ -2,7 +2,6 @@
using Lattice.Core.DragDrop.Models;
using Lattice.Core.DragDrop.Services;
using Lattice.Core.Geometry;
-using Microsoft.Extensions.DependencyInjection;
using System;
using System.Threading;
using System.Threading.Tasks;
@@ -76,24 +75,8 @@ public abstract class DropTargetBehaviorBase : IDropTarget
///
///
/// Экземпляр , используемый для регистрации цели сброса.
- /// При первом обращении выполняется получение сервиса из .
///
- protected IDragDropService DragDropService
- {
- get
- {
- if (_dragDropService == null)
- {
- _dragDropService = ServiceProvider.GetRequiredService();
- }
- return _dragDropService;
- }
- }
-
- ///
- /// Получает провайдер сервисов для разрешения зависимостей.
- ///
- protected IServiceProvider ServiceProvider { get; }
+ protected IDragDropService DragDropService { get; }
///
/// Получает текущие границы элемента в экранных координатах.
@@ -116,13 +99,13 @@ public abstract class DropTargetBehaviorBase : IDropTarget
///
/// Инициализирует новый экземпляр класса .
///
- /// Провайдер сервисов для разрешения зависимостей.
+ /// Сервис перетаскивания.
///
- /// Выбрасывается, когда равен null.
+ /// Выбрасывается, когда равен null.
///
- protected DropTargetBehaviorBase(IServiceProvider serviceProvider)
+ protected DropTargetBehaviorBase(IDragDropService dragDropService)
{
- ServiceProvider = serviceProvider ?? throw new ArgumentNullException(nameof(serviceProvider));
+ DragDropService = dragDropService ?? throw new ArgumentNullException(nameof(dragDropService));
}
///
diff --git a/Lattice.UI.DragDrop/Extensions/ServiceCollectionExtensions.cs b/Lattice.UI.DragDrop/Extensions/ServiceCollectionExtensions.cs
deleted file mode 100644
index b6223bb..0000000
--- a/Lattice.UI.DragDrop/Extensions/ServiceCollectionExtensions.cs
+++ /dev/null
@@ -1,194 +0,0 @@
-using Lattice.Core.DragDrop.Services;
-using Lattice.UI.DragDrop.Abstractions;
-using Microsoft.Extensions.DependencyInjection;
-using Microsoft.Extensions.DependencyInjection.Extensions;
-
-namespace Lattice.UI.DragDrop.Extensions;
-
-///
-/// Методы расширения для регистрации сервисов перетаскивания в контейнере зависимостей.
-///
-public static class ServiceCollectionExtensions
-{
- ///
- /// Добавляет сервисы перетаскивания в контейнер зависимостей.
- ///
- /// Коллекция сервисов для регистрации.
- /// Коллекция сервисов с зарегистрированными сервисами перетаскивания.
- ///
- ///
- /// Этот метод регистрирует основные сервисы перетаскивания, необходимые для работы системы:
- ///
- ///
- /// - Основной сервис управления перетаскиванием (
)
- /// - Абстракции для визуального представления и обратной связи
- /// - Провайдеры по умолчанию с безопасной реализацией
- ///
- ///
- /// Для полноценной работы в конкретной UI-платформе (WPF, Avalonia, MAUI и т.д.)
- /// необходимо переопределить регистрации платформенными реализациями.
- ///
- ///
- public static IServiceCollection AddLatticeDragDrop(this IServiceCollection services)
- {
- // Регистрация основного сервиса перетаскивания
- services.TryAddSingleton();
-
- // Регистрация UI-специфичных абстракций с реализациями по умолчанию
- // Эти реализации безопасны (ничего не делают), но могут быть переопределены платформенными реализациями
- services.TryAddSingleton();
- services.TryAddSingleton();
- services.TryAddSingleton();
-
- return services;
- }
-
- ///
- /// Добавляет или заменяет реализацию поставщика визуального представления перетаскивания.
- ///
- /// Тип поставщика визуального представления.
- /// Коллекция сервисов.
- /// Коллекция сервисов.
- ///
- /// Используйте этот метод для регистрации платформенной реализации поставщика визуального представления.
- ///
- public static IServiceCollection AddDragVisualProvider(this IServiceCollection services)
- where TProvider : class, IDragVisualProvider
- {
- services.Replace(ServiceDescriptor.Singleton());
- return services;
- }
-
- ///
- /// Добавляет или заменяет реализацию визуальной обратной связи для цели сброса.
- ///
- /// Тип элемента визуальной обратной связи.
- /// Коллекция сервисов.
- /// Коллекция сервисов.
- ///
- /// Используйте этот метод для регистрации платформенной реализации визуальной обратной связи.
- ///
- public static IServiceCollection AddDropVisualAdorner(this IServiceCollection services)
- where TAdorner : class, IDropVisualAdorner
- {
- services.Replace(ServiceDescriptor.Singleton());
- return services;
- }
-
- ///
- /// Добавляет или заменяет реализацию хоста для отображения визуальных элементов.
- ///
- /// Тип хоста визуальных элементов.
- /// Коллекция сервисов.
- /// Коллекция сервисов.
- ///
- /// Используйте этот метод для регистрации платформенной реализации хоста визуальных элементов.
- ///
- public static IServiceCollection AddDragDropHost(this IServiceCollection services)
- where THost : class, IDragDropHost
- {
- services.Replace(ServiceDescriptor.Singleton());
- return services;
- }
-
- #region Default Implementations
-
- ///
- /// Безопасная реализация поставщика визуального представления по умолчанию.
- ///
- ///
- /// Эта реализация ничего не делает и используется как заглушка до тех пор,
- /// пока не будет предоставлена платформенная реализация.
- ///
- private class DefaultDragVisualProvider : IDragVisualProvider
- {
- ///
- public object? CreateDragVisual(Core.DragDrop.Models.DragInfo dragInfo, Core.Geometry.Point initialPosition)
- {
- // Безопасная реализация: не создает визуальное представление
- return null;
- }
-
- ///
- public void UpdateDragVisualPosition(object dragVisual, Core.Geometry.Point position)
- {
- // Ничего не делаем, так как визуальное представление не было создано
- }
-
- ///
- public void ReleaseDragVisual(object dragVisual)
- {
- // Ничего не делаем, так как визуальное представление не было создано
- }
- }
-
- ///
- /// Безопасная реализация визуальной обратной связи по умолчанию.
- ///
- ///
- /// Эта реализация ничего не делает и используется как заглушка до тех пор,
- /// пока не будет предоставлена платформенная реализация.
- ///
- private class DefaultDropVisualAdorner : IDropVisualAdorner
- {
- ///
- public void Show(Core.DragDrop.Models.DropInfo dropInfo, Core.Geometry.Rect targetBounds)
- {
- // Безопасная реализация: не отображает визуальную обратную связь
- }
-
- ///
- public void Update(Core.DragDrop.Models.DropInfo dropInfo)
- {
- // Ничего не делаем, так как обратная связь не отображается
- }
-
- ///
- public void Hide()
- {
- // Ничего не делаем, так как обратная связь не отображается
- }
- }
-
- ///
- /// Безопасная реализация хоста визуальных элементов по умолчанию.
- ///
- ///
- /// Эта реализация ничего не делает и используется как заглушка до тех пор,
- /// пока не будет предоставлена платформенная реализация.
- ///
- private class DefaultDragDropHost : IDragDropHost
- {
- ///
- public void ShowDragVisual(object dragVisual, Core.Geometry.Point position)
- {
- // Безопасная реализация: не отображает визуальный элемент
- }
-
- ///
- public void UpdateDragVisualPosition(object dragVisual, Core.Geometry.Point position)
- {
- // Ничего не делаем, так как визуальный элемент не отображается
- }
-
- ///
- public void HideDragVisual(object dragVisual)
- {
- // Ничего не делаем, так как визуальный элемент не отображается
- }
-
- ///
- public void ShowDropAdorner(IDropVisualAdorner adorner)
- {
- // Безопасная реализация: не отображает обратную связь
- }
-
- ///
- public void HideDropAdorner(IDropVisualAdorner adorner)
- {
- // Ничего не делаем, так как обратная связь не отображается
- }
- }
-
- #endregion
-}
\ No newline at end of file
diff --git a/Lattice.UI.DragDrop/Lattice.UI.DragDrop.csproj b/Lattice.UI.DragDrop/Lattice.UI.DragDrop.csproj
index f9e1d46..969f0d7 100644
--- a/Lattice.UI.DragDrop/Lattice.UI.DragDrop.csproj
+++ b/Lattice.UI.DragDrop/Lattice.UI.DragDrop.csproj
@@ -13,8 +13,4 @@
-
-
-
-
\ No newline at end of file
diff --git a/Lattice.slnx b/Lattice.slnx
index 7a8e7a8..1031f9c 100644
--- a/Lattice.slnx
+++ b/Lattice.slnx
@@ -1,4 +1,5 @@
+