доработана winUI реализация
This commit is contained in:
@@ -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
|
||||
}
|
||||
Reference in New Issue
Block a user