274 lines
9.3 KiB
C#
274 lines
9.3 KiB
C#
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;
|
||
|
||
/// <summary>
|
||
/// Центральный менеджер для управления операциями drag-and-drop в WinUI приложении.
|
||
/// </summary>
|
||
/// <remarks>
|
||
/// <para>
|
||
/// Этот класс реализует шаблон Singleton и предоставляет единую точку для
|
||
/// настройки и управления всеми операциями перетаскивания в приложении.
|
||
/// </para>
|
||
/// <para>
|
||
/// Менеджер отвечает за:
|
||
/// - Инициализацию системы перетаскивания
|
||
/// - Регистрацию и отслеживание источников и целей перетаскивания
|
||
/// - Создание и управление визуальной обратной связью
|
||
/// - Координацию между поведением элементов и базовым сервисом перетаскивания
|
||
/// </para>
|
||
/// <para>
|
||
/// Для использования необходимо вызвать <see cref="Initialize"/> при запуске приложения
|
||
/// и использовать attached properties или методы расширения для настройки элементов.
|
||
/// </para>
|
||
/// </remarks>
|
||
public sealed class WinUIDragDropManager : IDisposable
|
||
{
|
||
#region Singleton
|
||
|
||
private static WinUIDragDropManager? _instance;
|
||
private static readonly object _lock = new();
|
||
|
||
/// <summary>
|
||
/// Получает единственный экземпляр менеджера.
|
||
/// </summary>
|
||
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<FrameworkElement, Behaviors.WinUIDragSourceBehavior> _dragSources = new();
|
||
private readonly Dictionary<FrameworkElement, Behaviors.WinUIDropTargetBehavior> _dropTargets = new();
|
||
private DragAdorner? _currentDragVisual;
|
||
private bool _disposed;
|
||
|
||
#endregion
|
||
|
||
#region Свойства
|
||
|
||
/// <summary>
|
||
/// Получает основной сервис перетаскивания.
|
||
/// </summary>
|
||
public IDragDropService DragDropService => _dragDropService;
|
||
|
||
/// <summary>
|
||
/// Получает или задает смещение визуального элемента перетаскивания относительно курсора.
|
||
/// </summary>
|
||
/// <value>
|
||
/// Точка, определяющая смещение по осям X и Y. Значение по умолчанию: (-20, -20).
|
||
/// Отрицательные значения поднимают визуальный элемент вверх и влево относительно курсора.
|
||
/// </value>
|
||
public Point DragVisualOffset { get; set; } = new Point(-20, -20);
|
||
|
||
#endregion
|
||
|
||
#region Конструктор
|
||
|
||
private WinUIDragDropManager()
|
||
{
|
||
_dragDropService = new DragDropService();
|
||
_host = new WinUIDragDropHost();
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region Публичные методы
|
||
|
||
/// <summary>
|
||
/// Инициализирует систему перетаскивания для указанного окна.
|
||
/// </summary>
|
||
/// <param name="window">Основное окно приложения, в котором будет работать перетаскивание.</param>
|
||
/// <exception cref="ObjectDisposedException">
|
||
/// Выбрасывается, если менеджер был удален.
|
||
/// </exception>
|
||
/// <remarks>
|
||
/// Этот метод должен быть вызван один раз при запуске приложения, обычно в методе
|
||
/// <see cref="Application.OnLaunched"/>.
|
||
/// </remarks>
|
||
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;
|
||
}
|
||
|
||
/// <summary>
|
||
/// Делает указанный элемент источником перетаскивания.
|
||
/// </summary>
|
||
/// <param name="element">Элемент, который станет источником перетаскивания.</param>
|
||
/// <param name="dragData">Данные, которые будут перетаскиваться. Если не указано, используются
|
||
/// DataContext или Tag элемента.</param>
|
||
/// <remarks>
|
||
/// Если элемент уже зарегистрирован как источник перетаскивания, метод не выполняет действий.
|
||
/// </remarks>
|
||
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;
|
||
}
|
||
|
||
/// <summary>
|
||
/// Делает указанный элемент целью сброса.
|
||
/// </summary>
|
||
/// <param name="element">Элемент, который станет целью сброса.</param>
|
||
/// <remarks>
|
||
/// Если элемент уже зарегистрирован как цель сброса, метод не выполняет действий.
|
||
/// </remarks>
|
||
public void MakeDropTarget(FrameworkElement element)
|
||
{
|
||
if (_disposed || _dropTargets.ContainsKey(element)) return;
|
||
|
||
var behavior = new Behaviors.WinUIDropTargetBehavior(_dragDropService, _host);
|
||
behavior.Attach(element);
|
||
_dropTargets[element] = behavior;
|
||
}
|
||
|
||
/// <summary>
|
||
/// Удаляет возможность перетаскивания.
|
||
/// </summary>
|
||
public void RemoveDragSource(FrameworkElement element)
|
||
{
|
||
if (_dragSources.Remove(element, out var behavior))
|
||
{
|
||
behavior.Detach();
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// Удаляет возможность сброса.
|
||
/// </summary>
|
||
public void RemoveDropTarget(FrameworkElement element)
|
||
{
|
||
if (_dropTargets.Remove(element, out var behavior))
|
||
{
|
||
behavior.Detach();
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// Очищает все регистрации.
|
||
/// </summary>
|
||
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
|
||
} |