Убраны синхронные методы
This commit is contained in:
@@ -12,7 +12,7 @@ public static class AsyncDragDropUtilities
|
||||
/// <summary>
|
||||
/// Создает асинхронную реализацию источника перетаскивания.
|
||||
/// </summary>
|
||||
public static IAsyncDragSource CreateAsyncDragSource(
|
||||
public static IDragSource CreateAsyncDragSource(
|
||||
Func<Task<object>> dataProviderAsync,
|
||||
Func<Task<bool>>? canDragAsync = null,
|
||||
Func<DragInfo, DragDropEffects, Task>? onCompletedAsync = null,
|
||||
@@ -24,7 +24,7 @@ public static class AsyncDragDropUtilities
|
||||
/// <summary>
|
||||
/// Создает асинхронную реализацию цели сброса.
|
||||
/// </summary>
|
||||
public static IAsyncDropTarget CreateAsyncDropTarget(
|
||||
public static IDropTarget CreateAsyncDropTarget(
|
||||
Func<DropInfo, Task<bool>>? canAcceptAsync = null,
|
||||
Func<DropInfo, Task>? onDragOverAsync = null,
|
||||
Func<DropInfo, Task>? onDropAsync = null,
|
||||
@@ -33,28 +33,39 @@ public static class AsyncDragDropUtilities
|
||||
return new AsyncDropTargetWrapper(canAcceptAsync, onDragOverAsync, onDropAsync, onDragLeaveAsync);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Создает адаптер для преобразования синхронного источника в асинхронный.
|
||||
/// </summary>
|
||||
public static IAsyncDragSource CreateAsyncAdapter(IDragSource syncSource)
|
||||
{
|
||||
return new SyncToAsyncDragSourceAdapter(syncSource);
|
||||
}
|
||||
|
||||
#region Factory Methods
|
||||
|
||||
/// <summary>
|
||||
/// Создает адаптер для преобразования синхронной цели в асинхронную.
|
||||
/// Создает информацию о перетаскивании.
|
||||
/// </summary>
|
||||
public static IAsyncDropTarget CreateAsyncAdapter(IDropTarget syncTarget)
|
||||
public static Models.DragInfo CreateDragInfo(
|
||||
object data,
|
||||
Geometry.Point startPosition,
|
||||
Enums.DragDropEffects allowedEffects = Enums.DragDropEffects.Copy | Enums.DragDropEffects.Move,
|
||||
object? source = null,
|
||||
Dictionary<string, object>? parameters = null)
|
||||
{
|
||||
return new SyncToAsyncDropTargetAdapter(syncTarget);
|
||||
var dragInfo = new Models.DragInfo(data, allowedEffects, startPosition, source);
|
||||
|
||||
if (parameters != null)
|
||||
{
|
||||
foreach (var param in parameters)
|
||||
{
|
||||
dragInfo.SetParameter(param.Key, param.Value);
|
||||
}
|
||||
}
|
||||
|
||||
return dragInfo;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Обертки-реализации
|
||||
|
||||
/// <summary>
|
||||
/// Обертка для создания асинхронного источника перетаскивания.
|
||||
/// </summary>
|
||||
private sealed class AsyncDragSourceWrapper : IAsyncDragSource
|
||||
private sealed class AsyncDragSourceWrapper : IDragSource
|
||||
{
|
||||
private readonly Func<Task<object>> _dataProviderAsync;
|
||||
private readonly Func<Task<bool>>? _canDragAsync;
|
||||
@@ -73,17 +84,14 @@ public static class AsyncDragDropUtilities
|
||||
_onCancelledAsync = onCancelledAsync;
|
||||
}
|
||||
|
||||
public async Task<(bool CanStart, DragInfo? DragInfo)> CanStartDragAsync()
|
||||
public async Task<(bool CanStart, DragInfo? DragInfo)> CanStartDragAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Проверяем, может ли начаться перетаскивание
|
||||
if (_canDragAsync != null)
|
||||
{
|
||||
var canDrag = await _canDragAsync().ConfigureAwait(false);
|
||||
if (!canDrag)
|
||||
return (false, null);
|
||||
}
|
||||
var canDrag = _canDragAsync != null ? await _canDragAsync().ConfigureAwait(false) : true;
|
||||
if (!canDrag)
|
||||
return (false, null);
|
||||
|
||||
// Получаем данные
|
||||
var data = await _dataProviderAsync().ConfigureAwait(false);
|
||||
@@ -91,7 +99,7 @@ public static class AsyncDragDropUtilities
|
||||
return (false, null);
|
||||
|
||||
// Создаем информацию о перетаскивании
|
||||
var dragInfo = DragDropUtilities.CreateDragInfo(
|
||||
var dragInfo = CreateDragInfo(
|
||||
data,
|
||||
Geometry.Point.Zero,
|
||||
DragDropEffects.Copy | DragDropEffects.Move,
|
||||
@@ -105,13 +113,13 @@ public static class AsyncDragDropUtilities
|
||||
}
|
||||
}
|
||||
|
||||
public Task<bool> StartDragAsync(DragInfo dragInfo)
|
||||
public Task<bool> StartDragAsync(DragInfo dragInfo, CancellationToken cancellationToken = default)
|
||||
{
|
||||
// Базовая реализация всегда разрешает начало
|
||||
return Task.FromResult(true);
|
||||
}
|
||||
|
||||
public async Task DragCompletedAsync(DragInfo dragInfo, DragDropEffects effects)
|
||||
public async Task DragCompletedAsync(DragInfo dragInfo, DragDropEffects effects, CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (_onCompletedAsync != null)
|
||||
{
|
||||
@@ -119,46 +127,19 @@ public static class AsyncDragDropUtilities
|
||||
}
|
||||
}
|
||||
|
||||
public async Task DragCancelledAsync(DragInfo dragInfo)
|
||||
public async Task DragCancelledAsync(DragInfo dragInfo, CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (_onCancelledAsync != null)
|
||||
{
|
||||
await _onCancelledAsync(dragInfo).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
#region Синхронная реализация (для IDragSource)
|
||||
|
||||
public bool CanStartDrag(out DragInfo? dragInfo)
|
||||
{
|
||||
// Для синхронного вызова используем Task.Result
|
||||
var result = Task.Run(() => CanStartDragAsync()).GetAwaiter().GetResult();
|
||||
dragInfo = result.DragInfo;
|
||||
return result.CanStart;
|
||||
}
|
||||
|
||||
public bool StartDrag(DragInfo dragInfo)
|
||||
{
|
||||
return Task.Run(() => StartDragAsync(dragInfo)).GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
public void DragCompleted(DragInfo dragInfo, DragDropEffects effects)
|
||||
{
|
||||
Task.Run(() => DragCompletedAsync(dragInfo, effects)).Wait();
|
||||
}
|
||||
|
||||
public void DragCancelled(DragInfo dragInfo)
|
||||
{
|
||||
Task.Run(() => DragCancelledAsync(dragInfo)).Wait();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Обертка для создания асинхронной цели сброса.
|
||||
/// </summary>
|
||||
private sealed class AsyncDropTargetWrapper : IAsyncDropTarget
|
||||
private sealed class AsyncDropTargetWrapper : IDropTarget
|
||||
{
|
||||
private readonly Func<DropInfo, Task<bool>>? _canAcceptAsync;
|
||||
private readonly Func<DropInfo, Task>? _onDragOverAsync;
|
||||
@@ -177,7 +158,7 @@ public static class AsyncDragDropUtilities
|
||||
_onDragLeaveAsync = onDragLeaveAsync;
|
||||
}
|
||||
|
||||
public async Task<bool> CanAcceptDropAsync(DropInfo dropInfo)
|
||||
public async Task<bool> CanAcceptDropAsync(DropInfo dropInfo, CancellationToken cancellationToken = default)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -193,7 +174,7 @@ public static class AsyncDragDropUtilities
|
||||
}
|
||||
}
|
||||
|
||||
public async Task DragOverAsync(DropInfo dropInfo)
|
||||
public async Task DragOverAsync(DropInfo dropInfo, CancellationToken cancellationToken = default)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -208,7 +189,7 @@ public static class AsyncDragDropUtilities
|
||||
}
|
||||
}
|
||||
|
||||
public async Task DropAsync(DropInfo dropInfo)
|
||||
public async Task DropAsync(DropInfo dropInfo, CancellationToken cancellationToken = default)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -223,7 +204,7 @@ public static class AsyncDragDropUtilities
|
||||
}
|
||||
}
|
||||
|
||||
public async Task DragLeaveAsync()
|
||||
public async Task DragLeaveAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -237,477 +218,8 @@ public static class AsyncDragDropUtilities
|
||||
// Игнорируем ошибки в обработчике
|
||||
}
|
||||
}
|
||||
|
||||
#region Синхронная реализация (для IDropTarget)
|
||||
|
||||
public bool CanAcceptDrop(DropInfo dropInfo)
|
||||
{
|
||||
return Task.Run(() => CanAcceptDropAsync(dropInfo)).GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
public void DragOver(DropInfo dropInfo)
|
||||
{
|
||||
Task.Run(() => DragOverAsync(dropInfo)).Wait();
|
||||
}
|
||||
|
||||
public void Drop(DropInfo dropInfo)
|
||||
{
|
||||
Task.Run(() => DropAsync(dropInfo)).Wait();
|
||||
}
|
||||
|
||||
public void DragLeave()
|
||||
{
|
||||
Task.Run(DragLeaveAsync).Wait();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Адаптер для преобразования синхронного источника в асинхронный.
|
||||
/// </summary>
|
||||
private sealed class SyncToAsyncDragSourceAdapter : IAsyncDragSource
|
||||
{
|
||||
private readonly IDragSource _syncSource;
|
||||
|
||||
public SyncToAsyncDragSourceAdapter(IDragSource syncSource)
|
||||
{
|
||||
_syncSource = syncSource ?? throw new ArgumentNullException(nameof(syncSource));
|
||||
}
|
||||
|
||||
public async Task<(bool CanStart, DragInfo? DragInfo)> CanStartDragAsync()
|
||||
{
|
||||
return await Task.Run(() =>
|
||||
{
|
||||
var canStart = _syncSource.CanStartDrag(out var dragInfo);
|
||||
return (canStart, dragInfo);
|
||||
}).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public async Task<bool> StartDragAsync(DragInfo dragInfo)
|
||||
{
|
||||
return await Task.Run(() => _syncSource.StartDrag(dragInfo)).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public async Task DragCompletedAsync(DragInfo dragInfo, DragDropEffects effects)
|
||||
{
|
||||
await Task.Run(() => _syncSource.DragCompleted(dragInfo, effects)).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public async Task DragCancelledAsync(DragInfo dragInfo)
|
||||
{
|
||||
await Task.Run(() => _syncSource.DragCancelled(dragInfo)).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
#region Синхронная реализация (делегируем синхронному источнику)
|
||||
|
||||
public bool CanStartDrag(out DragInfo? dragInfo)
|
||||
{
|
||||
return _syncSource.CanStartDrag(out dragInfo);
|
||||
}
|
||||
|
||||
public bool StartDrag(DragInfo dragInfo)
|
||||
{
|
||||
return _syncSource.StartDrag(dragInfo);
|
||||
}
|
||||
|
||||
public void DragCompleted(DragInfo dragInfo, DragDropEffects effects)
|
||||
{
|
||||
_syncSource.DragCompleted(dragInfo, effects);
|
||||
}
|
||||
|
||||
public void DragCancelled(DragInfo dragInfo)
|
||||
{
|
||||
_syncSource.DragCancelled(dragInfo);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Адаптер для преобразования синхронной цели в асинхронную.
|
||||
/// </summary>
|
||||
private sealed class SyncToAsyncDropTargetAdapter : IAsyncDropTarget
|
||||
{
|
||||
private readonly IDropTarget _syncTarget;
|
||||
|
||||
public SyncToAsyncDropTargetAdapter(IDropTarget syncTarget)
|
||||
{
|
||||
_syncTarget = syncTarget ?? throw new ArgumentNullException(nameof(syncTarget));
|
||||
}
|
||||
|
||||
public async Task<bool> CanAcceptDropAsync(DropInfo dropInfo)
|
||||
{
|
||||
return await Task.Run(() => _syncTarget.CanAcceptDrop(dropInfo)).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public async Task DragOverAsync(DropInfo dropInfo)
|
||||
{
|
||||
await Task.Run(() => _syncTarget.DragOver(dropInfo)).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public async Task DropAsync(DropInfo dropInfo)
|
||||
{
|
||||
await Task.Run(() => _syncTarget.Drop(dropInfo)).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public async Task DragLeaveAsync()
|
||||
{
|
||||
await Task.Run(() => _syncTarget.DragLeave()).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
#region Синхронная реализация (делегируем синхронной цели)
|
||||
|
||||
public bool CanAcceptDrop(DropInfo dropInfo)
|
||||
{
|
||||
return _syncTarget.CanAcceptDrop(dropInfo);
|
||||
}
|
||||
|
||||
public void DragOver(DropInfo dropInfo)
|
||||
{
|
||||
_syncTarget.DragOver(dropInfo);
|
||||
}
|
||||
|
||||
public void Drop(DropInfo dropInfo)
|
||||
{
|
||||
_syncTarget.Drop(dropInfo);
|
||||
}
|
||||
|
||||
public void DragLeave()
|
||||
{
|
||||
_syncTarget.DragLeave();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Утилитарные методы
|
||||
|
||||
/// <summary>
|
||||
/// Выполняет асинхронную операцию с таймаутом.
|
||||
/// </summary>
|
||||
public static async Task<T> ExecuteWithTimeoutAsync<T>(
|
||||
Task<T> task,
|
||||
TimeSpan timeout,
|
||||
T defaultValue = default!)
|
||||
{
|
||||
if (timeout <= TimeSpan.Zero)
|
||||
return await task.ConfigureAwait(false);
|
||||
|
||||
var delayTask = Task.Delay(timeout);
|
||||
var completedTask = await Task.WhenAny(task, delayTask).ConfigureAwait(false);
|
||||
|
||||
if (completedTask == delayTask)
|
||||
{
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
return await task.ConfigureAwait(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Выполняет асинхронную операцию с таймаутом.
|
||||
/// </summary>
|
||||
public static async Task<bool> ExecuteWithTimeoutAsync(
|
||||
Task task,
|
||||
TimeSpan timeout)
|
||||
{
|
||||
if (timeout <= TimeSpan.Zero)
|
||||
{
|
||||
await task.ConfigureAwait(false);
|
||||
return true;
|
||||
}
|
||||
|
||||
var delayTask = Task.Delay(timeout);
|
||||
var completedTask = await Task.WhenAny(task, delayTask).ConfigureAwait(false);
|
||||
|
||||
return completedTask == task;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Выполняет асинхронную операцию с таймаутом и обработкой ошибок.
|
||||
/// </summary>
|
||||
public static async Task<T?> ExecuteSafeWithTimeoutAsync<T>(
|
||||
Task<T> task,
|
||||
TimeSpan timeout,
|
||||
Func<Exception, T?> errorHandler = null) where T : class
|
||||
{
|
||||
try
|
||||
{
|
||||
if (timeout <= TimeSpan.Zero)
|
||||
return await task.ConfigureAwait(false);
|
||||
|
||||
var delayTask = Task.Delay(timeout);
|
||||
var completedTask = await Task.WhenAny(task, delayTask).ConfigureAwait(false);
|
||||
|
||||
if (completedTask == delayTask)
|
||||
{
|
||||
return default;
|
||||
}
|
||||
|
||||
return await task.ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return errorHandler?.Invoke(ex) ?? default;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Создает комбинированный источник из синхронного и асинхронного.
|
||||
/// </summary>
|
||||
public static IAsyncDragSource Combine(
|
||||
IDragSource syncSource,
|
||||
IAsyncDragSource asyncSource,
|
||||
bool preferAsync = true)
|
||||
{
|
||||
return new CombinedDragSource(syncSource, asyncSource, preferAsync);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Создает комбинированную цель из синхронной и асинхронной.
|
||||
/// </summary>
|
||||
public static IAsyncDropTarget Combine(
|
||||
IDropTarget syncTarget,
|
||||
IAsyncDropTarget asyncTarget,
|
||||
bool preferAsync = true)
|
||||
{
|
||||
return new CombinedDropTarget(syncTarget, asyncTarget, preferAsync);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Комбинированные реализации
|
||||
|
||||
/// <summary>
|
||||
/// Комбинированный источник, поддерживающий как синхронный, так и асинхронный API.
|
||||
/// </summary>
|
||||
private sealed class CombinedDragSource : IAsyncDragSource
|
||||
{
|
||||
private readonly IDragSource _syncSource;
|
||||
private readonly IAsyncDragSource _asyncSource;
|
||||
private readonly bool _preferAsync;
|
||||
|
||||
public CombinedDragSource(IDragSource syncSource, IAsyncDragSource asyncSource, bool preferAsync)
|
||||
{
|
||||
_syncSource = syncSource ?? throw new ArgumentNullException(nameof(syncSource));
|
||||
_asyncSource = asyncSource ?? throw new ArgumentNullException(nameof(asyncSource));
|
||||
_preferAsync = preferAsync;
|
||||
}
|
||||
|
||||
public async Task<(bool CanStart, DragInfo? DragInfo)> CanStartDragAsync()
|
||||
{
|
||||
if (_preferAsync)
|
||||
{
|
||||
try
|
||||
{
|
||||
return await _asyncSource.CanStartDragAsync().ConfigureAwait(false);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// В случае ошибки пробуем синхронную версию
|
||||
}
|
||||
}
|
||||
|
||||
// Используем синхронную версию в отдельной задаче
|
||||
return await Task.Run(() =>
|
||||
{
|
||||
var canStart = _syncSource.CanStartDrag(out var dragInfo);
|
||||
return (canStart, dragInfo);
|
||||
}).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public async Task<bool> StartDragAsync(DragInfo dragInfo)
|
||||
{
|
||||
if (_preferAsync)
|
||||
{
|
||||
try
|
||||
{
|
||||
return await _asyncSource.StartDragAsync(dragInfo).ConfigureAwait(false);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// В случае ошибки пробуем синхронную версию
|
||||
}
|
||||
}
|
||||
|
||||
return await Task.Run(() => _syncSource.StartDrag(dragInfo)).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public async Task DragCompletedAsync(DragInfo dragInfo, DragDropEffects effects)
|
||||
{
|
||||
if (_preferAsync)
|
||||
{
|
||||
try
|
||||
{
|
||||
await _asyncSource.DragCompletedAsync(dragInfo, effects).ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
catch
|
||||
{
|
||||
// В случае ошибки пробуем синхронную версию
|
||||
}
|
||||
}
|
||||
|
||||
await Task.Run(() => _syncSource.DragCompleted(dragInfo, effects)).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public async Task DragCancelledAsync(DragInfo dragInfo)
|
||||
{
|
||||
if (_preferAsync)
|
||||
{
|
||||
try
|
||||
{
|
||||
await _asyncSource.DragCancelledAsync(dragInfo).ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
catch
|
||||
{
|
||||
// В случае ошибки пробуем синхронную версию
|
||||
}
|
||||
}
|
||||
|
||||
await Task.Run(() => _syncSource.DragCancelled(dragInfo)).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
#region Синхронная реализация
|
||||
|
||||
public bool CanStartDrag(out DragInfo? dragInfo)
|
||||
{
|
||||
return _syncSource.CanStartDrag(out dragInfo);
|
||||
}
|
||||
|
||||
public bool StartDrag(DragInfo dragInfo)
|
||||
{
|
||||
return _syncSource.StartDrag(dragInfo);
|
||||
}
|
||||
|
||||
public void DragCompleted(DragInfo dragInfo, DragDropEffects effects)
|
||||
{
|
||||
_syncSource.DragCompleted(dragInfo, effects);
|
||||
}
|
||||
|
||||
public void DragCancelled(DragInfo dragInfo)
|
||||
{
|
||||
_syncSource.DragCancelled(dragInfo);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Комбинированная цель, поддерживающая как синхронный, так и асинхронный API.
|
||||
/// </summary>
|
||||
private sealed class CombinedDropTarget : IAsyncDropTarget
|
||||
{
|
||||
private readonly IDropTarget _syncTarget;
|
||||
private readonly IAsyncDropTarget _asyncTarget;
|
||||
private readonly bool _preferAsync;
|
||||
|
||||
public CombinedDropTarget(IDropTarget syncTarget, IAsyncDropTarget asyncTarget, bool preferAsync)
|
||||
{
|
||||
_syncTarget = syncTarget ?? throw new ArgumentNullException(nameof(syncTarget));
|
||||
_asyncTarget = asyncTarget ?? throw new ArgumentNullException(nameof(asyncTarget));
|
||||
_preferAsync = preferAsync;
|
||||
}
|
||||
|
||||
public async Task<bool> CanAcceptDropAsync(DropInfo dropInfo)
|
||||
{
|
||||
if (_preferAsync)
|
||||
{
|
||||
try
|
||||
{
|
||||
return await _asyncTarget.CanAcceptDropAsync(dropInfo).ConfigureAwait(false);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// В случае ошибки пробуем синхронную версию
|
||||
}
|
||||
}
|
||||
|
||||
return await Task.Run(() => _syncTarget.CanAcceptDrop(dropInfo)).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public async Task DragOverAsync(DropInfo dropInfo)
|
||||
{
|
||||
if (_preferAsync)
|
||||
{
|
||||
try
|
||||
{
|
||||
await _asyncTarget.DragOverAsync(dropInfo).ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
catch
|
||||
{
|
||||
// В случае ошибки пробуем синхронную версию
|
||||
}
|
||||
}
|
||||
|
||||
await Task.Run(() => _syncTarget.DragOver(dropInfo)).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public async Task DropAsync(DropInfo dropInfo)
|
||||
{
|
||||
if (_preferAsync)
|
||||
{
|
||||
try
|
||||
{
|
||||
await _asyncTarget.DropAsync(dropInfo).ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
catch
|
||||
{
|
||||
// В случае ошибки пробуем синхронную версию
|
||||
}
|
||||
}
|
||||
|
||||
await Task.Run(() => _syncTarget.Drop(dropInfo)).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public async Task DragLeaveAsync()
|
||||
{
|
||||
if (_preferAsync)
|
||||
{
|
||||
try
|
||||
{
|
||||
await _asyncTarget.DragLeaveAsync().ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
catch
|
||||
{
|
||||
// В случае ошибки пробуем синхронную версию
|
||||
}
|
||||
}
|
||||
|
||||
await Task.Run(() => _syncTarget.DragLeave()).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
#region Синхронная реализация
|
||||
|
||||
public bool CanAcceptDrop(DropInfo dropInfo)
|
||||
{
|
||||
return _syncTarget.CanAcceptDrop(dropInfo);
|
||||
}
|
||||
|
||||
public void DragOver(DropInfo dropInfo)
|
||||
{
|
||||
_syncTarget.DragOver(dropInfo);
|
||||
}
|
||||
|
||||
public void Drop(DropInfo dropInfo)
|
||||
{
|
||||
_syncTarget.Drop(dropInfo);
|
||||
}
|
||||
|
||||
public void DragLeave()
|
||||
{
|
||||
_syncTarget.DragLeave();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -1,275 +0,0 @@
|
||||
namespace Lattice.Core.DragDrop.Utilities;
|
||||
|
||||
/// <summary>
|
||||
/// Утилиты для работы с системой перетаскивания.
|
||||
/// </summary>
|
||||
public static class DragDropUtilities
|
||||
{
|
||||
#region Effect Utilities
|
||||
|
||||
/// <summary>
|
||||
/// Проверяет, совместимы ли эффекты источника и цели.
|
||||
/// </summary>
|
||||
public static bool AreEffectsCompatible(Enums.DragDropEffects sourceEffects, Enums.DragDropEffects targetEffects)
|
||||
{
|
||||
if (sourceEffects == Enums.DragDropEffects.None || targetEffects == Enums.DragDropEffects.None)
|
||||
return false;
|
||||
|
||||
return (sourceEffects & targetEffects) != Enums.DragDropEffects.None;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Получает наиболее подходящий эффект на основе доступных.
|
||||
/// </summary>
|
||||
public static Enums.DragDropEffects GetBestEffect(Enums.DragDropEffects available, Enums.DragDropEffects preferred)
|
||||
{
|
||||
if ((available & preferred) != Enums.DragDropEffects.None)
|
||||
return available & preferred;
|
||||
|
||||
if ((available & Enums.DragDropEffects.Move) != Enums.DragDropEffects.None)
|
||||
return Enums.DragDropEffects.Move;
|
||||
|
||||
if ((available & Enums.DragDropEffects.Copy) != Enums.DragDropEffects.None)
|
||||
return Enums.DragDropEffects.Copy;
|
||||
|
||||
if ((available & Enums.DragDropEffects.Link) != Enums.DragDropEffects.None)
|
||||
return Enums.DragDropEffects.Link;
|
||||
|
||||
return Enums.DragDropEffects.None;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Geometry Utilities
|
||||
|
||||
/// <summary>
|
||||
/// Вычисляет расстояние между двумя точками.
|
||||
/// </summary>
|
||||
public static double CalculateDistance(Geometry.Point p1, Geometry.Point p2)
|
||||
{
|
||||
var dx = p2.X - p1.X;
|
||||
var dy = p2.Y - p1.Y;
|
||||
return Math.Sqrt(dx * dx + dy * dy);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Проверяет, превысило ли перемещение пороговое значение.
|
||||
/// </summary>
|
||||
public static bool HasExceededDragThreshold(Geometry.Point startPoint, Geometry.Point currentPoint, double threshold)
|
||||
{
|
||||
var distance = CalculateDistance(startPoint, currentPoint);
|
||||
return distance >= threshold;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Определяет позицию сброса относительно прямоугольника.
|
||||
/// </summary>
|
||||
public static Enums.DropPosition GetDropPosition(Geometry.Point point, Geometry.Rect bounds, double edgeThreshold = 20.0)
|
||||
{
|
||||
if (!bounds.Contains(new Geometry.Point(point.X, point.Y)))
|
||||
return Enums.DropPosition.Inside;
|
||||
|
||||
var relativeX = (point.X - bounds.X) / bounds.Width;
|
||||
var relativeY = (point.Y - bounds.Y) / bounds.Height;
|
||||
|
||||
if (relativeX < edgeThreshold / bounds.Width)
|
||||
return Enums.DropPosition.Left;
|
||||
if (relativeX > 1 - edgeThreshold / bounds.Width)
|
||||
return Enums.DropPosition.Right;
|
||||
if (relativeY < edgeThreshold / bounds.Height)
|
||||
return Enums.DropPosition.Top;
|
||||
if (relativeY > 1 - edgeThreshold / bounds.Height)
|
||||
return Enums.DropPosition.Bottom;
|
||||
|
||||
return Enums.DropPosition.Center;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Factory Methods
|
||||
|
||||
/// <summary>
|
||||
/// Создает информацию о перетаскивании.
|
||||
/// </summary>
|
||||
public static Models.DragInfo CreateDragInfo(
|
||||
object data,
|
||||
Geometry.Point startPosition,
|
||||
Enums.DragDropEffects allowedEffects = Enums.DragDropEffects.Copy | Enums.DragDropEffects.Move,
|
||||
object? source = null,
|
||||
Dictionary<string, object>? parameters = null)
|
||||
{
|
||||
var dragInfo = new Models.DragInfo(data, allowedEffects, startPosition, source);
|
||||
|
||||
if (parameters != null)
|
||||
{
|
||||
foreach (var param in parameters)
|
||||
{
|
||||
dragInfo.SetParameter(param.Key, param.Value);
|
||||
}
|
||||
}
|
||||
|
||||
return dragInfo;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Создает простую реализацию источника перетаскивания.
|
||||
/// </summary>
|
||||
public static Abstractions.IDragSource CreateSimpleDragSource(
|
||||
Func<object> dataProvider,
|
||||
Func<bool>? canDrag = null,
|
||||
Action<Models.DragInfo, Enums.DragDropEffects>? onCompleted = null,
|
||||
Action<Models.DragInfo>? onCancelled = null)
|
||||
{
|
||||
return new SimpleDragSource(dataProvider, canDrag, onCompleted, onCancelled);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Создает простую реализацию цели сброса.
|
||||
/// </summary>
|
||||
public static Abstractions.IDropTarget CreateSimpleDropTarget(
|
||||
Func<Models.DropInfo, bool>? canAccept = null,
|
||||
Action<Models.DropInfo>? onDragOver = null,
|
||||
Action<Models.DropInfo>? onDrop = null,
|
||||
Action? onDragLeave = null)
|
||||
{
|
||||
return new SimpleDropTarget(canAccept, onDragOver, onDrop, onDragLeave);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Data Extraction
|
||||
|
||||
/// <summary>
|
||||
/// Извлекает данные из элемента с поддержкой различных паттернов.
|
||||
/// </summary>
|
||||
public static object? ExtractData(object? element)
|
||||
{
|
||||
if (element == null)
|
||||
return null;
|
||||
|
||||
// Проверяем, реализует ли элемент специальный интерфейс
|
||||
if (element is Abstractions.IDragSource dragSource)
|
||||
{
|
||||
if (dragSource.CanStartDrag(out var dragInfo) && dragInfo != null)
|
||||
return dragInfo.Data;
|
||||
}
|
||||
|
||||
// В реальной реализации здесь будет рефлексия для проверки свойств
|
||||
// DataContext, Content и т.д.
|
||||
|
||||
// Возвращаем сам элемент как данные
|
||||
return element;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Проверяет, совместимы ли данные с указанными типами.
|
||||
/// </summary>
|
||||
public static bool IsDataCompatible(object? data, IEnumerable<Type>? acceptedTypes)
|
||||
{
|
||||
if (data == null || acceptedTypes == null)
|
||||
return false;
|
||||
|
||||
var dataType = data.GetType();
|
||||
|
||||
foreach (var acceptedType in acceptedTypes)
|
||||
{
|
||||
if (acceptedType.IsAssignableFrom(dataType))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Helper Classes
|
||||
|
||||
private sealed class SimpleDragSource : Abstractions.IDragSource
|
||||
{
|
||||
private readonly Func<object> _dataProvider;
|
||||
private readonly Func<bool>? _canDrag;
|
||||
private readonly Action<Models.DragInfo, Enums.DragDropEffects>? _onCompleted;
|
||||
private readonly Action<Models.DragInfo>? _onCancelled;
|
||||
|
||||
public SimpleDragSource(
|
||||
Func<object> dataProvider,
|
||||
Func<bool>? canDrag = null,
|
||||
Action<Models.DragInfo, Enums.DragDropEffects>? onCompleted = null,
|
||||
Action<Models.DragInfo>? onCancelled = null)
|
||||
{
|
||||
_dataProvider = dataProvider ?? throw new ArgumentNullException(nameof(dataProvider));
|
||||
_canDrag = canDrag;
|
||||
_onCompleted = onCompleted;
|
||||
_onCancelled = onCancelled;
|
||||
}
|
||||
|
||||
public bool CanStartDrag(out Models.DragInfo? dragInfo)
|
||||
{
|
||||
dragInfo = null;
|
||||
|
||||
if (_canDrag?.Invoke() == false)
|
||||
return false;
|
||||
|
||||
var data = _dataProvider();
|
||||
if (data == null)
|
||||
return false;
|
||||
|
||||
dragInfo = CreateDragInfo(data, Geometry.Point.Zero, Enums.DragDropEffects.Copy | Enums.DragDropEffects.Move, this);
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool StartDrag(Models.DragInfo dragInfo) => true;
|
||||
|
||||
public void DragCompleted(Models.DragInfo dragInfo, Enums.DragDropEffects effects)
|
||||
{
|
||||
_onCompleted?.Invoke(dragInfo, effects);
|
||||
}
|
||||
|
||||
public void DragCancelled(Models.DragInfo dragInfo)
|
||||
{
|
||||
_onCancelled?.Invoke(dragInfo);
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class SimpleDropTarget : Abstractions.IDropTarget
|
||||
{
|
||||
private readonly Func<Models.DropInfo, bool>? _canAccept;
|
||||
private readonly Action<Models.DropInfo>? _onDragOver;
|
||||
private readonly Action<Models.DropInfo>? _onDrop;
|
||||
private readonly Action? _onDragLeave;
|
||||
|
||||
public SimpleDropTarget(
|
||||
Func<Models.DropInfo, bool>? canAccept = null,
|
||||
Action<Models.DropInfo>? onDragOver = null,
|
||||
Action<Models.DropInfo>? onDrop = null,
|
||||
Action? onDragLeave = null)
|
||||
{
|
||||
_canAccept = canAccept;
|
||||
_onDragOver = onDragOver;
|
||||
_onDrop = onDrop;
|
||||
_onDragLeave = onDragLeave;
|
||||
}
|
||||
|
||||
public bool CanAcceptDrop(Models.DropInfo dropInfo)
|
||||
{
|
||||
return _canAccept?.Invoke(dropInfo) ?? true;
|
||||
}
|
||||
|
||||
public void DragOver(Models.DropInfo dropInfo)
|
||||
{
|
||||
_onDragOver?.Invoke(dropInfo);
|
||||
}
|
||||
|
||||
public void Drop(Models.DropInfo dropInfo)
|
||||
{
|
||||
_onDrop?.Invoke(dropInfo);
|
||||
}
|
||||
|
||||
public void DragLeave()
|
||||
{
|
||||
_onDragLeave?.Invoke();
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
Reference in New Issue
Block a user