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
}