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 @@ +