using Lattice.Core.DragDrop.Services; using Lattice.Core.Geometry; using Lattice.UI.DragDrop.WinUI.Controls; using Microsoft.UI.Xaml; using System; using System.Collections.Generic; namespace Lattice.UI.DragDrop.WinUI.Services; /// /// Центральный менеджер для управления операциями drag-and-drop в WinUI приложении. /// /// /// /// Этот класс реализует шаблон Singleton и предоставляет единую точку для /// настройки и управления всеми операциями перетаскивания в приложении. /// /// /// Менеджер отвечает за: /// - Инициализацию системы перетаскивания /// - Регистрацию и отслеживание источников и целей перетаскивания /// - Создание и управление визуальной обратной связью /// - Координацию между поведением элементов и базовым сервисом перетаскивания /// /// /// Для использования необходимо вызвать при запуске приложения /// и использовать attached properties или методы расширения для настройки элементов. /// /// public sealed class WinUIDragDropManager : IDisposable { #region Singleton private static WinUIDragDropManager? _instance; private static readonly object _lock = new(); /// /// Получает единственный экземпляр менеджера. /// public static WinUIDragDropManager Instance { get { if (_instance == null) { lock (_lock) { _instance ??= new WinUIDragDropManager(); } } return _instance; } } #endregion #region Поля private readonly DragDropService _dragDropService; private readonly WinUIDragDropHost _host; private readonly Dictionary _dragSources = new(); private readonly Dictionary _dropTargets = new(); private DragAdorner? _currentDragVisual; private bool _disposed; #endregion #region Свойства /// /// Получает основной сервис перетаскивания. /// public IDragDropService DragDropService => _dragDropService; /// /// Получает или задает смещение визуального элемента перетаскивания относительно курсора. /// /// /// Точка, определяющая смещение по осям X и Y. Значение по умолчанию: (-20, -20). /// Отрицательные значения поднимают визуальный элемент вверх и влево относительно курсора. /// public Point DragVisualOffset { get; set; } = new Point(-20, -20); #endregion #region Конструктор private WinUIDragDropManager() { _dragDropService = new DragDropService(); _host = new WinUIDragDropHost(); } #endregion #region Публичные методы /// /// Инициализирует систему перетаскивания для указанного окна. /// /// Основное окно приложения, в котором будет работать перетаскивание. /// /// Выбрасывается, если менеджер был удален. /// /// /// Этот метод должен быть вызван один раз при запуске приложения, обычно в методе /// . /// public void Initialize(Window window) { if (_disposed) throw new ObjectDisposedException(nameof(WinUIDragDropManager)); _host.Initialize(window); // Подписываемся на события _dragDropService.DragStarted += OnDragStarted; _dragDropService.DragUpdated += OnDragUpdated; _dragDropService.DragCompleted += OnDragCompleted; _dragDropService.DragCancelled += OnDragCancelled; } /// /// Делает указанный элемент источником перетаскивания. /// /// Элемент, который станет источником перетаскивания. /// Данные, которые будут перетаскиваться. Если не указано, используются /// DataContext или Tag элемента. /// /// Если элемент уже зарегистрирован как источник перетаскивания, метод не выполняет действий. /// public void MakeDragSource(FrameworkElement element, object? dragData = null) { if (_disposed || _dragSources.ContainsKey(element)) return; var behavior = new Behaviors.WinUIDragSourceBehavior(_dragDropService, _host); behavior.Attach(element, dragData); _dragSources[element] = behavior; } /// /// Делает указанный элемент целью сброса. /// /// Элемент, который станет целью сброса. /// /// Если элемент уже зарегистрирован как цель сброса, метод не выполняет действий. /// public void MakeDropTarget(FrameworkElement element) { if (_disposed || _dropTargets.ContainsKey(element)) return; var behavior = new Behaviors.WinUIDropTargetBehavior(_dragDropService, _host); behavior.Attach(element); _dropTargets[element] = behavior; } /// /// Удаляет возможность перетаскивания. /// public void RemoveDragSource(FrameworkElement element) { if (_dragSources.Remove(element, out var behavior)) { behavior.Detach(); } } /// /// Удаляет возможность сброса. /// public void RemoveDropTarget(FrameworkElement element) { if (_dropTargets.Remove(element, out var behavior)) { behavior.Detach(); } } /// /// Очищает все регистрации. /// public void Clear() { foreach (var behavior in _dragSources.Values) { behavior.Detach(); } _dragSources.Clear(); foreach (var behavior in _dropTargets.Values) { behavior.Detach(); } _dropTargets.Clear(); } #endregion #region Обработчики событий private void OnDragStarted(object? sender, DragStartedEventArgs e) { // Создаем визуальное представление _currentDragVisual = new DragAdorner { DragData = e.DragInfo.Data, Opacity = 0.8 }; var position = new Point( e.Position.X + DragVisualOffset.X, e.Position.Y + DragVisualOffset.Y ); _currentDragVisual.UpdatePosition(position); _host.ShowDragVisual(_currentDragVisual, position); } private void OnDragUpdated(object? sender, DragUpdatedEventArgs e) { if (_currentDragVisual != null) { var position = new Point( e.Position.X + DragVisualOffset.X, e.Position.Y + DragVisualOffset.Y ); _currentDragVisual.UpdatePosition(position); } } private void OnDragCompleted(object? sender, DragCompletedEventArgs e) { CleanupDragVisual(); } private void OnDragCancelled(object? sender, DragCancelledEventArgs e) { CleanupDragVisual(); } private void CleanupDragVisual() { if (_currentDragVisual != null) { _currentDragVisual.Hide(); _currentDragVisual = null; } } #endregion #region IDisposable public void Dispose() { if (_disposed) return; Clear(); // Отписываемся от событий _dragDropService.DragStarted -= OnDragStarted; _dragDropService.DragUpdated -= OnDragUpdated; _dragDropService.DragCompleted -= OnDragCompleted; _dragDropService.DragCancelled -= OnDragCancelled; _dragDropService.Dispose(); _host.Dispose(); _disposed = true; GC.SuppressFinalize(this); } #endregion }