доработана winUI реализация
This commit is contained in:
@@ -1,17 +1,4 @@
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using Microsoft.UI.Xaml.Controls.Primitives;
|
||||
using Microsoft.UI.Xaml.Data;
|
||||
using Microsoft.UI.Xaml.Input;
|
||||
using Microsoft.UI.Xaml.Media;
|
||||
using Microsoft.UI.Xaml.Navigation;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices.WindowsRuntime;
|
||||
using Windows.Foundation;
|
||||
using Windows.Foundation.Collections;
|
||||
|
||||
// To learn more about WinUI, the WinUI project structure,
|
||||
// and more about our project templates, see: http://aka.ms/winui-project-info.
|
||||
|
||||
@@ -1,17 +1,4 @@
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using Microsoft.UI.Xaml.Controls.Primitives;
|
||||
using Microsoft.UI.Xaml.Data;
|
||||
using Microsoft.UI.Xaml.Input;
|
||||
using Microsoft.UI.Xaml.Media;
|
||||
using Microsoft.UI.Xaml.Navigation;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices.WindowsRuntime;
|
||||
using Windows.Foundation;
|
||||
using Windows.Foundation.Collections;
|
||||
|
||||
// To learn more about WinUI, the WinUI project structure,
|
||||
// and more about our project templates, see: http://aka.ms/winui-project-info.
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using Lattice.Core.DragDrop.Services;
|
||||
using Lattice.Core.Geometry;
|
||||
using Lattice.Core.Geometry;
|
||||
using Lattice.UI.Docking.Abstractions;
|
||||
using Lattice.UI.Docking.Models;
|
||||
using Lattice.UI.Docking.Services;
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
using Lattice.Core.DragDrop.Abstractions;
|
||||
using Lattice.Core.DragDrop.Models;
|
||||
using Lattice.Core.DragDrop.Models;
|
||||
using Lattice.Core.DragDrop.Services;
|
||||
using Lattice.Core.Geometry;
|
||||
using Lattice.UI.DragDrop.Abstractions;
|
||||
using Lattice.UI.DragDrop.Behaviors;
|
||||
using Lattice.UI.DragDrop.WinUI.Services;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Input;
|
||||
@@ -13,29 +14,54 @@ namespace Lattice.UI.DragDrop.WinUI.Behaviors;
|
||||
|
||||
/// <summary>
|
||||
/// Реализация поведения источника перетаскивания для элементов WinUI.
|
||||
/// Наследуется от <see cref="DragSourceBehaviorBase{FrameworkElement}"/> для использования
|
||||
/// общей логики управления операциями перетаскивания и интеграции с системой <see cref="Core.DragDrop"/>.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// Этот класс обрабатывает события мыши/тач и преобразует их в операции перетаскивания.
|
||||
/// Он реализует интерфейс <see cref="IDragSource"/> для интеграции с системой перетаскивания.
|
||||
/// Этот класс предоставляет конкретную реализацию для платформы WinUI, обрабатывая события
|
||||
/// указателя (мышь, тач, перо) и преобразуя их в операции перетаскивания через центральный
|
||||
/// сервис <see cref="IDragDropService"/>.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// Поведение автоматически отслеживает начало перемещения мыши, проверяет порог
|
||||
/// перетаскивания и инициирует операцию через <see cref="IDragDropService"/>.
|
||||
/// Основные функции:
|
||||
/// <list type="bullet">
|
||||
/// <item>Обработка событий PointerPressed, PointerMoved, PointerReleased</item>
|
||||
/// <item>Автоматическое отслеживание порога начала перетаскивания</item>
|
||||
/// <item>Создание информации о перетаскивании на основе данных элемента</item>
|
||||
/// <item>Интеграция с визуальной обратной связью через <see cref="WinUIDragDropHost"/></item>
|
||||
/// <item>Преобразование координат между локальной системой элемента и экранными координатами</item>
|
||||
/// </list>
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// Для использования необходимо:
|
||||
/// <list type="number">
|
||||
/// <item>Создать экземпляр поведения через фабрику <see cref="Factories.WinUIDragDropFactory.CreateDragSourceBehavior"/></item>
|
||||
/// <item>Прикрепить к элементу с помощью метода <see cref="Attach"/></item>
|
||||
/// <item>Указать данные для перетаскивания (опционально, по умолчанию используется DataContext)</item>
|
||||
/// </list>
|
||||
/// </para>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// // Создание поведения
|
||||
/// var behavior = WinUIDragDropFactory.CreateDragSourceBehavior(dragDropService, host);
|
||||
///
|
||||
/// // Прикрепление к элементу
|
||||
/// behavior.Attach(myElement, myData);
|
||||
///
|
||||
/// // Или через attached properties
|
||||
/// <Border x:Name="DragElement"
|
||||
/// local:DragDropProperties.IsDragSource="True"
|
||||
/// local:DragDropProperties.DragData="{Binding MyData}" />
|
||||
/// </code>
|
||||
/// </example>
|
||||
/// </remarks>
|
||||
public sealed class WinUIDragSourceBehavior : IDragSource
|
||||
public sealed class WinUIDragSourceBehavior : DragSourceBehaviorBase<FrameworkElement>
|
||||
{
|
||||
#region Поля
|
||||
|
||||
private readonly IDragDropService _dragDropService;
|
||||
private readonly WinUIDragDropHost _host;
|
||||
|
||||
private FrameworkElement? _element;
|
||||
private readonly IDragDropHost _host;
|
||||
private object? _dragData;
|
||||
private Point _dragStartPosition;
|
||||
private bool _isDragging;
|
||||
private CancellationTokenSource? _cancellationTokenSource;
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -44,14 +70,27 @@ public sealed class WinUIDragSourceBehavior : IDragSource
|
||||
/// <summary>
|
||||
/// Инициализирует новый экземпляр класса <see cref="WinUIDragSourceBehavior"/>.
|
||||
/// </summary>
|
||||
/// <param name="dragDropService">Сервис для управления операциями перетаскивания.</param>
|
||||
/// <param name="host">Хост для отображения визуальных элементов.</param>
|
||||
/// <param name="dragDropService">
|
||||
/// Сервис управления операциями перетаскивания. Используется для координации
|
||||
/// между источниками и целями перетаскивания.
|
||||
/// </param>
|
||||
/// <param name="host">
|
||||
/// Хост для управления визуальными элементами перетаскивания. Обеспечивает
|
||||
/// отображение визуальной обратной связи во время операции.
|
||||
/// </param>
|
||||
/// <exception cref="ArgumentNullException">
|
||||
/// Выбрасывается, если любой из параметров равен null.
|
||||
/// Выбрасывается, если <paramref name="dragDropService"/> или <paramref name="host"/>
|
||||
/// равны null.
|
||||
/// </exception>
|
||||
public WinUIDragSourceBehavior(IDragDropService dragDropService, WinUIDragDropHost host)
|
||||
/// <remarks>
|
||||
/// Конструктор инициализирует базовый класс <see cref="DragSourceBehaviorBase{FrameworkElement}"/>
|
||||
/// и сохраняет ссылки на необходимые сервисы для последующего использования.
|
||||
/// </remarks>
|
||||
public WinUIDragSourceBehavior(
|
||||
Core.DragDrop.Services.IDragDropService dragDropService,
|
||||
WinUIDragDropHost host)
|
||||
: base(dragDropService)
|
||||
{
|
||||
_dragDropService = dragDropService ?? throw new ArgumentNullException(nameof(dragDropService));
|
||||
_host = host ?? throw new ArgumentNullException(nameof(host));
|
||||
}
|
||||
|
||||
@@ -60,207 +99,202 @@ public sealed class WinUIDragSourceBehavior : IDragSource
|
||||
#region Публичные методы
|
||||
|
||||
/// <summary>
|
||||
/// Прикрепляет поведение к указанному элементу.
|
||||
/// Прикрепляет поведение к указанному элементу WinUI.
|
||||
/// </summary>
|
||||
/// <param name="element">Элемент, к которому прикрепляется поведение.</param>
|
||||
/// <param name="dragData">Данные для перетаскивания. Если не указано, используются
|
||||
/// DataContext или Tag элемента.</param>
|
||||
/// <param name="element">
|
||||
/// Элемент <see cref="FrameworkElement"/>, который должен стать источником перетаскивания.
|
||||
/// Не может быть null.
|
||||
/// </param>
|
||||
/// <param name="dragData">
|
||||
/// Данные, которые будут перетаскиваться. Может быть null.
|
||||
/// Если не указано, используется <see cref="FrameworkElement.DataContext"/> или
|
||||
/// <see cref="FrameworkElement.Tag"/> элемента.
|
||||
/// </param>
|
||||
/// <exception cref="ArgumentNullException">
|
||||
/// Выбрасывается, если <paramref name="element"/> равен null.
|
||||
/// </exception>
|
||||
/// <remarks>
|
||||
/// После вызова этого метода элемент начинает обрабатывать события перетаскивания.
|
||||
/// <para>
|
||||
/// После вызова этого метода:
|
||||
/// <list type="bullet">
|
||||
/// <item>Элементу автоматически подписываются обработчики событий указателя</item>
|
||||
/// <item>Поведение начинает отслеживать взаимодействия с элементом</item>
|
||||
/// <item>При превышении порога перетаскивания инициируется операция через <see cref="Core.DragDrop.Services.IDragDropService"/></item>
|
||||
/// </list>
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// Для открепления поведения используйте метод <see cref="Detach"/>.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public void Attach(FrameworkElement element, object? dragData = null)
|
||||
{
|
||||
Detach();
|
||||
if (element == null)
|
||||
throw new ArgumentNullException(nameof(element));
|
||||
|
||||
_element = element ?? throw new ArgumentNullException(nameof(element));
|
||||
_dragData = dragData ?? element.DataContext ?? element.Tag;
|
||||
|
||||
SubscribeToEvents();
|
||||
AssociatedElement = element;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Открепляет поведение от элемента.
|
||||
/// Открепляет поведение от текущего элемента.
|
||||
/// </summary>
|
||||
public void Detach()
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// Этот метод выполняет следующие действия:
|
||||
/// <list type="bullet">
|
||||
/// <item>Отписывается от всех событий элемента</item>
|
||||
/// <item>Сбрасывает внутреннее состояние</item>
|
||||
/// <item>Освобождает ссылки на связанные объекты</item>
|
||||
/// </list>
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// Если в момент вызова активна операция перетаскивания, она будет автоматически отменена.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// После вызова этого метода поведение может быть повторно прикреплено к другому элементу.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public new void Detach()
|
||||
{
|
||||
if (_element == null) return;
|
||||
|
||||
UnsubscribeFromEvents();
|
||||
|
||||
if (_isDragging)
|
||||
{
|
||||
_dragDropService.CancelDragAsync().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
_element = null;
|
||||
base.Detach();
|
||||
_dragData = null;
|
||||
_isDragging = false;
|
||||
|
||||
_cancellationTokenSource?.Dispose();
|
||||
_cancellationTokenSource = null;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Обработчики событий
|
||||
#region Реализация абстрактных методов DragSourceBehaviorBase<FrameworkElement>
|
||||
|
||||
private void SubscribeToEvents()
|
||||
/// <inheritdoc/>
|
||||
protected override void SubscribeToEvents(FrameworkElement element)
|
||||
{
|
||||
if (_element == null) return;
|
||||
if (element == null) return;
|
||||
|
||||
_element.PointerPressed += OnPointerPressed;
|
||||
_element.PointerMoved += OnPointerMoved;
|
||||
_element.PointerReleased += OnPointerReleased;
|
||||
_element.PointerCanceled += OnPointerCanceled;
|
||||
_element.PointerCaptureLost += OnPointerCaptureLost;
|
||||
element.PointerPressed += OnPointerPressed;
|
||||
element.PointerMoved += OnPointerMoved;
|
||||
element.PointerReleased += OnPointerReleased;
|
||||
element.PointerCanceled += OnPointerCanceled;
|
||||
element.PointerCaptureLost += OnPointerCaptureLost;
|
||||
}
|
||||
|
||||
private void UnsubscribeFromEvents()
|
||||
/// <inheritdoc/>
|
||||
protected override void UnsubscribeFromEvents(FrameworkElement element)
|
||||
{
|
||||
if (_element == null) return;
|
||||
if (element == null) return;
|
||||
|
||||
_element.PointerPressed -= OnPointerPressed;
|
||||
_element.PointerMoved -= OnPointerMoved;
|
||||
_element.PointerReleased -= OnPointerReleased;
|
||||
_element.PointerCanceled -= OnPointerCanceled;
|
||||
_element.PointerCaptureLost -= OnPointerCaptureLost;
|
||||
element.PointerPressed -= OnPointerPressed;
|
||||
element.PointerMoved -= OnPointerMoved;
|
||||
element.PointerReleased -= OnPointerReleased;
|
||||
element.PointerCanceled -= OnPointerCanceled;
|
||||
element.PointerCaptureLost -= OnPointerCaptureLost;
|
||||
}
|
||||
|
||||
private void OnPointerPressed(object sender, PointerRoutedEventArgs e)
|
||||
/// <inheritdoc/>
|
||||
protected override Point ConvertToScreenCoordinates(Point point)
|
||||
{
|
||||
if (_element == null || _isDragging) return;
|
||||
|
||||
var point = e.GetCurrentPoint(_element);
|
||||
_dragStartPosition = new Point(point.Position.X, point.Position.Y);
|
||||
|
||||
_cancellationTokenSource?.Dispose();
|
||||
_cancellationTokenSource = new CancellationTokenSource();
|
||||
}
|
||||
|
||||
private async void OnPointerMoved(object sender, PointerRoutedEventArgs e)
|
||||
{
|
||||
if (_element == null || _isDragging || _cancellationTokenSource?.IsCancellationRequested == true)
|
||||
return;
|
||||
|
||||
var point = e.GetCurrentPoint(_element);
|
||||
var currentPosition = new Point(point.Position.X, point.Position.Y);
|
||||
|
||||
var distance = CalculateDistance(_dragStartPosition, currentPosition);
|
||||
if (distance > _dragDropService.DragStartThreshold)
|
||||
{
|
||||
await StartDragAsync(currentPosition);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnPointerReleased(object sender, PointerRoutedEventArgs e)
|
||||
{
|
||||
ResetState();
|
||||
}
|
||||
|
||||
private void OnPointerCanceled(object sender, PointerRoutedEventArgs e)
|
||||
{
|
||||
ResetState();
|
||||
}
|
||||
|
||||
private void OnPointerCaptureLost(object sender, PointerRoutedEventArgs e)
|
||||
{
|
||||
ResetState();
|
||||
}
|
||||
|
||||
private double CalculateDistance(Point p1, Point p2)
|
||||
{
|
||||
var dx = p2.X - p1.X;
|
||||
var dy = p2.Y - p1.Y;
|
||||
return Math.Sqrt(dx * dx + dy * dy);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Преобразует координаты элемента в экранные координаты.
|
||||
/// </summary>
|
||||
/// <param name="point">Точка в координатах элемента.</param>
|
||||
/// <returns>Точка в экранных координатах.</returns>
|
||||
private Point ConvertToScreenCoordinates(Point point)
|
||||
{
|
||||
if (_element == null) return point;
|
||||
|
||||
try
|
||||
{
|
||||
var transform = _element.TransformToVisual(Window.Current.Content);
|
||||
var screenPoint = transform.TransformPoint(new Windows.Foundation.Point(point.X, point.Y));
|
||||
return new Point(screenPoint.X, screenPoint.Y);
|
||||
}
|
||||
catch
|
||||
{
|
||||
if (AssociatedElement == null)
|
||||
return point;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task StartDragAsync(Point position)
|
||||
{
|
||||
if (_element == null || _dragData == null || _isDragging) return;
|
||||
|
||||
try
|
||||
{
|
||||
var screenPosition = ConvertToScreenCoordinates(position);
|
||||
var started = await _dragDropService.StartDragAsync(this, screenPosition);
|
||||
_isDragging = started;
|
||||
}
|
||||
catch
|
||||
{
|
||||
ResetState();
|
||||
}
|
||||
}
|
||||
|
||||
private void ResetState()
|
||||
{
|
||||
_isDragging = false;
|
||||
_dragStartPosition = default;
|
||||
|
||||
_cancellationTokenSource?.Cancel();
|
||||
_cancellationTokenSource?.Dispose();
|
||||
_cancellationTokenSource = null;
|
||||
return WinUIWindowHelper.ConvertToScreenCoordinates(AssociatedElement, point);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IDragSource Implementation
|
||||
#region Реализация интерфейса IDragSource
|
||||
|
||||
public async Task<DragInfo?> TryStartDragAsync(Point startPosition, CancellationToken cancellationToken = default)
|
||||
/// <inheritdoc/>
|
||||
public override async Task<DragInfo?> TryStartDragAsync(
|
||||
Point startPosition,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (_element == null || _dragData == null)
|
||||
if (AssociatedElement == null || _dragData == null)
|
||||
return null;
|
||||
|
||||
try
|
||||
{
|
||||
// Создаем информацию о перетаскивании
|
||||
var dragInfo = new DragInfo(
|
||||
data: _dragData,
|
||||
allowedEffects: Core.DragDrop.Enums.DragDropEffects.Copy |
|
||||
Core.DragDrop.Enums.DragDropEffects.Move,
|
||||
Core.DragDrop.Enums.DragDropEffects.Move |
|
||||
Core.DragDrop.Enums.DragDropEffects.Link,
|
||||
startPosition: startPosition,
|
||||
source: this
|
||||
);
|
||||
|
||||
return dragInfo;
|
||||
// Добавляем дополнительные параметры
|
||||
dragInfo.SetParameter("SourceElement", AssociatedElement);
|
||||
dragInfo.SetParameter("SourceType", AssociatedElement.GetType().Name);
|
||||
|
||||
return await Task.FromResult(dragInfo);
|
||||
}
|
||||
catch
|
||||
catch (Exception ex)
|
||||
{
|
||||
// Логирование ошибки создания информации о перетаскивании
|
||||
System.Diagnostics.Debug.WriteLine(
|
||||
$"Ошибка создания DragInfo: {ex.Message}");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public Task OnDragCompletedAsync(DragInfo dragInfo, Core.DragDrop.Enums.DragDropEffects effects, CancellationToken cancellationToken = default)
|
||||
#endregion
|
||||
|
||||
#region Обработчики событий WinUI
|
||||
|
||||
private async void OnPointerPressed(object sender, PointerRoutedEventArgs e)
|
||||
{
|
||||
_isDragging = false;
|
||||
return Task.CompletedTask;
|
||||
if (AssociatedElement == null) return;
|
||||
|
||||
var point = e.GetCurrentPoint(AssociatedElement);
|
||||
var position = new Point(point.Position.X, point.Position.Y);
|
||||
|
||||
await OnInteractionStarted(position);
|
||||
}
|
||||
|
||||
public Task OnDragCancelledAsync(DragInfo dragInfo, CancellationToken cancellationToken = default)
|
||||
private async void OnPointerMoved(object sender, PointerRoutedEventArgs e)
|
||||
{
|
||||
_isDragging = false;
|
||||
return Task.CompletedTask;
|
||||
if (AssociatedElement == null) return;
|
||||
|
||||
var point = e.GetCurrentPoint(AssociatedElement);
|
||||
var position = new Point(point.Position.X, point.Position.Y);
|
||||
|
||||
await OnInteractionMoved(position);
|
||||
}
|
||||
|
||||
private async void OnPointerReleased(object sender, PointerRoutedEventArgs e)
|
||||
{
|
||||
await OnInteractionEnded();
|
||||
}
|
||||
|
||||
private async void OnPointerCanceled(object sender, PointerRoutedEventArgs e)
|
||||
{
|
||||
await OnInteractionCancelled();
|
||||
}
|
||||
|
||||
private async void OnPointerCaptureLost(object sender, PointerRoutedEventArgs e)
|
||||
{
|
||||
await OnInteractionCancelled();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Переопределение виртуальных методов
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void OnDragCompleted(DragInfo dragInfo, Core.DragDrop.Enums.DragDropEffects effects)
|
||||
{
|
||||
base.OnDragCompleted(dragInfo, effects);
|
||||
|
||||
// Дополнительная логика для WinUI может быть добавлена здесь
|
||||
// Например, обновление состояния элемента после успешного перетаскивания
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void OnDragCancelled(DragInfo dragInfo)
|
||||
{
|
||||
base.OnDragCancelled(dragInfo);
|
||||
|
||||
// Дополнительная логика для WinUI может быть добавлена здесь
|
||||
// Например, восстановление визуального состояния элемента после отмены
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
using Lattice.Core.DragDrop.Abstractions;
|
||||
using Lattice.Core.DragDrop.Models;
|
||||
using Lattice.Core.DragDrop.Models;
|
||||
using Lattice.Core.Geometry;
|
||||
using Lattice.UI.DragDrop.WinUI.Services;
|
||||
using Lattice.UI.DragDrop.Abstractions;
|
||||
using Lattice.UI.DragDrop.Behaviors;
|
||||
using Microsoft.UI.Xaml;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
@@ -11,27 +13,52 @@ namespace Lattice.UI.DragDrop.WinUI.Behaviors;
|
||||
|
||||
/// <summary>
|
||||
/// Реализация поведения цели сброса для элементов WinUI.
|
||||
/// Наследуется от <see cref="DropTargetBehaviorBase{FrameworkElement}"/> для использования
|
||||
/// общей логики регистрации целей и обработки операций сброса.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// Этот класс обрабатывает события перетаскивания WinUI и преобразует их в вызовы
|
||||
/// методов интерфейса <see cref="IDropTarget"/>.
|
||||
/// Этот класс предоставляет конкретную реализацию для платформы WinUI, обрабатывая события
|
||||
/// перетаскивания WinUI и преобразуя их в вызовы методов интерфейса <see cref="Core.DragDrop.Abstractions.IDropTarget"/>.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// Поведение автоматически регистрирует элемент в системе перетаскивания,
|
||||
/// обновляет его границы при изменении размера и обрабатывает все этапы операции сброса.
|
||||
/// Основные функции:
|
||||
/// <list type="bullet">
|
||||
/// <item>Автоматическая регистрация в <see cref="IDragDropService"/> при прикреплении к элементу</item>
|
||||
/// <item>Обработка событий DragEnter, DragOver, DragLeave, Drop</item>
|
||||
/// <item>Автоматическое обновление границ элемента при изменении размера или позиции</item>
|
||||
/// <item>Поддержка фильтрации принимаемых типов данных</item>
|
||||
/// <item>Интеграция с визуальной обратной связью</item>
|
||||
/// </list>
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// Для использования необходимо:
|
||||
/// <list type="number">
|
||||
/// <item>Создать экземпляр поведения через фабрику <see cref="Factories.WinUIDragDropFactory.CreateDropTargetBehavior"/></item>
|
||||
/// <item>Прикрепить к элементу с помощью метода <see cref="Attach"/></item>
|
||||
/// <item>Настроить фильтры принимаемых данных (опционально)</item>
|
||||
/// </list>
|
||||
/// </para>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// // Создание поведения с фильтрацией типов
|
||||
/// var behavior = WinUIDragDropFactory.CreateDropTargetBehavior(dragDropService, host);
|
||||
/// behavior.AcceptTypes(typeof(MyDataModel), typeof(string));
|
||||
/// behavior.Attach(myDropArea);
|
||||
///
|
||||
/// // Или через attached properties
|
||||
/// <Border x:Name="DropArea"
|
||||
/// local:DragDropProperties.IsDropTarget="True" />
|
||||
/// </code>
|
||||
/// </example>
|
||||
/// </remarks>
|
||||
public sealed class WinUIDropTargetBehavior : IDropTarget
|
||||
public sealed class WinUIDropTargetBehavior : DropTargetBehaviorBase<FrameworkElement>
|
||||
{
|
||||
#region Поля
|
||||
|
||||
private readonly Lattice.Core.DragDrop.Services.IDragDropService _dragDropService;
|
||||
private readonly WinUIDragDropHost _host;
|
||||
|
||||
private FrameworkElement? _element;
|
||||
private string? _registrationId;
|
||||
private Rect _currentBounds;
|
||||
private readonly IDragDropHost _host;
|
||||
private readonly List<Type> _acceptedTypes = new();
|
||||
private readonly HashSet<string> _acceptedFormats = new();
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -40,14 +67,28 @@ public sealed class WinUIDropTargetBehavior : IDropTarget
|
||||
/// <summary>
|
||||
/// Инициализирует новый экземпляр класса <see cref="WinUIDropTargetBehavior"/>.
|
||||
/// </summary>
|
||||
/// <param name="dragDropService">Сервис для управления операциями перетаскивания.</param>
|
||||
/// <param name="host">Хост для отображения визуальных элементов.</param>
|
||||
/// <param name="dragDropService">
|
||||
/// Сервис управления операциями перетаскивания. Используется для регистрации
|
||||
/// цели и координации операций сброса.
|
||||
/// </param>
|
||||
/// <param name="host">
|
||||
/// Хост для управления визуальной обратной связью. Обеспечивает отображение
|
||||
/// индикаторов возможности сброса.
|
||||
/// </param>
|
||||
/// <exception cref="ArgumentNullException">
|
||||
/// Выбрасывается, если любой из параметров равен null.
|
||||
/// Выбрасывается, если <paramref name="dragDropService"/> или <paramref name="host"/>
|
||||
/// равны null.
|
||||
/// </exception>
|
||||
public WinUIDropTargetBehavior(Lattice.Core.DragDrop.Services.IDragDropService dragDropService, WinUIDragDropHost host)
|
||||
/// <remarks>
|
||||
/// Конструктор инициализирует базовый класс и сохраняет ссылки на сервисы.
|
||||
/// По умолчанию цель принимает все типы данных. Для настройки фильтрации
|
||||
/// используйте методы <see cref="AcceptTypes"/> и <see cref="AcceptFormats"/>.
|
||||
/// </remarks>
|
||||
public WinUIDropTargetBehavior(
|
||||
Core.DragDrop.Services.IDragDropService dragDropService,
|
||||
IDragDropHost host)
|
||||
: base(dragDropService)
|
||||
{
|
||||
_dragDropService = dragDropService ?? throw new ArgumentNullException(nameof(dragDropService));
|
||||
_host = host ?? throw new ArgumentNullException(nameof(host));
|
||||
}
|
||||
|
||||
@@ -56,106 +97,301 @@ public sealed class WinUIDropTargetBehavior : IDropTarget
|
||||
#region Публичные методы
|
||||
|
||||
/// <summary>
|
||||
/// Прикрепляет поведение к указанному элементу.
|
||||
/// Прикрепляет поведение к указанному элементу WinUI.
|
||||
/// </summary>
|
||||
/// <param name="element">Элемент, к которому прикрепляется поведение.</param>
|
||||
/// <param name="element">
|
||||
/// Элемент <see cref="FrameworkElement"/>, который должен стать целью сброса.
|
||||
/// Не может быть null.
|
||||
/// </param>
|
||||
/// <exception cref="ArgumentNullException">
|
||||
/// Выбрасывается, если <paramref name="element"/> равен null.
|
||||
/// </exception>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// После вызова этого метода:
|
||||
/// 1. Элементу устанавливается <see cref="UIElement.AllowDrop"/> = true
|
||||
/// 2. Поведение подписывается на события перетаскивания
|
||||
/// 3. Элемент регистрируется в системе перетаскивания
|
||||
/// <list type="bullet">
|
||||
/// <item>Элементу устанавливается свойство <see cref="UIElement.AllowDrop"/> = true</item>
|
||||
/// <item>Поведение подписывается на события перетаскивания WinUI</item>
|
||||
/// <item>Элемент регистрируется в системе перетаскивания с текущими границами</item>
|
||||
/// <item>Начинается отслеживание изменений размера и позиции элемента</item>
|
||||
/// </list>
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// Для открепления поведения используйте метод <see cref="Detach"/>.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public void Attach(FrameworkElement element)
|
||||
{
|
||||
Detach();
|
||||
if (element == null)
|
||||
throw new ArgumentNullException(nameof(element));
|
||||
|
||||
_element = element ?? throw new ArgumentNullException(nameof(element));
|
||||
element.AllowDrop = true;
|
||||
|
||||
SubscribeToEvents();
|
||||
UpdateBounds();
|
||||
RegisterToService();
|
||||
AssociatedElement = element;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Открепляет поведение от элемента.
|
||||
/// Настраивает поведение для приема только указанных типов данных.
|
||||
/// </summary>
|
||||
public void Detach()
|
||||
/// <param name="types">
|
||||
/// Типы данных, которые может принимать цель. Если пусто, принимаются все типы.
|
||||
/// </param>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// Этот метод позволяет ограничить типы данных, которые могут быть сброшены на цель.
|
||||
/// Проверка выполняется в методе <see cref="CanAcceptDropAsync"/> путем сравнения
|
||||
/// типа сбрасываемых данных с указанными типами.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// Если метод не вызывался или передан пустой список, цель будет принимать данные любого типа.
|
||||
/// </para>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// // Принимать только строки и объекты MyModel
|
||||
/// behavior.AcceptTypes(typeof(string), typeof(MyModel));
|
||||
/// </code>
|
||||
/// </example>
|
||||
/// </remarks>
|
||||
public void AcceptTypes(params Type[] types)
|
||||
{
|
||||
if (_element == null) return;
|
||||
_acceptedTypes.Clear();
|
||||
if (types != null && types.Length > 0)
|
||||
{
|
||||
_acceptedTypes.AddRange(types);
|
||||
}
|
||||
}
|
||||
|
||||
UnsubscribeFromEvents();
|
||||
UnregisterFromService();
|
||||
/// <summary>
|
||||
/// Настраивает поведение для приема только указанных форматов данных.
|
||||
/// </summary>
|
||||
/// <param name="formats">
|
||||
/// Форматы данных (например, "Text", "Bitmap", "FileDrop"), которые может принимать цель.
|
||||
/// Если пусто, формат не проверяется.
|
||||
/// </param>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// Этот метод позволяет ограничить форматы данных, которые могут быть сброшены на цель.
|
||||
/// Актуально для межпроцессного перетаскивания или работы с системными форматами.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// Если метод не вызывался или передан пустой список, проверка формата не выполняется.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public void AcceptFormats(params string[] formats)
|
||||
{
|
||||
_acceptedFormats.Clear();
|
||||
if (formats != null && formats.Length > 0)
|
||||
{
|
||||
foreach (var format in formats)
|
||||
{
|
||||
_acceptedFormats.Add(format);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_element.AllowDrop = false;
|
||||
_element = null;
|
||||
_currentBounds = Rect.Empty;
|
||||
/// <summary>
|
||||
/// Открепляет поведение от текущего элемента.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// Этот метод выполняет следующие действия:
|
||||
/// <list type="bullet">
|
||||
/// <item>Отписывается от всех событий элемента</item>
|
||||
/// <item>Отменяет регистрацию цели в системе перетаскивания</item>
|
||||
/// <item>Сбрасывает свойство <see cref="UIElement.AllowDrop"/> = false</item>
|
||||
/// <item>Освобождает ссылки на связанные объекты</item>
|
||||
/// </list>
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// После вызова этого метода поведение может быть повторно прикреплено к другому элементу.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public new void Detach()
|
||||
{
|
||||
if (AssociatedElement != null)
|
||||
{
|
||||
AssociatedElement.AllowDrop = false;
|
||||
}
|
||||
base.Detach();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Обработчики событий
|
||||
#region Реализация абстрактных методов DropTargetBehaviorBase<FrameworkElement>
|
||||
|
||||
private void SubscribeToEvents()
|
||||
/// <inheritdoc/>
|
||||
protected override void SubscribeToEvents(FrameworkElement element)
|
||||
{
|
||||
if (_element == null) return;
|
||||
if (element == null) return;
|
||||
|
||||
_element.DragEnter += OnDragEnter;
|
||||
_element.DragOver += OnDragOver;
|
||||
_element.DragLeave += OnDragLeave;
|
||||
_element.Drop += OnDrop;
|
||||
_element.SizeChanged += OnSizeChanged;
|
||||
element.DragEnter += OnDragEnter;
|
||||
element.DragOver += OnDragOver;
|
||||
element.DragLeave += OnDragLeave;
|
||||
element.Drop += OnDrop;
|
||||
element.SizeChanged += OnSizeChanged;
|
||||
element.LayoutUpdated += OnLayoutUpdated;
|
||||
}
|
||||
|
||||
private void UnsubscribeFromEvents()
|
||||
/// <inheritdoc/>
|
||||
protected override void UnsubscribeFromEvents(FrameworkElement element)
|
||||
{
|
||||
if (_element == null) return;
|
||||
if (element == null) return;
|
||||
|
||||
_element.DragEnter -= OnDragEnter;
|
||||
_element.DragOver -= OnDragOver;
|
||||
_element.DragLeave -= OnDragLeave;
|
||||
_element.Drop -= OnDrop;
|
||||
_element.SizeChanged -= OnSizeChanged;
|
||||
element.DragEnter -= OnDragEnter;
|
||||
element.DragOver -= OnDragOver;
|
||||
element.DragLeave -= OnDragLeave;
|
||||
element.Drop -= OnDrop;
|
||||
element.SizeChanged -= OnSizeChanged;
|
||||
element.LayoutUpdated -= OnLayoutUpdated;
|
||||
}
|
||||
|
||||
private async void OnDragEnter(object sender, DragEventArgs e)
|
||||
/// <inheritdoc/>
|
||||
protected override Rect GetScreenBounds(FrameworkElement element)
|
||||
{
|
||||
if (_element == null) return;
|
||||
if (element == null || !element.IsLoaded)
|
||||
return Rect.Empty;
|
||||
|
||||
try
|
||||
{
|
||||
var position = e.GetPosition(_element);
|
||||
var window = Window.Current;
|
||||
if (window?.Content == null)
|
||||
return Rect.Empty;
|
||||
|
||||
// Преобразуем локальные координаты элемента в координаты окна
|
||||
var transform = element.TransformToVisual(window.Content);
|
||||
var position = transform.TransformPoint(new Windows.Foundation.Point(0, 0));
|
||||
|
||||
return new Rect(
|
||||
position.X,
|
||||
position.Y,
|
||||
element.ActualWidth,
|
||||
element.ActualHeight
|
||||
);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine(
|
||||
$"Ошибка получения границ элемента: {ex.Message}");
|
||||
return Rect.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Реализация интерфейса IDropTarget
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override async Task<bool> CanAcceptDropAsync(
|
||||
DropInfo dropInfo,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
// Проверяем, есть ли данные для сброса
|
||||
if (dropInfo.Data == null)
|
||||
return false;
|
||||
|
||||
// Проверяем фильтр по типам
|
||||
if (_acceptedTypes.Count > 0)
|
||||
{
|
||||
var dataType = dropInfo.Data.GetType();
|
||||
if (!_acceptedTypes.Any(t => t.IsAssignableFrom(dataType)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Проверяем фильтр по форматам (если данные предоставляют информацию о формате)
|
||||
if (_acceptedFormats.Count > 0 && dropInfo.Data is Windows.ApplicationModel.DataTransfer.DataPackageView dataView)
|
||||
{
|
||||
var availableFormats = dataView.AvailableFormats;
|
||||
if (!_acceptedFormats.Any(f => availableFormats.Contains(f)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Дополнительная проверка может быть добавлена в производных классах
|
||||
return await Task.FromResult(true);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override async Task OnDragOverAsync(
|
||||
DropInfo dropInfo,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
await base.OnDragOverAsync(dropInfo, cancellationToken);
|
||||
|
||||
// Дополнительная логика для WinUI может быть добавлена здесь
|
||||
// Например, обновление визуальной обратной связи через хост
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override async Task OnDropAsync(
|
||||
DropInfo dropInfo,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
// Базовая реализация вызывает CanAcceptDropAsync и помечает как обработанное
|
||||
if (await CanAcceptDropAsync(dropInfo, cancellationToken))
|
||||
{
|
||||
dropInfo.MarkAsHandled();
|
||||
|
||||
// Здесь может быть добавлена логика обработки сброшенных данных
|
||||
// Например, вызов события или обновление модели данных
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override async Task OnDragLeaveAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
await base.OnDragLeaveAsync(cancellationToken);
|
||||
|
||||
// Дополнительная логика для WinUI может быть добавлена здесь
|
||||
// Например, скрытие визуальной обратной связи
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Обработчики событий WinUI
|
||||
|
||||
private async void OnDragEnter(object sender, DragEventArgs e)
|
||||
{
|
||||
if (AssociatedElement == null) return;
|
||||
|
||||
try
|
||||
{
|
||||
var position = e.GetPosition(AssociatedElement);
|
||||
var dropInfo = CreateDropInfo(e, new Point(position.X, position.Y));
|
||||
|
||||
if (await CanAcceptDropAsync(dropInfo))
|
||||
{
|
||||
e.AcceptedOperation = Windows.ApplicationModel.DataTransfer.DataPackageOperation.Copy;
|
||||
e.Handled = true;
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
catch (Exception ex)
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine($"Ошибка в OnDragEnter: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private async void OnDragOver(object sender, DragEventArgs e)
|
||||
{
|
||||
if (_element == null) return;
|
||||
if (AssociatedElement == null) return;
|
||||
|
||||
try
|
||||
{
|
||||
var position = e.GetPosition(_element);
|
||||
var position = e.GetPosition(AssociatedElement);
|
||||
var dropInfo = CreateDropInfo(e, new Point(position.X, position.Y));
|
||||
|
||||
if (await CanAcceptDropAsync(dropInfo))
|
||||
{
|
||||
e.AcceptedOperation = Windows.ApplicationModel.DataTransfer.DataPackageOperation.Copy;
|
||||
await OnDragOverAsync(dropInfo);
|
||||
e.Handled = true;
|
||||
}
|
||||
|
||||
e.Handled = true;
|
||||
}
|
||||
catch { }
|
||||
catch (Exception ex)
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine($"Ошибка в OnDragOver: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private async void OnDragLeave(object sender, DragEventArgs e)
|
||||
@@ -165,131 +401,114 @@ public sealed class WinUIDropTargetBehavior : IDropTarget
|
||||
|
||||
private async void OnDrop(object sender, DragEventArgs e)
|
||||
{
|
||||
if (_element == null) return;
|
||||
if (AssociatedElement == null) return;
|
||||
|
||||
try
|
||||
{
|
||||
var position = e.GetPosition(_element);
|
||||
var position = e.GetPosition(AssociatedElement);
|
||||
var dropInfo = CreateDropInfo(e, new Point(position.X, position.Y));
|
||||
|
||||
if (await CanAcceptDropAsync(dropInfo))
|
||||
{
|
||||
await OnDropAsync(dropInfo);
|
||||
dropInfo.MarkAsHandled();
|
||||
e.Handled = true;
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
catch (Exception ex)
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine($"Ошибка в OnDrop: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private void OnSizeChanged(object sender, SizeChangedEventArgs e)
|
||||
{
|
||||
UpdateBounds();
|
||||
OnElementLayoutChanged();
|
||||
}
|
||||
|
||||
private void OnLayoutUpdated(object sender, object e)
|
||||
{
|
||||
OnElementLayoutChanged();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Вспомогательные методы
|
||||
|
||||
/// <summary>
|
||||
/// Создает объект <see cref="DropInfo"/> на основе события перетаскивания WinUI.
|
||||
/// </summary>
|
||||
/// <param name="e">Аргументы события перетаскивания WinUI.</param>
|
||||
/// <param name="position">Локальная позиция курсора относительно элемента.</param>
|
||||
/// <returns>
|
||||
/// Экземпляр <see cref="DropInfo"/>, содержащий информацию о потенциальном сбросе.
|
||||
/// </returns>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// Этот метод извлекает данные из события перетаскивания и преобразует их
|
||||
/// в формат, понятный системе <see cref="Core.DragDrop"/>.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// Поддерживаются как пользовательские данные (через свойство "DragData"),
|
||||
/// так и стандартные форматы данных WinUI.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
private DropInfo CreateDropInfo(DragEventArgs e, Point position)
|
||||
{
|
||||
object? data = null;
|
||||
|
||||
// Пытаемся получить пользовательские данные
|
||||
if (e.DataView.Properties.TryGetValue("DragData", out var dragData))
|
||||
{
|
||||
data = dragData;
|
||||
}
|
||||
// Или получаем данные из DataPackage
|
||||
else if (e.DataView.Contains(Windows.ApplicationModel.DataTransfer.StandardDataFormats.Text))
|
||||
{
|
||||
// Для текстовых данных можем установить асинхронную загрузку
|
||||
data = new AsyncDataProvider(async () =>
|
||||
{
|
||||
return await e.DataView.GetTextAsync();
|
||||
});
|
||||
}
|
||||
|
||||
// Определяем разрешенные эффекты на основе модификаторов клавиатуры
|
||||
var allowedEffects = Core.DragDrop.Enums.DragDropEffects.None;
|
||||
if (e.AllowedOperations.HasFlag(Windows.ApplicationModel.DataTransfer.DataPackageOperation.Copy))
|
||||
allowedEffects |= Core.DragDrop.Enums.DragDropEffects.Copy;
|
||||
if (e.AllowedOperations.HasFlag(Windows.ApplicationModel.DataTransfer.DataPackageOperation.Move))
|
||||
allowedEffects |= Core.DragDrop.Enums.DragDropEffects.Move;
|
||||
if (e.AllowedOperations.HasFlag(Windows.ApplicationModel.DataTransfer.DataPackageOperation.Link))
|
||||
allowedEffects |= Core.DragDrop.Enums.DragDropEffects.Link;
|
||||
|
||||
return new DropInfo(
|
||||
data: data,
|
||||
position: position,
|
||||
allowedEffects: Core.DragDrop.Enums.DragDropEffects.Copy |
|
||||
Core.DragDrop.Enums.DragDropEffects.Move,
|
||||
target: _element
|
||||
allowedEffects: allowedEffects,
|
||||
target: this
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Получает границы элемента в экранных координатах.
|
||||
/// </summary>
|
||||
/// <returns>Прямоугольник с границами элемента или <see cref="Rect.Empty"/>, если
|
||||
/// элемент не загружен или не может быть преобразован.</returns>
|
||||
private Rect GetScreenBounds()
|
||||
{
|
||||
if (_element == null || !_element.IsLoaded)
|
||||
return Rect.Empty;
|
||||
|
||||
try
|
||||
{
|
||||
var transform = _element.TransformToVisual(Window.Current.Content);
|
||||
var position = transform.TransformPoint(new Windows.Foundation.Point(0, 0));
|
||||
|
||||
return new Rect(
|
||||
position.X,
|
||||
position.Y,
|
||||
_element.ActualWidth,
|
||||
_element.ActualHeight
|
||||
);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return Rect.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateBounds()
|
||||
{
|
||||
if (_element == null) return;
|
||||
|
||||
_currentBounds = GetScreenBounds();
|
||||
|
||||
if (_registrationId != null && _currentBounds != Rect.Empty)
|
||||
{
|
||||
_dragDropService.UpdateDropTargetBounds(_registrationId, _currentBounds);
|
||||
}
|
||||
}
|
||||
|
||||
private void RegisterToService()
|
||||
{
|
||||
if (_element == null) return;
|
||||
|
||||
_currentBounds = GetScreenBounds();
|
||||
|
||||
if (_currentBounds != Rect.Empty && _registrationId == null)
|
||||
{
|
||||
_registrationId = _dragDropService.RegisterDropTarget(this, _currentBounds);
|
||||
}
|
||||
}
|
||||
|
||||
private void UnregisterFromService()
|
||||
{
|
||||
if (_registrationId != null)
|
||||
{
|
||||
_dragDropService.UnregisterDropTarget(_registrationId);
|
||||
_registrationId = null;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
#region IDropTarget Implementation
|
||||
/// <summary>
|
||||
/// Предоставляет асинхронный доступ к данным перетаскивания.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Этот класс используется для отложенной загрузки данных перетаскивания,
|
||||
/// что особенно важно для больших данных или данных, требующих обработки.
|
||||
/// </remarks>
|
||||
internal class AsyncDataProvider
|
||||
{
|
||||
private readonly Func<Task<object>> _dataLoader;
|
||||
|
||||
public Task<bool> CanAcceptDropAsync(DropInfo dropInfo, CancellationToken ct = default)
|
||||
public AsyncDataProvider(Func<Task<object>> dataLoader)
|
||||
{
|
||||
return Task.FromResult(true); // Принимаем все по умолчанию
|
||||
_dataLoader = dataLoader;
|
||||
}
|
||||
|
||||
public Task OnDragOverAsync(DropInfo dropInfo, CancellationToken ct = default)
|
||||
public async Task<object> GetDataAsync()
|
||||
{
|
||||
dropInfo.SuggestedEffects = Core.DragDrop.Enums.DragDropEffects.Move;
|
||||
return Task.CompletedTask;
|
||||
return await _dataLoader();
|
||||
}
|
||||
|
||||
public Task OnDropAsync(DropInfo dropInfo, CancellationToken ct = default)
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task OnDragLeaveAsync(CancellationToken ct = default)
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -1,30 +1,64 @@
|
||||
using Lattice.Core.DragDrop.Factories;
|
||||
using Lattice.Core.DragDrop.Services;
|
||||
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;
|
||||
|
||||
/// <summary>
|
||||
/// Фабрика для создания компонентов системы перетаскивания WinUI.
|
||||
/// Предоставляет методы для быстрой настройки drag-and-drop в приложениях WinUI.
|
||||
/// Фабрика для создания и настройки компонентов системы перетаскивания WinUI.
|
||||
/// Предоставляет удобные методы для быстрой интеграции drag-and-drop функциональности
|
||||
/// в приложениях WinUI с поддержкой различных сценариев использования.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Эта фабрика упрощает интеграцию системы перетаскивания в WinUI приложения,
|
||||
/// предоставляя готовые методы для наиболее распространенных сценариев использования.
|
||||
/// <para>
|
||||
/// <see cref="WinUIDragDropFactory"/> служит высокоуровневым API для работы с системой
|
||||
/// перетаскивания, инкапсулируя сложность инициализации и настройки компонентов.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// Основные возможности фабрики:
|
||||
/// <list type="bullet">
|
||||
/// <item>Создание и инициализация менеджера перетаскивания</item>
|
||||
/// <item>Генерация поведений для источников и целей перетаскивания</item>
|
||||
/// <item>Создание визуальных элементов для обратной связи</item>
|
||||
/// <item>Предварительные конфигурации для типовых сценариев</item>
|
||||
/// <item>Вспомогательные методы для работы с XAML</item>
|
||||
/// </list>
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// Фабрика поддерживает два подхода к использованию:
|
||||
/// <list type="number">
|
||||
/// <item><strong>Императивный подход</strong> - создание компонентов в коде C#</item>
|
||||
/// <item><strong>Декларативный подход</strong> - использование attached properties в XAML</item>
|
||||
/// </list>
|
||||
/// </para>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// // Императивный подход
|
||||
/// 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" />
|
||||
/// </code>
|
||||
/// </example>
|
||||
/// </remarks>
|
||||
public static class WinUIDragDropFactory
|
||||
{
|
||||
#region Основные компоненты
|
||||
|
||||
/// <summary>
|
||||
/// Создает и инициализирует менеджер перетаскивания для WinUI окна.
|
||||
/// Создает и инициализирует менеджер перетаскивания для указанного окна WinUI.
|
||||
/// </summary>
|
||||
/// <param name="window">
|
||||
/// Окно WinUI, для которого создается менеджер перетаскивания.
|
||||
/// Не может быть null.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// Инициализированный экземпляр <see cref="WinUIDragDropManager"/>.
|
||||
@@ -33,30 +67,38 @@ public static class WinUIDragDropFactory
|
||||
/// Выбрасывается, когда <paramref name="window"/> равен null.
|
||||
/// </exception>
|
||||
/// <remarks>
|
||||
/// Этот метод создает менеджер перетаскивания и настраивает его для работы с указанным окном.
|
||||
/// Менеджер автоматически создает оверлей для визуальных элементов и подписывается на события.
|
||||
/// </remarks>
|
||||
/// <para>
|
||||
/// Этот метод является основным способом получения менеджера перетаскивания.
|
||||
/// Он создает (или возвращает существующий) экземпляр менеджера и инициализирует
|
||||
/// его для работы с указанным окном.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// Метод следует вызывать один раз при запуске приложения, обычно в конструкторе
|
||||
/// главного окна или в методе <see cref="Application.OnLaunched"/>.
|
||||
/// </para>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// public partial class MainWindow : Window
|
||||
/// {
|
||||
/// private WinUIDragDropManager _dragDropManager;
|
||||
///
|
||||
/// public MainWindow()
|
||||
/// {
|
||||
/// InitializeComponent();
|
||||
/// _dragDropManager = WinUIDragDropFactory.CreateManager(this);
|
||||
/// var manager = WinUIDragDropFactory.CreateManager(this);
|
||||
/// }
|
||||
/// }
|
||||
/// </code>
|
||||
/// </example>
|
||||
/// </remarks>
|
||||
public static WinUIDragDropManager CreateManager(Window window)
|
||||
{
|
||||
if (window == null)
|
||||
throw new ArgumentNullException(nameof(window));
|
||||
|
||||
var manager = WinUIDragDropManager.Instance;
|
||||
manager.Initialize(window);
|
||||
if (!manager.IsInitialized)
|
||||
{
|
||||
manager.Initialize(window);
|
||||
}
|
||||
return manager;
|
||||
}
|
||||
|
||||
@@ -67,14 +109,34 @@ public static class WinUIDragDropFactory
|
||||
/// Окно WinUI, для которого создается менеджер перетаскивания.
|
||||
/// </param>
|
||||
/// <param name="configure">
|
||||
/// Делегат для настройки менеджера перед инициализацией.
|
||||
/// Делегат для настройки параметров менеджера перед инициализацией.
|
||||
/// Передает экземпляр <see cref="WinUIDragDropManager"/> для конфигурации.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// Инициализированный экземпляр <see cref="WinUIDragDropManager"/>.
|
||||
/// </returns>
|
||||
/// <exception cref="ArgumentNullException">
|
||||
/// Выбрасывается, когда <paramref name="window"/> равен null.
|
||||
/// </exception>
|
||||
/// <remarks>
|
||||
/// Этот метод позволяет настроить параметры менеджера (например, смещение визуального элемента)
|
||||
/// перед его инициализацией.
|
||||
/// <para>
|
||||
/// Этот метод позволяет настроить параметры менеджера перед его инициализацией,
|
||||
/// что полезно для тонкой настройки поведения системы перетаскивания.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// Доступные для настройки параметры включают:
|
||||
/// <list type="bullet">
|
||||
/// <item><see cref="WinUIDragDropManager.DragVisualOffset"/> - смещение визуального элемента</item>
|
||||
/// </list>
|
||||
/// </para>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// var manager = WinUIDragDropFactory.CreateManager(window, m =>
|
||||
/// {
|
||||
/// m.DragVisualOffset = new Point(-15, -15); // Ближе к курсору
|
||||
/// });
|
||||
/// </code>
|
||||
/// </example>
|
||||
/// </remarks>
|
||||
public static WinUIDragDropManager CreateManager(Window window, Action<WinUIDragDropManager> configure)
|
||||
{
|
||||
@@ -82,13 +144,20 @@ public static class WinUIDragDropFactory
|
||||
throw new ArgumentNullException(nameof(window));
|
||||
|
||||
var manager = WinUIDragDropManager.Instance;
|
||||
|
||||
// Применяем настройки перед инициализацией
|
||||
configure?.Invoke(manager);
|
||||
manager.Initialize(window);
|
||||
|
||||
if (!manager.IsInitialized)
|
||||
{
|
||||
manager.Initialize(window);
|
||||
}
|
||||
|
||||
return manager;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Создает хост для визуальных элементов перетаскивания.
|
||||
/// Создает хост для управления визуальными элементами перетаскивания.
|
||||
/// </summary>
|
||||
/// <param name="window">
|
||||
/// Окно, к которому будет привязан хост.
|
||||
@@ -96,9 +165,26 @@ public static class WinUIDragDropFactory
|
||||
/// <returns>
|
||||
/// Экземпляр <see cref="WinUIDragDropHost"/>, готовый к использованию.
|
||||
/// </returns>
|
||||
/// <exception cref="ArgumentNullException">
|
||||
/// Выбрасывается, если <paramref name="window"/> равен null.
|
||||
/// </exception>
|
||||
/// <remarks>
|
||||
/// Хост управляет отображением визуальных элементов (drag-визуализация, drop-превью)
|
||||
/// на оверлейном слое поверх основного содержимого окна.
|
||||
/// <para>
|
||||
/// Хост управляет отображением визуальных элементов во время операций перетаскивания,
|
||||
/// включая drag-визуализации (элементы, следующие за курсором) и drop-превью
|
||||
/// (подсветка областей сброса).
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// В большинстве случаев хост создается автоматически менеджером перетаскивания.
|
||||
/// Этот метод полезен для продвинутых сценариев, когда требуется прямой контроль
|
||||
/// над визуальной обратной связью.
|
||||
/// </para>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// var host = WinUIDragDropFactory.CreateHost(window);
|
||||
/// // Настройка кастомной визуализации
|
||||
/// </code>
|
||||
/// </example>
|
||||
/// </remarks>
|
||||
public static WinUIDragDropHost CreateHost(Window window)
|
||||
{
|
||||
@@ -130,7 +216,21 @@ public static class WinUIDragDropFactory
|
||||
/// Выбрасывается, если любой из параметров равен null.
|
||||
/// </exception>
|
||||
/// <remarks>
|
||||
/// Созданное поведение необходимо прикрепить к элементу с помощью метода <see cref="WinUIDragSourceBehavior.Attach"/>.
|
||||
/// <para>
|
||||
/// Созданное поведение необходимо прикрепить к элементу с помощью метода
|
||||
/// <see cref="WinUIDragSourceBehavior.Attach"/>.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// Этот метод полезен для продвинутых сценариев, когда требуется создавать поведения
|
||||
/// вручную, например, при динамическом создании элементов интерфейса.
|
||||
/// </para>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// // Создание поведения вручную
|
||||
/// var behavior = WinUIDragDropFactory.CreateDragSourceBehavior(service, host);
|
||||
/// behavior.Attach(element, data);
|
||||
/// </code>
|
||||
/// </example>
|
||||
/// </remarks>
|
||||
public static WinUIDragSourceBehavior CreateDragSourceBehavior(
|
||||
IDragDropService dragDropService,
|
||||
@@ -160,7 +260,24 @@ public static class WinUIDragDropFactory
|
||||
/// Выбрасывается, если любой из параметров равен null.
|
||||
/// </exception>
|
||||
/// <remarks>
|
||||
/// Созданное поведение необходимо прикрепить к элементу с помощью метода <see cref="WinUIDropTargetBehavior.Attach"/>.
|
||||
/// <para>
|
||||
/// Созданное поведение необходимо прикрепить к элементу с помощью метода
|
||||
/// <see cref="WinUIDropTargetBehavior.Attach"/>.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// Поведение можно дополнительно настроить с помощью методов
|
||||
/// <see cref="WinUIDropTargetBehavior.AcceptTypes"/> и
|
||||
/// <see cref="WinUIDropTargetBehavior.AcceptFormats"/> для фильтрации
|
||||
/// принимаемых данных.
|
||||
/// </para>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// // Создание поведения вручную
|
||||
/// var behavior = WinUIDragDropFactory.CreateDropTargetBehavior(service, host);
|
||||
/// behavior.AcceptTypes(typeof(string), typeof(MyModel));
|
||||
/// behavior.Attach(dropArea);
|
||||
/// </code>
|
||||
/// </example>
|
||||
/// </remarks>
|
||||
public static WinUIDropTargetBehavior CreateDropTargetBehavior(
|
||||
IDragDropService dragDropService,
|
||||
@@ -174,6 +291,93 @@ public static class WinUIDragDropFactory
|
||||
return new WinUIDropTargetBehavior(dragDropService, host);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Создает поведение источника перетаскивания, используя сервисы из менеджера по умолчанию.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// Экземпляр <see cref="WinUIDragSourceBehavior"/>, готовый к прикреплению к элементу.
|
||||
/// </returns>
|
||||
/// <exception cref="InvalidOperationException">
|
||||
/// Выбрасывается, если менеджер не инициализирован.
|
||||
/// </exception>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// Этот метод использует <see cref="WinUIDragDropManager.Instance"/> для получения
|
||||
/// сервиса перетаскивания и хоста, что упрощает создание поведений в контексте
|
||||
/// уже инициализированной системы.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// Перед использованием убедитесь, что менеджер инициализирован через метод
|
||||
/// <see cref="CreateManager"/>.
|
||||
/// </para>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// // Инициализация менеджера
|
||||
/// WinUIDragDropFactory.CreateManager(window);
|
||||
///
|
||||
/// // Создание поведения с использованием менеджера
|
||||
/// var behavior = WinUIDragDropFactory.CreateDragSourceBehavior();
|
||||
/// behavior.Attach(element, data);
|
||||
/// </code>
|
||||
/// </example>
|
||||
/// </remarks>
|
||||
public static WinUIDragSourceBehavior CreateDragSourceBehavior()
|
||||
{
|
||||
var manager = WinUIDragDropManager.Instance;
|
||||
if (!manager.IsInitialized)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
"Менеджер не инициализирован. Вызовите CreateManager перед созданием поведений.");
|
||||
}
|
||||
|
||||
return new WinUIDragSourceBehavior(manager.DragDropService, manager.Host);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Создает поведение цели сброса, используя сервисы из менеджера по умолчанию.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// Экземпляр <see cref="WinUIDropTargetBehavior"/>, готовый к прикреплению к элементу.
|
||||
/// </returns>
|
||||
/// <exception cref="InvalidOperationException">
|
||||
/// Выбрасывается, если менеджер не инициализирован.
|
||||
/// </exception>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// Этот метод использует <see cref="WinUIDragDropManager.Instance"/> для получения
|
||||
/// сервиса перетаскивания и хоста, что упрощает создание поведений в контексте
|
||||
/// уже инициализированной системы.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// Поведение можно дополнительно настроить с помощью методов
|
||||
/// <see cref="WinUIDropTargetBehavior.AcceptTypes"/> и
|
||||
/// <see cref="WinUIDropTargetBehavior.AcceptFormats"/> для фильтрации
|
||||
/// принимаемых данных.
|
||||
/// </para>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// // Инициализация менеджера
|
||||
/// WinUIDragDropFactory.CreateManager(window);
|
||||
///
|
||||
/// // Создание поведения с использованием менеджера
|
||||
/// var behavior = WinUIDragDropFactory.CreateDropTargetBehavior();
|
||||
/// behavior.AcceptTypes(typeof(string));
|
||||
/// behavior.Attach(dropArea);
|
||||
/// </code>
|
||||
/// </example>
|
||||
/// </remarks>
|
||||
public static WinUIDropTargetBehavior CreateDropTargetBehavior()
|
||||
{
|
||||
var manager = WinUIDragDropManager.Instance;
|
||||
if (!manager.IsInitialized)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
"Менеджер не инициализирован. Вызовите CreateManager перед созданием поведений.");
|
||||
}
|
||||
|
||||
return new WinUIDropTargetBehavior(manager.DragDropService, manager.Host);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Визуальные элементы
|
||||
@@ -183,23 +387,44 @@ public static class WinUIDragDropFactory
|
||||
/// </summary>
|
||||
/// <param name="dragData">
|
||||
/// Данные, которые будут отображены в визуальном элементе.
|
||||
/// Могут быть любого типа, поддерживаемого системой перетаскивания.
|
||||
/// </param>
|
||||
/// <param name="opacity">
|
||||
/// Прозрачность элемента (от 0.0 до 1.0).
|
||||
/// Прозрачность элемента в диапазоне от 0.0 (полностью прозрачный) до 1.0 (полностью непрозрачный).
|
||||
/// Значение по умолчанию: 0.8.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// Экземпляр <see cref="DragAdorner"/>, настроенный для отображения указанных данных.
|
||||
/// </returns>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// Созданный элемент можно использовать с методом <see cref="WinUIDragDropHost.ShowDragVisual"/>
|
||||
/// для отображения во время операции перетаскивания.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// Элемент автоматически адаптирует отображение в зависимости от типа данных:
|
||||
/// <list type="bullet">
|
||||
/// <item>Для строк отображается текстовое представление</item>
|
||||
/// <item>Для изображений отображается миниатюра</item>
|
||||
/// <item>Для пользовательских объектов используется DataTemplate или ToString()</item>
|
||||
/// </list>
|
||||
/// </para>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// // Создание визуального элемента
|
||||
/// var adorner = WinUIDragDropFactory.CreateDragAdorner("Текст для перетаскивания");
|
||||
///
|
||||
/// // Отображение во время перетаскивания
|
||||
/// host.ShowDragVisual(adorner, position);
|
||||
/// </code>
|
||||
/// </example>
|
||||
/// </remarks>
|
||||
public static DragAdorner CreateDragAdorner(object dragData, double opacity = 0.8)
|
||||
{
|
||||
return new DragAdorner
|
||||
{
|
||||
DragData = dragData,
|
||||
Opacity = opacity
|
||||
DragData = dragData ?? throw new ArgumentNullException(nameof(dragData)),
|
||||
Opacity = Math.Clamp(opacity, 0.0, 1.0)
|
||||
};
|
||||
}
|
||||
|
||||
@@ -207,16 +432,40 @@ public static class WinUIDragDropFactory
|
||||
/// Создает элемент предварительного просмотра для области сброса.
|
||||
/// </summary>
|
||||
/// <param name="color">
|
||||
/// Цвет подсветки области. Если не указан, используется цвет из ресурсов темы.
|
||||
/// Цвет подсветки области. Если не указан, используется системный цвет акцента.
|
||||
/// </param>
|
||||
/// <param name="thickness">
|
||||
/// Толщина границы подсветки.
|
||||
/// Толщина границы подсветки в пикселях.
|
||||
/// Значение по умолчанию: 2.0.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// Экземпляр <see cref="DropPreviewAdorner"/>, готовый к отображению.
|
||||
/// </returns>
|
||||
/// <remarks>
|
||||
/// Этот элемент используется для визуального указания области, на которую можно сбросить данные.
|
||||
/// <para>
|
||||
/// Этот элемент используется для визуального указания области, на которую можно
|
||||
/// сбросить данные. Он отображается как подсветка границ целевого элемента с
|
||||
/// поддержкой анимации появления и скрытия.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// Элемент автоматически адаптирует свой внешний вид в зависимости от состояния:
|
||||
/// <list type="bullet">
|
||||
/// <item><strong>Normal</strong> - стандартное состояние</item>
|
||||
/// <item><strong>Highlighted</strong> - подсветка при наведении</item>
|
||||
/// </list>
|
||||
/// </para>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// // Создание элемента подсветки
|
||||
/// var preview = WinUIDragDropFactory.CreateDropPreviewAdorner(
|
||||
/// Colors.Blue, // Цвет
|
||||
/// 3.0 // Толщина границы
|
||||
/// );
|
||||
///
|
||||
/// // Отображение подсветки
|
||||
/// preview.Show(bounds);
|
||||
/// </code>
|
||||
/// </example>
|
||||
/// </remarks>
|
||||
public static DropPreviewAdorner CreateDropPreviewAdorner(
|
||||
Windows.UI.Color? color = null,
|
||||
@@ -224,7 +473,7 @@ public static class WinUIDragDropFactory
|
||||
{
|
||||
var adorner = new DropPreviewAdorner
|
||||
{
|
||||
PreviewThickness = thickness
|
||||
PreviewThickness = Math.Max(thickness, 0.0)
|
||||
};
|
||||
|
||||
if (color.HasValue)
|
||||
@@ -249,18 +498,35 @@ public static class WinUIDragDropFactory
|
||||
/// Кортеж, содержащий менеджер перетаскивания и сервис перетаскивания.
|
||||
/// </returns>
|
||||
/// <remarks>
|
||||
/// Этот метод создает все необходимые компоненты для работы перетаскивания в приложении.
|
||||
/// Возвращаемый сервис можно использовать для создания дополнительных источников и целей.
|
||||
/// <para>
|
||||
/// Этот метод создает все необходимые компоненты для работы перетаскивания в приложении
|
||||
/// и возвращает их для дальнейшего использования.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// Возвращаемый сервис можно использовать для создания дополнительных источников и целей
|
||||
/// или для низкоуровневого управления операциями перетаскивания.
|
||||
/// </para>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// // Создание полной системы
|
||||
/// var (manager, service) = WinUIDragDropFactory.CreateCompleteSystem(window);
|
||||
///
|
||||
/// // Использование менеджера для настройки элементов
|
||||
/// manager.MakeDragSource(element, data);
|
||||
///
|
||||
/// // Использование сервиса для расширенного управления
|
||||
/// var stats = service.GetStats();
|
||||
/// </code>
|
||||
/// </example>
|
||||
/// </remarks>
|
||||
public static (WinUIDragDropManager Manager, IDragDropService Service) CreateCompleteSystem(Window window)
|
||||
{
|
||||
var service = DragDropFactory.CreateDragDropService();
|
||||
var manager = CreateManager(window);
|
||||
return (manager, service);
|
||||
return (manager, manager.DragDropService);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Создает систему перетаскивания, оптимизированную для списков и коллекций.
|
||||
/// Создает систему перетаскивания, оптимизированную для переупорядочивания элементов в списках.
|
||||
/// </summary>
|
||||
/// <param name="window">
|
||||
/// Главное окно приложения.
|
||||
@@ -269,13 +535,45 @@ public static class WinUIDragDropFactory
|
||||
/// Менеджер перетаскивания, настроенный для переупорядочивания элементов в списках.
|
||||
/// </returns>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// Эта конфигурация устанавливает оптимальные параметры для перетаскивания элементов
|
||||
/// внутри списков (например, для изменения порядка элементов в ListView или ItemsControl).
|
||||
/// внутри списков и коллекций, таких как:
|
||||
/// <list type="bullet">
|
||||
/// <item>Изменение порядка элементов в ListView</item>
|
||||
/// <item>Перемещение элементов между ItemsControl</item>
|
||||
/// <item>Сортировка элементов в коллекциях</item>
|
||||
/// </list>
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// Особенности конфигурации:
|
||||
/// <list type="bullet">
|
||||
/// <item>Уменьшенный порог начала перетаскивания для более быстрого отклика</item>
|
||||
/// <item>Смещение визуального элемента для лучшего визуального выравнивания</item>
|
||||
/// <item>Оптимизированная визуальная обратная связь</item>
|
||||
/// </list>
|
||||
/// </para>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// // Создание системы для переупорядочивания списков
|
||||
/// 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);
|
||||
/// </code>
|
||||
/// </example>
|
||||
/// </remarks>
|
||||
public static WinUIDragDropManager CreateListReorderSystem(Window window)
|
||||
{
|
||||
var manager = CreateManager(window, m =>
|
||||
{
|
||||
// Уменьшенное смещение для лучшего визуального выравнивания в списках
|
||||
m.DragVisualOffset = new Core.Geometry.Point(-15, -15);
|
||||
});
|
||||
|
||||
@@ -283,7 +581,7 @@ public static class WinUIDragDropFactory
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Создает систему перетаскивания для файлов и документов.
|
||||
/// Создает систему перетаскивания для работы с файлами и документами.
|
||||
/// </summary>
|
||||
/// <param name="window">
|
||||
/// Главное окно приложения.
|
||||
@@ -292,13 +590,37 @@ public static class WinUIDragDropFactory
|
||||
/// Менеджер перетаскивания, настроенный для работы с файлами.
|
||||
/// </returns>
|
||||
/// <remarks>
|
||||
/// Эта конфигурация устанавливает увеличенный порог перетаскивания и специальные
|
||||
/// визуальные эффекты, характерные для операций с файлами.
|
||||
/// <para>
|
||||
/// Эта конфигурация оптимизирована для сценариев работы с файлами:
|
||||
/// <list type="bullet">
|
||||
/// <item>Перетаскивание файлов из проводника в приложение</item>
|
||||
/// <item>Перемещение файлов между элементами интерфейса</item>
|
||||
/// <item>Работа с большими объемами данных</item>
|
||||
/// </list>
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// Особенности конфигурации:
|
||||
/// <list type="bullet">
|
||||
/// <item>Увеличенный порог перетаскивания для предотвращения случайных операций</item>
|
||||
/// <item>Специальные визуальные эффекты, характерные для файловых операций</item>
|
||||
/// <item>Оптимизация для работы с внешними источниками данных</item>
|
||||
/// </list>
|
||||
/// </para>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// // Создание системы для работы с файлами
|
||||
/// var manager = WinUIDragDropFactory.CreateFileDragDropSystem(window);
|
||||
///
|
||||
/// // Настройка области для приема файлов
|
||||
/// manager.MakeDropTarget(fileDropArea);
|
||||
/// </code>
|
||||
/// </example>
|
||||
/// </remarks>
|
||||
public static WinUIDragDropManager CreateFileDragDropSystem(Window window)
|
||||
{
|
||||
var manager = CreateManager(window, m =>
|
||||
{
|
||||
// Увеличенное смещение для файлов (имитация "переноса" файла)
|
||||
m.DragVisualOffset = new Core.Geometry.Point(-25, -25);
|
||||
});
|
||||
|
||||
@@ -315,13 +637,39 @@ public static class WinUIDragDropFactory
|
||||
/// Менеджер перетаскивания, настроенный для точного позиционирования.
|
||||
/// </returns>
|
||||
/// <remarks>
|
||||
/// Эта конфигурация уменьшает порог начала перетаскивания для более точного контроля
|
||||
/// и устанавливает минималистичную визуализацию.
|
||||
/// <para>
|
||||
/// Эта конфигурация оптимизирована для приложений, требующих высокой точности
|
||||
/// позиционирования, таких как:
|
||||
/// <list type="bullet">
|
||||
/// <item>Графические редакторы (Photoshop, Figma)</item>
|
||||
/// <item>Инструменты проектирования интерфейсов</item>
|
||||
/// <item>CAD-системы и приложения для 3D-моделирования</item>
|
||||
/// </list>
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// Особенности конфигурации:
|
||||
/// <list type="bullet">
|
||||
/// <item>Минимальный порог начала перетаскивания для максимальной точности</item>
|
||||
/// <item>Минималистичная визуализация для уменьшения визуального шума</item>
|
||||
/// <item>Оптимизация для работы с высокоточными устройствами ввода (графические планшеты)</item>
|
||||
/// </list>
|
||||
/// </para>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// // Создание системы для графического редактора
|
||||
/// var manager = WinUIDragDropFactory.CreateDesignToolSystem(window);
|
||||
///
|
||||
/// // Настройка инструментов для перетаскивания
|
||||
/// manager.MakeDragSource(toolIcon, toolData);
|
||||
/// manager.MakeDropTarget(canvas);
|
||||
/// </code>
|
||||
/// </example>
|
||||
/// </remarks>
|
||||
public static WinUIDragDropManager CreateDesignToolSystem(Window window)
|
||||
{
|
||||
var manager = CreateManager(window, m =>
|
||||
{
|
||||
// Минимальное смещение для точного позиционирования
|
||||
m.DragVisualOffset = new Core.Geometry.Point(-10, -10);
|
||||
});
|
||||
|
||||
@@ -344,11 +692,25 @@ public static class WinUIDragDropFactory
|
||||
/// <param name="dragData">
|
||||
/// Данные для перетаскивания. Если не указаны, будут использованы DataContext или Tag элемента.
|
||||
/// </param>
|
||||
/// <exception cref="ArgumentNullException">
|
||||
/// Выбрасывается, если <paramref name="manager"/> или <paramref name="element"/> равны null.
|
||||
/// </exception>
|
||||
/// <remarks>
|
||||
/// Этот метод является оберткой вокруг <see cref="WinUIDragDropManager.MakeDragSource"/>,
|
||||
/// предоставляя более удобный API.
|
||||
/// <para>
|
||||
/// Этот метод является удобной оберткой вокруг <see cref="WinUIDragDropManager.MakeDragSource"/>,
|
||||
/// предоставляющей более лаконичный синтаксис.
|
||||
/// </para>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// // Использование вспомогательного метода
|
||||
/// WinUIDragDropFactory.SetupAsDragSource(manager, myElement, myData);
|
||||
///
|
||||
/// // Эквивалентно:
|
||||
/// manager.MakeDragSource(myElement, myData);
|
||||
/// </code>
|
||||
/// </example>
|
||||
/// </remarks>
|
||||
public static void SetupAsDragSource(WinUIDragDropManager manager, Microsoft.UI.Xaml.FrameworkElement element, object dragData = null)
|
||||
public static void SetupAsDragSource(WinUIDragDropManager manager, FrameworkElement element, object dragData = null)
|
||||
{
|
||||
if (manager == null)
|
||||
throw new ArgumentNullException(nameof(manager));
|
||||
@@ -367,11 +729,25 @@ public static class WinUIDragDropFactory
|
||||
/// <param name="element">
|
||||
/// Элемент WinUI, который должен стать целью сброса.
|
||||
/// </param>
|
||||
/// <exception cref="ArgumentNullException">
|
||||
/// Выбрасывается, если <paramref name="manager"/> или <paramref name="element"/> равны null.
|
||||
/// </exception>
|
||||
/// <remarks>
|
||||
/// Этот метод является оберткой вокруг <see cref="WinUIDragDropManager.MakeDropTarget"/>,
|
||||
/// предоставляя более удобный API.
|
||||
/// <para>
|
||||
/// Этот метод является удобной оберткой вокруг <see cref="WinUIDragDropManager.MakeDropTarget"/>,
|
||||
/// предоставляющей более лаконичный синтаксис.
|
||||
/// </para>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// // Использование вспомогательного метода
|
||||
/// WinUIDragDropFactory.SetupAsDropTarget(manager, myDropArea);
|
||||
///
|
||||
/// // Эквивалентно:
|
||||
/// manager.MakeDropTarget(myDropArea);
|
||||
/// </code>
|
||||
/// </example>
|
||||
/// </remarks>
|
||||
public static void SetupAsDropTarget(WinUIDragDropManager manager, Microsoft.UI.Xaml.FrameworkElement element)
|
||||
public static void SetupAsDropTarget(WinUIDragDropManager manager, FrameworkElement element)
|
||||
{
|
||||
if (manager == null)
|
||||
throw new ArgumentNullException(nameof(manager));
|
||||
@@ -388,20 +764,43 @@ public static class WinUIDragDropFactory
|
||||
/// Менеджер перетаскивания.
|
||||
/// </param>
|
||||
/// <param name="container">
|
||||
/// Контейнер (например, StackPanel или Grid), дочерние элементы которого можно переупорядочивать.
|
||||
/// Контейнер (например, StackPanel, Grid или ItemsControl), дочерние элементы которого можно переупорядочивать.
|
||||
/// </param>
|
||||
/// <param name="childSelector">
|
||||
/// Функция для получения данных перетаскивания из дочернего элемента.
|
||||
/// Если не указана, используются DataContext дочерних элементов.
|
||||
/// </param>
|
||||
/// <exception cref="ArgumentNullException">
|
||||
/// Выбрасывается, если <paramref name="manager"/> или <paramref name="container"/> равны null.
|
||||
/// </exception>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// Этот метод автоматически настраивает все дочерние элементы контейнера как источники перетаскивания,
|
||||
/// а сам контейнер — как цель сброса, создавая функциональность переупорядочивания.
|
||||
/// а сам контейнер — как цель сброса, создавая функциональность переупорядочивания элементов.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// Поддерживаемые типы контейнеров:
|
||||
/// <list type="bullet">
|
||||
/// <item><see cref="Panel"/> и его производные (StackPanel, Grid, Canvas)</item>
|
||||
/// <item><see cref="ItemsControl"/> и его производные (ListView, ListBox)</item>
|
||||
/// <item>Любые другие контейнеры с коллекцией дочерних элементов</item>
|
||||
/// </list>
|
||||
/// </para>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// // Настройка StackPanel для переупорядочивания дочерних элементов
|
||||
/// WinUIDragDropFactory.SetupReorderContainer(manager, myStackPanel);
|
||||
///
|
||||
/// // С кастомным селектором данных
|
||||
/// WinUIDragDropFactory.SetupReorderContainer(manager, myListView,
|
||||
/// element => ((FrameworkElement)element).DataContext);
|
||||
/// </code>
|
||||
/// </example>
|
||||
/// </remarks>
|
||||
public static void SetupReorderContainer(
|
||||
WinUIDragDropManager manager,
|
||||
Microsoft.UI.Xaml.Controls.Panel container,
|
||||
Func<Microsoft.UI.Xaml.FrameworkElement, object> childSelector = null)
|
||||
FrameworkElement container,
|
||||
Func<FrameworkElement, object> childSelector = null)
|
||||
{
|
||||
if (manager == null)
|
||||
throw new ArgumentNullException(nameof(manager));
|
||||
@@ -411,10 +810,50 @@ public static class WinUIDragDropFactory
|
||||
// Настраиваем контейнер как цель сброса
|
||||
manager.MakeDropTarget(container);
|
||||
|
||||
// Настраиваем все дочерние элементы как источники перетаскивания
|
||||
foreach (var child in container.Children)
|
||||
// Настраиваем дочерние элементы как источники перетаскивания
|
||||
if (container is Panel panel)
|
||||
{
|
||||
if (child is Microsoft.UI.Xaml.FrameworkElement element)
|
||||
SetupPanelChildren(manager, panel, childSelector);
|
||||
}
|
||||
else if (container is ItemsControl itemsControl)
|
||||
{
|
||||
SetupItemsControlChildren(manager, itemsControl, childSelector);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Настраивает дочерние элементы Panel как источники перетаскивания.
|
||||
/// </summary>
|
||||
private static void SetupPanelChildren(
|
||||
WinUIDragDropManager manager,
|
||||
Panel panel,
|
||||
Func<FrameworkElement, object> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Настраивает элементы ItemsControl как источники перетаскивания.
|
||||
/// </summary>
|
||||
private static void SetupItemsControlChildren(
|
||||
WinUIDragDropManager manager,
|
||||
ItemsControl itemsControl,
|
||||
Func<FrameworkElement, object> 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);
|
||||
@@ -427,7 +866,7 @@ public static class WinUIDragDropFactory
|
||||
#region Методы для работы с XAML
|
||||
|
||||
/// <summary>
|
||||
/// Настраивает прикрепленные свойства для элемента источника перетаскивания.
|
||||
/// Настраивает attached properties для элемента источника перетаскивания.
|
||||
/// </summary>
|
||||
/// <param name="element">
|
||||
/// Элемент, который должен стать источником перетаскивания.
|
||||
@@ -435,39 +874,76 @@ public static class WinUIDragDropFactory
|
||||
/// <param name="dragData">
|
||||
/// Данные для перетаскивания.
|
||||
/// </param>
|
||||
/// <exception cref="ArgumentNullException">
|
||||
/// Выбрасывается, если <paramref name="element"/> равен null.
|
||||
/// </exception>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// Этот метод устанавливает attached properties, которые активируют поведение перетаскивания
|
||||
/// при использовании в XAML. Эквивалентно установке свойств IsDragSource и DragData в XAML.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// Метод полезен для динамической настройки элементов в коде C# при сохранении
|
||||
/// декларативного стиля программирования.
|
||||
/// </para>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// // Настройка элемента в коде C#
|
||||
/// WinUIDragDropFactory.SetupDragSourceInXaml(myElement, myData);
|
||||
///
|
||||
/// // Эквивалентно в XAML:
|
||||
/// <Border local:DragDropProperties.IsDragSource="True"
|
||||
/// local:DragDropProperties.DragData="{Binding MyData}" />
|
||||
/// </code>
|
||||
/// </example>
|
||||
/// </remarks>
|
||||
public static void SetupDragSourceInXaml(Microsoft.UI.Xaml.FrameworkElement element, object dragData)
|
||||
public static void SetupDragSourceInXaml(FrameworkElement element, object dragData)
|
||||
{
|
||||
if (element == null)
|
||||
throw new ArgumentNullException(nameof(element));
|
||||
|
||||
// Устанавливаем attached properties
|
||||
element.SetValue(Behaviors.WinUIDragSourceBehavior.IsEnabledProperty, true);
|
||||
element.SetValue(DragDropProperties.IsDragSourceProperty, true);
|
||||
if (dragData != null)
|
||||
{
|
||||
element.SetValue(Behaviors.WinUIDragSourceBehavior.DragDataProperty, dragData);
|
||||
element.SetValue(DragDropProperties.DragDataProperty, dragData);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Настраивает прикрепленные свойства для элемента цели сброса.
|
||||
/// Настраивает attached properties для элемента цели сброса.
|
||||
/// </summary>
|
||||
/// <param name="element">
|
||||
/// Элемент, который должен стать целью сброса.
|
||||
/// </param>
|
||||
/// <exception cref="ArgumentNullException">
|
||||
/// Выбрасывается, если <paramref name="element"/> равен null.
|
||||
/// </exception>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// Этот метод устанавливает attached properties, которые активируют поведение цели сброса
|
||||
/// при использовании в XAML. Эквивалентно установке свойства IsDropTarget в XAML.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// Метод полезен для динамической настройки элементов в коде C# при сохранении
|
||||
/// декларативного стиля программирования.
|
||||
/// </para>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// // Настройка элемента в коде C#
|
||||
/// WinUIDragDropFactory.SetupDropTargetInXaml(myDropArea);
|
||||
///
|
||||
/// // Эквивалентно в XAML:
|
||||
/// <Border local:DragDropProperties.IsDropTarget="True" />
|
||||
/// </code>
|
||||
/// </example>
|
||||
/// </remarks>
|
||||
public static void SetupDropTargetInXaml(Microsoft.UI.Xaml.FrameworkElement element)
|
||||
public static void SetupDropTargetInXaml(FrameworkElement element)
|
||||
{
|
||||
if (element == null)
|
||||
throw new ArgumentNullException(nameof(element));
|
||||
|
||||
element.SetValue(Behaviors.WinUIDropTargetBehavior.IsEnabledProperty, true);
|
||||
element.SetValue(DragDropProperties.IsDropTargetProperty, true);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
89
Lattice.UI.DragDrop.WinUI/Helpers/WinUIWindowHelper.cs
Normal file
89
Lattice.UI.DragDrop.WinUI/Helpers/WinUIWindowHelper.cs
Normal file
@@ -0,0 +1,89 @@
|
||||
using Lattice.Core.Geometry;
|
||||
using Microsoft.UI.Xaml;
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
/// <summary>
|
||||
/// Вспомогательный класс для получения экранных координат в WinUI 3.
|
||||
/// Использует P/Invoke для доступа к нативным API Windows.
|
||||
/// </summary>
|
||||
internal static class WinUIWindowHelper
|
||||
{
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct POINT
|
||||
{
|
||||
public int X;
|
||||
public int Y;
|
||||
|
||||
public POINT(int x, int y)
|
||||
{
|
||||
X = x;
|
||||
Y = y;
|
||||
}
|
||||
}
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
public static extern bool ClientToScreen(IntPtr hWnd, ref POINT lpPoint);
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
public static extern IntPtr WindowFromPoint(POINT point);
|
||||
|
||||
[DllImport("user32.dll", SetLastError = true)]
|
||||
public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
|
||||
|
||||
/// <summary>
|
||||
/// Преобразует координаты элемента в экранные координаты.
|
||||
/// </summary>
|
||||
public static Point ConvertToScreenCoordinates(FrameworkElement element, Point localPoint)
|
||||
{
|
||||
if (element == null || !element.IsLoaded)
|
||||
return localPoint;
|
||||
|
||||
try
|
||||
{
|
||||
var window = Window.Current;
|
||||
if (window == null)
|
||||
return localPoint;
|
||||
|
||||
// Получаем хэндл окна
|
||||
var hwnd = GetWindowHandle(window);
|
||||
if (hwnd == IntPtr.Zero)
|
||||
return localPoint;
|
||||
|
||||
// Преобразуем координаты элемента в координаты окна
|
||||
var transform = element.TransformToVisual(window.Content);
|
||||
var windowPoint = transform.TransformPoint(
|
||||
new Windows.Foundation.Point(localPoint.X, localPoint.Y));
|
||||
|
||||
// Преобразуем в POINT для P/Invoke
|
||||
var point = new POINT(
|
||||
(int)Math.Round(windowPoint.X),
|
||||
(int)Math.Round(windowPoint.Y));
|
||||
|
||||
// Преобразуем клиентские координаты в экранные
|
||||
if (ClientToScreen(hwnd, ref point))
|
||||
{
|
||||
return new Point(point.X, point.Y);
|
||||
}
|
||||
|
||||
return localPoint;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine($"Ошибка преобразования координат: {ex.Message}");
|
||||
return localPoint;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Получает хэндл окна WinUI.
|
||||
/// </summary>
|
||||
private static IntPtr GetWindowHandle(Window window)
|
||||
{
|
||||
// В WinUI 3 можно использовать WinRT API для получения хэндла
|
||||
// или альтернативные методы в зависимости от контекста
|
||||
var hwnd = WinRT.Interop.WindowNative.GetWindowHandle(window);
|
||||
return hwnd;
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
using Lattice.Core.DragDrop.Services;
|
||||
using Lattice.Core.Geometry;
|
||||
using Lattice.UI.DragDrop.WinUI.Behaviors;
|
||||
using Lattice.UI.DragDrop.WinUI.Controls;
|
||||
using Microsoft.UI.Xaml;
|
||||
using System;
|
||||
@@ -9,41 +10,87 @@ namespace Lattice.UI.DragDrop.WinUI.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Центральный менеджер для управления операциями drag-and-drop в WinUI приложении.
|
||||
/// Координирует работу источников и целей перетаскивания, управляет визуальной обратной связью
|
||||
/// и обеспечивает согласованное взаимодействие всех компонентов системы.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// Этот класс реализует шаблон Singleton и предоставляет единую точку для
|
||||
/// настройки и управления всеми операциями перетаскивания в приложении.
|
||||
/// <see cref="WinUIDragDropManager"/> реализует шаблон Singleton и служит единой точкой
|
||||
/// входа для настройки и управления операциями перетаскивания в WinUI-приложении.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// Менеджер отвечает за:
|
||||
/// - Инициализацию системы перетаскивания
|
||||
/// - Регистрацию и отслеживание источников и целей перетаскивания
|
||||
/// - Создание и управление визуальной обратной связью
|
||||
/// - Координацию между поведением элементов и базовым сервисом перетаскивания
|
||||
/// Основные функции менеджера:
|
||||
/// <list type="bullet">
|
||||
/// <item>Инициализация системы перетаскивания для конкретного окна</item>
|
||||
/// <item>Регистрация и отслеживание источников и целей перетаскивания</item>
|
||||
/// <item>Управление жизненным циклом операций перетаскивания</item>
|
||||
/// <item>Обеспечение визуальной обратной связи через <see cref="WinUIDragDropHost"/></item>
|
||||
/// <item>Координация взаимодействия между <see cref="WinUIDragSourceBehavior"/> и <see cref="WinUIDropTargetBehavior"/></item>
|
||||
/// </list>
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// Для использования необходимо вызвать <see cref="Initialize"/> при запуске приложения
|
||||
/// и использовать attached properties или методы расширения для настройки элементов.
|
||||
/// Для использования менеджера необходимо:
|
||||
/// <list type="number">
|
||||
/// <item>Вызвать <see cref="Initialize"/> при создании главного окна приложения</item>
|
||||
/// <item>Настроить элементы как источники или цели через методы <see cref="MakeDragSource"/> и <see cref="MakeDropTarget"/></item>
|
||||
/// <item>Использовать attached properties для декларативной настройки в XAML</item>
|
||||
/// </list>
|
||||
/// </para>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// // Инициализация в коде
|
||||
/// public partial class MainWindow : Window
|
||||
/// {
|
||||
/// private WinUIDragDropManager _manager;
|
||||
///
|
||||
/// public MainWindow()
|
||||
/// {
|
||||
/// InitializeComponent();
|
||||
/// _manager = WinUIDragDropManager.Instance;
|
||||
/// _manager.Initialize(this);
|
||||
///
|
||||
/// // Настройка элементов
|
||||
/// _manager.MakeDragSource(myElement, myData);
|
||||
/// _manager.MakeDropTarget(myDropArea);
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// // Или через attached properties в XAML
|
||||
/// <Border x:Name="DragElement"
|
||||
/// local:DragDropProperties.IsDragSource="True"
|
||||
/// local:DragDropProperties.DragData="{Binding MyData}" />
|
||||
/// <Border x:Name="DropArea"
|
||||
/// local:DragDropProperties.IsDropTarget="True" />
|
||||
/// </code>
|
||||
/// </example>
|
||||
/// </remarks>
|
||||
public sealed class WinUIDragDropManager : IDisposable
|
||||
{
|
||||
#region Singleton
|
||||
#region Singleton Implementation
|
||||
|
||||
private static WinUIDragDropManager? _instance;
|
||||
private static readonly object _lock = new();
|
||||
private static readonly object _lockObject = new();
|
||||
|
||||
/// <summary>
|
||||
/// Получает единственный экземпляр менеджера.
|
||||
/// Получает единственный экземпляр <see cref="WinUIDragDropManager"/>.
|
||||
/// Реализует шаблон Singleton с ленивой инициализацией и потокобезопасностью.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// Единственный экземпляр менеджера перетаскивания для всего приложения.
|
||||
/// Если экземпляр еще не создан, он будет инициализирован при первом обращении.
|
||||
/// </value>
|
||||
/// <remarks>
|
||||
/// Использование Singleton гарантирует, что во всем приложении существует только один
|
||||
/// экземпляр менеджера, что обеспечивает согласованное управление всеми операциями
|
||||
/// перетаскивания и предотвращает конфликты между разными компонентами системы.
|
||||
/// </remarks>
|
||||
public static WinUIDragDropManager Instance
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_instance == null)
|
||||
{
|
||||
lock (_lock)
|
||||
lock (_lockObject)
|
||||
{
|
||||
_instance ??= new WinUIDragDropManager();
|
||||
}
|
||||
@@ -54,37 +101,107 @@ public sealed class WinUIDragDropManager : IDisposable
|
||||
|
||||
#endregion
|
||||
|
||||
#region Поля
|
||||
#region Fields
|
||||
|
||||
private readonly DragDropService _dragDropService;
|
||||
private readonly IDragDropService _dragDropService;
|
||||
private readonly WinUIDragDropHost _host;
|
||||
private readonly Dictionary<FrameworkElement, Behaviors.WinUIDragSourceBehavior> _dragSources = new();
|
||||
private readonly Dictionary<FrameworkElement, Behaviors.WinUIDropTargetBehavior> _dropTargets = new();
|
||||
private readonly Dictionary<FrameworkElement, WinUIDragSourceBehavior> _dragSources = new();
|
||||
private readonly Dictionary<FrameworkElement, WinUIDropTargetBehavior> _dropTargets = new();
|
||||
private DragAdorner? _currentDragVisual;
|
||||
private bool _disposed;
|
||||
private bool _initialized;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Свойства
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// Получает основной сервис перетаскивания.
|
||||
/// Получает сервис перетаскивания, используемый менеджером для координации операций.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// Экземпляр <see cref="IDragDropService"/>, через который менеджер взаимодействует
|
||||
/// с ядром системы перетаскивания.
|
||||
/// </value>
|
||||
/// <remarks>
|
||||
/// Этот сервис предоставляет низкоуровневый API для управления операциями перетаскивания
|
||||
/// и может использоваться для расширенной настройки системы.
|
||||
/// </remarks>
|
||||
public IDragDropService DragDropService => _dragDropService;
|
||||
|
||||
/// <summary>
|
||||
/// Получает хост для управления визуальными элементами перетаскивания.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// Экземпляр <see cref="WinUIDragDropHost"/>, отвечающий за отображение и позиционирование
|
||||
/// визуальной обратной связи во время операций перетаскивания.
|
||||
/// </value>
|
||||
public WinUIDragDropHost Host => _host;
|
||||
|
||||
/// <summary>
|
||||
/// Получает или задает смещение визуального элемента перетаскивания относительно курсора.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// Точка, определяющая смещение по осям X и Y. Значение по умолчанию: (-20, -20).
|
||||
/// Отрицательные значения поднимают визуальный элемент вверх и влево относительно курсора.
|
||||
/// Точка, определяющая смещение по осям X и Y в пикселях.
|
||||
/// Значение по умолчанию: (-20, -20).
|
||||
/// </value>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// Отрицательные значения смещают визуальный элемент вверх и влево относительно курсора,
|
||||
/// что является стандартным поведением в большинстве систем drag-and-drop.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// Настройка смещения позволяет:
|
||||
/// <list type="bullet">
|
||||
/// <item>Предотвратить перекрытие курсора визуальным элементом</item>
|
||||
/// <item>Обеспечить лучшую видимость области под курсором</item>
|
||||
/// <item>Создать более естественное визуальное восприятие</item>
|
||||
/// </list>
|
||||
/// </para>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// // Настройка смещения через фабрику
|
||||
/// var manager = WinUIDragDropFactory.CreateManager(window, m =>
|
||||
/// {
|
||||
/// m.DragVisualOffset = new Point(-15, -15); // Более близко к курсору
|
||||
/// });
|
||||
/// </code>
|
||||
/// </example>
|
||||
/// </remarks>
|
||||
public Point DragVisualOffset { get; set; } = new Point(-20, -20);
|
||||
|
||||
/// <summary>
|
||||
/// Получает значение, указывающее, инициализирован ли менеджер.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// true, если метод <see cref="Initialize"/> был успешно вызван;
|
||||
/// в противном случае — false.
|
||||
/// </value>
|
||||
/// <remarks>
|
||||
/// Проверка этого свойства позволяет избежать повторной инициализации менеджера
|
||||
/// и гарантирует, что система перетаскивания готова к использованию.
|
||||
/// </remarks>
|
||||
public bool IsInitialized => _initialized;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Конструктор
|
||||
#region Constructor
|
||||
|
||||
/// <summary>
|
||||
/// Инициализирует новый экземпляр класса <see cref="WinUIDragDropManager"/>.
|
||||
/// Конструктор является приватным в соответствии с шаблоном Singleton.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// Внутренний конструктор создает:
|
||||
/// <list type="bullet">
|
||||
/// <item>Экземпляр <see cref="DragDropService"/> для управления операциями перетаскивания</item>
|
||||
/// <item>Экземпляр <see cref="WinUIDragDropHost"/> для визуальной обратной связи</item>
|
||||
/// </list>
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// Для получения экземпляра менеджера используйте свойство <see cref="Instance"/>.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
private WinUIDragDropManager()
|
||||
{
|
||||
_dragDropService = new DragDropService();
|
||||
@@ -93,71 +210,196 @@ public sealed class WinUIDragDropManager : IDisposable
|
||||
|
||||
#endregion
|
||||
|
||||
#region Публичные методы
|
||||
#region Public Methods
|
||||
|
||||
/// <summary>
|
||||
/// Инициализирует систему перетаскивания для указанного окна.
|
||||
/// Инициализирует систему перетаскивания для указанного окна WinUI.
|
||||
/// Этот метод должен быть вызван один раз при запуске приложения.
|
||||
/// </summary>
|
||||
/// <param name="window">Основное окно приложения, в котором будет работать перетаскивание.</param>
|
||||
/// <exception cref="ObjectDisposedException">
|
||||
/// Выбрасывается, если менеджер был удален.
|
||||
/// <param name="window">
|
||||
/// Главное окно приложения, для которого настраивается система перетаскивания.
|
||||
/// Не может быть null.
|
||||
/// </param>
|
||||
/// <exception cref="ArgumentNullException">
|
||||
/// Выбрасывается, если <paramref name="window"/> равен null.
|
||||
/// </exception>
|
||||
/// <exception cref="InvalidOperationException">
|
||||
/// Выбрасывается, если менеджер уже инициализирован или был удален.
|
||||
/// </exception>
|
||||
/// <remarks>
|
||||
/// Этот метод должен быть вызван один раз при запуске приложения, обычно в методе
|
||||
/// <see cref="Application.OnLaunched"/>.
|
||||
/// <para>
|
||||
/// Этот метод выполняет следующие действия:
|
||||
/// <list type="bullet">
|
||||
/// <item>Настраивает хост визуальных элементов для работы с указанным окном</item>
|
||||
/// <item>Подписывается на события сервиса перетаскивания для управления визуальной обратной связью</item>
|
||||
/// <item>Помечает менеджер как инициализированный</item>
|
||||
/// </list>
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// Метод должен быть вызван до использования любых других методов менеджера.
|
||||
/// Рекомендуется вызывать его в конструкторе главного окна приложения.
|
||||
/// </para>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// public MainWindow()
|
||||
/// {
|
||||
/// InitializeComponent();
|
||||
/// WinUIDragDropManager.Instance.Initialize(this);
|
||||
/// }
|
||||
/// </code>
|
||||
/// </example>
|
||||
/// </remarks>
|
||||
public void Initialize(Window window)
|
||||
{
|
||||
if (_disposed) throw new ObjectDisposedException(nameof(WinUIDragDropManager));
|
||||
if (_disposed)
|
||||
throw new ObjectDisposedException(nameof(WinUIDragDropManager));
|
||||
|
||||
if (_initialized)
|
||||
throw new InvalidOperationException("Менеджер уже инициализирован.");
|
||||
|
||||
if (window == null)
|
||||
throw new ArgumentNullException(nameof(window));
|
||||
|
||||
// Инициализируем хост для работы с окном
|
||||
_host.Initialize(window);
|
||||
|
||||
// Подписываемся на события
|
||||
// Подписываемся на события сервиса перетаскивания
|
||||
_dragDropService.DragStarted += OnDragStarted;
|
||||
_dragDropService.DragUpdated += OnDragUpdated;
|
||||
_dragDropService.DragCompleted += OnDragCompleted;
|
||||
_dragDropService.DragCancelled += OnDragCancelled;
|
||||
|
||||
_initialized = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Делает указанный элемент источником перетаскивания.
|
||||
/// Настраивает указанный элемент как источник перетаскивания.
|
||||
/// </summary>
|
||||
/// <param name="element">Элемент, который станет источником перетаскивания.</param>
|
||||
/// <param name="dragData">Данные, которые будут перетаскиваться. Если не указано, используются
|
||||
/// DataContext или Tag элемента.</param>
|
||||
/// <param name="element">
|
||||
/// Элемент <see cref="FrameworkElement"/ который должен стать источником перетаскивания.
|
||||
/// Не может быть null.
|
||||
/// </param>
|
||||
/// <param name="dragData">
|
||||
/// Данные, которые будут перетаскиваться. Может быть null.
|
||||
/// Если не указано, используются <see cref="FrameworkElement.DataContext"/> или
|
||||
/// <see cref="FrameworkElement.Tag"/> элемента.
|
||||
/// </param>
|
||||
/// <exception cref="ArgumentNullException">
|
||||
/// Выбрасывается, если <paramref name="element"/> равен null.
|
||||
/// </exception>
|
||||
/// <exception cref="InvalidOperationException">
|
||||
/// Выбрасывается, если менеджер не инициализирован или был удален.
|
||||
/// </exception>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// После вызова этого метода элемент приобретает следующие возможности:
|
||||
/// <list type="bullet">
|
||||
/// <item>Реагирует на жесты перетаскивания (удержание и перемещение указателя)</item>
|
||||
/// <item>Предоставляет указанные данные для перетаскивания</item>
|
||||
/// <item>Интегрируется с системой визуальной обратной связи</item>
|
||||
/// </list>
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// Если элемент уже зарегистрирован как источник перетаскивания, метод не выполняет действий.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// Для отмены регистрации используйте метод <see cref="RemoveDragSource"/>.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public void MakeDragSource(FrameworkElement element, object? dragData = null)
|
||||
{
|
||||
if (_disposed || _dragSources.ContainsKey(element)) return;
|
||||
ValidateManagerState();
|
||||
|
||||
var behavior = new Behaviors.WinUIDragSourceBehavior(_dragDropService, _host);
|
||||
if (element == null)
|
||||
throw new ArgumentNullException(nameof(element));
|
||||
|
||||
// Если элемент уже зарегистрирован, ничего не делаем
|
||||
if (_dragSources.ContainsKey(element))
|
||||
return;
|
||||
|
||||
// Создаем и настраиваем поведение
|
||||
var behavior = new WinUIDragSourceBehavior(_dragDropService, _host);
|
||||
behavior.Attach(element, dragData);
|
||||
_dragSources[element] = behavior;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Делает указанный элемент целью сброса.
|
||||
/// Настраивает указанный элемент как цель сброса.
|
||||
/// </summary>
|
||||
/// <param name="element">Элемент, который станет целью сброса.</param>
|
||||
/// <param name="element">
|
||||
/// Элемент <see cref="FrameworkElement"/>, который должен стать целью сброса.
|
||||
/// Не может быть null.
|
||||
/// </param>
|
||||
/// <exception cref="ArgumentNullException">
|
||||
/// Выбрасывается, если <paramref name="element"/> равен null.
|
||||
/// </exception>
|
||||
/// <exception cref="InvalidOperationException">
|
||||
/// Выбрасывается, если менеджер не инициализирован или был удален.
|
||||
/// </exception>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// После вызова этого метода элемент приобретает следующие возможности:
|
||||
/// <list type="bullet">
|
||||
/// <item>Принимает данные, сбрасываемые пользователем</item>
|
||||
/// <item>Предоставляет визуальную обратную связь при наведении</item>
|
||||
/// <item>Автоматически обновляет свои границы при изменении размера или позиции</item>
|
||||
/// </list>
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// По умолчанию цель принимает данные любого типа. Для настройки фильтрации типов
|
||||
/// используйте методы <see cref="WinUIDropTargetBehavior.AcceptTypes"/> и
|
||||
/// <see cref="WinUIDropTargetBehavior.AcceptFormats"/>.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// Если элемент уже зарегистрирован как цель сброса, метод не выполняет действий.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// Для отмены регистрации используйте метод <see cref="RemoveDropTarget"/>.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public void MakeDropTarget(FrameworkElement element)
|
||||
{
|
||||
if (_disposed || _dropTargets.ContainsKey(element)) return;
|
||||
ValidateManagerState();
|
||||
|
||||
var behavior = new Behaviors.WinUIDropTargetBehavior(_dragDropService, _host);
|
||||
if (element == null)
|
||||
throw new ArgumentNullException(nameof(element));
|
||||
|
||||
// Если элемент уже зарегистрирован, ничего не делаем
|
||||
if (_dropTargets.ContainsKey(element))
|
||||
return;
|
||||
|
||||
// Создаем и настраиваем поведение
|
||||
var behavior = new WinUIDropTargetBehavior(_dragDropService, _host);
|
||||
behavior.Attach(element);
|
||||
_dropTargets[element] = behavior;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Удаляет возможность перетаскивания.
|
||||
/// Удаляет возможность перетаскивания у указанного элемента.
|
||||
/// </summary>
|
||||
/// <param name="element">
|
||||
/// Элемент, у которого нужно отключить возможность перетаскивания.
|
||||
/// Если элемент не зарегистрирован как источник перетаскивания, метод не выполняет действий.
|
||||
/// </param>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// Этот метод выполняет следующие действия:
|
||||
/// <list type="bullet">
|
||||
/// <item>Открепляет поведение перетаскивания от элемента</item>
|
||||
/// <item>Отписывается от всех событий элемента</item>
|
||||
/// <item>Удаляет элемент из внутреннего словаря источников</item>
|
||||
/// <item>Освобождает ресурсы, связанные с поведением</item>
|
||||
/// </list>
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// Метод безопасен для вызова даже если элемент не был зарегистрирован как источник.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public void RemoveDragSource(FrameworkElement element)
|
||||
{
|
||||
if (element == null || _disposed || !_dragSources.ContainsKey(element))
|
||||
return;
|
||||
|
||||
if (_dragSources.Remove(element, out var behavior))
|
||||
{
|
||||
behavior.Detach();
|
||||
@@ -165,10 +407,31 @@ public sealed class WinUIDragDropManager : IDisposable
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Удаляет возможность сброса.
|
||||
/// Удаляет возможность сброса у указанного элемента.
|
||||
/// </summary>
|
||||
/// <param name="element">
|
||||
/// Элемент, у которого нужно отключить возможность сброса.
|
||||
/// Если элемент не зарегистрирован как цель сброса, метод не выполняет действий.
|
||||
/// </param>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// Этот метод выполняет следующие действия:
|
||||
/// <list type="bullet">
|
||||
/// <item>Открепляет поведение цели сброса от элемента</item>
|
||||
/// <item>Восстанавливает свойство <see cref="UIElement.AllowDrop"/> = false</item>
|
||||
/// <item>Удаляет элемент из внутреннего словаря целей</item>
|
||||
/// <item>Освобождает ресурсы, связанные с поведением</item>
|
||||
/// </list>
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// Метод безопасен для вызова даже если элемент не был зарегистрирован как цель.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public void RemoveDropTarget(FrameworkElement element)
|
||||
{
|
||||
if (element == null || _disposed || !_dropTargets.ContainsKey(element))
|
||||
return;
|
||||
|
||||
if (_dropTargets.Remove(element, out var behavior))
|
||||
{
|
||||
behavior.Detach();
|
||||
@@ -176,16 +439,35 @@ public sealed class WinUIDragDropManager : IDisposable
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Очищает все регистрации.
|
||||
/// Очищает все регистрации источников и целей перетаскивания.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// Этот метод полезен в следующих сценариях:
|
||||
/// <list type="bullet">
|
||||
/// <item>При перезагрузке содержимого интерфейса</item>
|
||||
/// <item>При смене контекста данных</item>
|
||||
/// <item>При освобождении ресурсов перед удалением менеджера</item>
|
||||
/// </list>
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// После вызова этого метода все элементы теряют возможность участвовать в операциях
|
||||
/// перетаскивания. Для восстановления функциональности необходимо повторно
|
||||
/// зарегистрировать элементы через <see cref="MakeDragSource"/> и <see cref="MakeDropTarget"/>.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public void Clear()
|
||||
{
|
||||
if (_disposed) return;
|
||||
|
||||
// Открепляем все источники
|
||||
foreach (var behavior in _dragSources.Values)
|
||||
{
|
||||
behavior.Detach();
|
||||
}
|
||||
_dragSources.Clear();
|
||||
|
||||
// Открепляем все цели
|
||||
foreach (var behavior in _dropTargets.Values)
|
||||
{
|
||||
behavior.Detach();
|
||||
@@ -195,27 +477,37 @@ public sealed class WinUIDragDropManager : IDisposable
|
||||
|
||||
#endregion
|
||||
|
||||
#region Обработчики событий
|
||||
#region Event Handlers
|
||||
|
||||
private void OnDragStarted(object? sender, DragStartedEventArgs e)
|
||||
/// <summary>
|
||||
/// Обрабатывает событие начала перетаскивания.
|
||||
/// Создает и отображает визуальный элемент для обратной связи.
|
||||
/// </summary>
|
||||
private void OnDragStarted(object? sender, Core.DragDrop.Services.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)
|
||||
/// <summary>
|
||||
/// Обрабатывает событие обновления позиции перетаскивания.
|
||||
/// Обновляет позицию визуального элемента для следования за курсором.
|
||||
/// </summary>
|
||||
private void OnDragUpdated(object? sender, Core.DragDrop.Services.DragUpdatedEventArgs e)
|
||||
{
|
||||
if (_currentDragVisual != null)
|
||||
{
|
||||
@@ -228,16 +520,27 @@ public sealed class WinUIDragDropManager : IDisposable
|
||||
}
|
||||
}
|
||||
|
||||
private void OnDragCompleted(object? sender, DragCompletedEventArgs e)
|
||||
/// <summary>
|
||||
/// Обрабатывает событие завершения перетаскивания.
|
||||
/// Очищает визуальные элементы и восстанавливает состояние.
|
||||
/// </summary>
|
||||
private void OnDragCompleted(object? sender, Core.DragDrop.Services.DragCompletedEventArgs e)
|
||||
{
|
||||
CleanupDragVisual();
|
||||
}
|
||||
|
||||
private void OnDragCancelled(object? sender, DragCancelledEventArgs e)
|
||||
/// <summary>
|
||||
/// Обрабатывает событие отмены перетаскивания.
|
||||
/// Очищает визуальные элементы и восстанавливает состояние.
|
||||
/// </summary>
|
||||
private void OnDragCancelled(object? sender, Core.DragDrop.Services.DragCancelledEventArgs e)
|
||||
{
|
||||
CleanupDragVisual();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Освобождает ресурсы визуального элемента перетаскивания.
|
||||
/// </summary>
|
||||
private void CleanupDragVisual()
|
||||
{
|
||||
if (_currentDragVisual != null)
|
||||
@@ -249,24 +552,72 @@ public sealed class WinUIDragDropManager : IDisposable
|
||||
|
||||
#endregion
|
||||
|
||||
#region IDisposable
|
||||
#region Helper Methods
|
||||
|
||||
/// <summary>
|
||||
/// Проверяет состояние менеджера перед выполнением операций.
|
||||
/// </summary>
|
||||
/// <exception cref="ObjectDisposedException">
|
||||
/// Выбрасывается, если менеджер был удален.
|
||||
/// </exception>
|
||||
/// <exception cref="InvalidOperationException">
|
||||
/// Выбрасывается, если менеджер не инициализирован.
|
||||
/// </exception>
|
||||
private void ValidateManagerState()
|
||||
{
|
||||
if (_disposed)
|
||||
throw new ObjectDisposedException(nameof(WinUIDragDropManager));
|
||||
|
||||
if (!_initialized)
|
||||
throw new InvalidOperationException(
|
||||
"Менеджер не инициализирован. Вызовите метод Initialize перед использованием.");
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IDisposable Implementation
|
||||
|
||||
/// <summary>
|
||||
/// Освобождает все ресурсы, используемые <see cref="WinUIDragDropManager"/>.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// Этот метод выполняет следующие действия:
|
||||
/// <list type="bullet">
|
||||
/// <item>Отписывается от всех событий сервиса перетаскивания</item>
|
||||
/// <item>Очищает все зарегистрированные источники и цели</item>
|
||||
/// <item>Освобождает ресурсы хоста визуальных элементов</item>
|
||||
/// <item>Освобождает ресурсы сервиса перетаскивания</item>
|
||||
/// </list>
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// После вызова этого метода менеджер перестает быть пригодным для использования.
|
||||
/// Попытка использовать методы менеджера после удаления приведет к исключению
|
||||
/// <see cref="ObjectDisposedException"/>.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// Метод безопасен для многократного вызова.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public void Dispose()
|
||||
{
|
||||
if (_disposed) return;
|
||||
|
||||
Clear();
|
||||
|
||||
// Отписываемся от событий
|
||||
_dragDropService.DragStarted -= OnDragStarted;
|
||||
_dragDropService.DragUpdated -= OnDragUpdated;
|
||||
_dragDropService.DragCompleted -= OnDragCompleted;
|
||||
_dragDropService.DragCancelled -= OnDragCancelled;
|
||||
|
||||
// Очищаем все регистрации
|
||||
Clear();
|
||||
|
||||
// Освобождаем ресурсы
|
||||
_dragDropService.Dispose();
|
||||
_host.Dispose();
|
||||
|
||||
_disposed = true;
|
||||
_initialized = false;
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user