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 }