diff --git a/Lattice.Core.DragDrop/Abstractions/IAsyncDragSource.cs b/Lattice.Core.DragDrop/Abstractions/IAsyncDragSource.cs
deleted file mode 100644
index 834b788..0000000
--- a/Lattice.Core.DragDrop/Abstractions/IAsyncDragSource.cs
+++ /dev/null
@@ -1,28 +0,0 @@
-namespace Lattice.Core.DragDrop.Abstractions;
-
-///
-/// Определяет контракт для объектов, которые могут быть источником данных
-/// в операции перетаскивания с поддержкой асинхронных операций.
-///
-public interface IAsyncDragSource : IDragSource
-{
- ///
- /// Определяет, может ли объект начать операцию перетаскивания (асинхронно).
- ///
- Task<(bool CanStart, Models.DragInfo? DragInfo)> CanStartDragAsync();
-
- ///
- /// Начинает операцию перетаскивания (асинхронно).
- ///
- Task StartDragAsync(Models.DragInfo dragInfo);
-
- ///
- /// Вызывается при завершении операции перетаскивания (асинхронно).
- ///
- Task DragCompletedAsync(Models.DragInfo dragInfo, Enums.DragDropEffects effects);
-
- ///
- /// Вызывается при отмене операции перетаскивания (асинхронно).
- ///
- Task DragCancelledAsync(Models.DragInfo dragInfo);
-}
\ No newline at end of file
diff --git a/Lattice.Core.DragDrop/Abstractions/IAsyncDropTarget.cs b/Lattice.Core.DragDrop/Abstractions/IAsyncDropTarget.cs
deleted file mode 100644
index 99aa78c..0000000
--- a/Lattice.Core.DragDrop/Abstractions/IAsyncDropTarget.cs
+++ /dev/null
@@ -1,28 +0,0 @@
-namespace Lattice.Core.DragDrop.Abstractions;
-
-///
-/// Определяет контракт для объектов, которые могут принимать сбрасываемые данные
-/// в операции перетаскивания с поддержкой асинхронных операций.
-///
-public interface IAsyncDropTarget : IDropTarget
-{
- ///
- /// Определяет, может ли объект принять сбрасываемые данные (асинхронно).
- ///
- Task CanAcceptDropAsync(Models.DropInfo dropInfo);
-
- ///
- /// Вызывается, когда перетаскиваемый объект находится над целью (асинхронно).
- ///
- Task DragOverAsync(Models.DropInfo dropInfo);
-
- ///
- /// Вызывается, когда пользователь сбрасывает данные на цель (асинхронно).
- ///
- Task DropAsync(Models.DropInfo dropInfo);
-
- ///
- /// Вызывается, когда перетаскиваемый объект покидает область цели (асинхронно).
- ///
- Task DragLeaveAsync();
-}
\ No newline at end of file
diff --git a/Lattice.Core.DragDrop/Abstractions/IDragSource.cs b/Lattice.Core.DragDrop/Abstractions/IDragSource.cs
index b5fea3b..65822e9 100644
--- a/Lattice.Core.DragDrop/Abstractions/IDragSource.cs
+++ b/Lattice.Core.DragDrop/Abstractions/IDragSource.cs
@@ -13,18 +13,15 @@ public interface IDragSource
///
/// Определяет, может ли объект начать операцию перетаскивания.
///
- ///
- /// Информация о перетаскивании, которая будет заполнена данными, если операция разрешена.
- ///
///
- /// true, если объект может начать перетаскивание; в противном случае — false.
+ /// Кортеж, содержащий флаг возможности начала перетаскивания и информацию о перетаскивании.
///
///
/// Этот метод вызывается системой перетаскивания для проверки возможности
/// начала операции. Если метод возвращает true, он должен заполнить
- /// необходимыми данными.
+ /// DragInfo необходимыми данными.
///
- bool CanStartDrag(out Models.DragInfo? dragInfo);
+ Task<(bool CanStart, Models.DragInfo? DragInfo)> CanStartDragAsync(CancellationToken cancellationToken = default);
///
/// Начинает операцию перетаскивания.
@@ -38,7 +35,7 @@ public interface IDragSource
/// Реализация должна подготовить данные для перетаскивания и, возможно,
/// создать визуальное представление перетаскиваемого объекта.
///
- bool StartDrag(Models.DragInfo dragInfo);
+ Task StartDragAsync(Models.DragInfo dragInfo, CancellationToken cancellationToken = default);
///
/// Вызывается при завершении операции перетаскивания.
@@ -50,7 +47,7 @@ public interface IDragSource
/// (успешного или неуспешного). Реализация может выполнить очистку
/// или обновить состояние на основе результата операции.
///
- void DragCompleted(Models.DragInfo dragInfo, Enums.DragDropEffects effects);
+ Task DragCompletedAsync(Models.DragInfo dragInfo, Enums.DragDropEffects effects, CancellationToken cancellationToken = default);
///
/// Вызывается при отмене операции перетаскивания.
@@ -60,5 +57,5 @@ public interface IDragSource
/// Этот метод вызывается, когда операция перетаскивания была отменена
/// пользователем (например, нажатием клавиши Escape).
///
- void DragCancelled(Models.DragInfo dragInfo);
+ Task DragCancelledAsync(Models.DragInfo dragInfo, CancellationToken cancellationToken = default);
}
\ No newline at end of file
diff --git a/Lattice.Core.DragDrop/Abstractions/IDropTarget.cs b/Lattice.Core.DragDrop/Abstractions/IDropTarget.cs
index 736666e..b3eb5f5 100644
--- a/Lattice.Core.DragDrop/Abstractions/IDropTarget.cs
+++ b/Lattice.Core.DragDrop/Abstractions/IDropTarget.cs
@@ -22,7 +22,7 @@ public interface IDropTarget
/// Реализация должна проверить, совместимы ли данные с целью, и установить
/// предлагаемые эффекты в .
///
- bool CanAcceptDrop(Models.DropInfo dropInfo);
+ Task CanAcceptDropAsync(Models.DropInfo dropInfo, CancellationToken cancellationToken = default);
///
/// Вызывается, когда перетаскиваемый объект находится над целью.
@@ -32,7 +32,7 @@ public interface IDropTarget
/// Этот метод вызывается постоянно, пока пользователь перемещает объект над целью.
/// Реализация может обновить визуальную обратную связь или изменить предлагаемые эффекты.
///
- void DragOver(Models.DropInfo dropInfo);
+ Task DragOverAsync(Models.DropInfo dropInfo, CancellationToken cancellationToken = default);
///
/// Вызывается, когда пользователь сбрасывает данные на цель.
@@ -42,7 +42,7 @@ public interface IDropTarget
/// Этот метод вызывается, когда пользователь отпускает кнопку мыши над целью.
/// Реализация должна обработать принятие данных и выполнить соответствующее действие.
///
- void Drop(Models.DropInfo dropInfo);
+ Task DropAsync(Models.DropInfo dropInfo, CancellationToken cancellationToken = default);
///
/// Вызывается, когда перетаскиваемый объект покидает область цели.
@@ -51,5 +51,5 @@ public interface IDropTarget
/// Этот метод вызывается, когда пользователь перемещает объект за пределы цели.
/// Реализация должна очистить любую визуальную обратную связь, установленную ранее.
///
- void DragLeave();
+ Task DragLeaveAsync(CancellationToken cancellationToken = default);
}
\ No newline at end of file
diff --git a/Lattice.Core.DragDrop/Constants/DragDropConstants.cs b/Lattice.Core.DragDrop/Constants/DragDropConstants.cs
new file mode 100644
index 0000000..6bb931c
--- /dev/null
+++ b/Lattice.Core.DragDrop/Constants/DragDropConstants.cs
@@ -0,0 +1,27 @@
+namespace Lattice.Core.DragDrop.Constants;
+
+///
+/// Константы для системы перетаскивания.
+///
+public static class DragDropConstants
+{
+ ///
+ /// Порог начала перетаскивания по умолчанию (пикселей).
+ ///
+ public const double DefaultDragThreshold = 3.0;
+
+ ///
+ /// Интервал очистки по умолчанию (миллисекунды).
+ ///
+ public const int DefaultCleanupInterval = 60000;
+
+ ///
+ /// Таймаут асинхронных операций по умолчанию (миллисекунды).
+ ///
+ public const int DefaultAsyncTimeout = 5000;
+
+ ///
+ /// Время жизни неиспользуемых целей (минуты).
+ ///
+ public const int TargetLifetimeMinutes = 10;
+}
\ No newline at end of file
diff --git a/Lattice.Core.DragDrop/Enums/DragDropEffects.cs b/Lattice.Core.DragDrop/Enums/DragDropEffects.cs
index 808b92f..9fcc970 100644
--- a/Lattice.Core.DragDrop/Enums/DragDropEffects.cs
+++ b/Lattice.Core.DragDrop/Enums/DragDropEffects.cs
@@ -88,7 +88,7 @@ public static class DragDropEffectsExtensions
///
public static DragDropEffects GetEffectFromKeys(bool controlKey, bool shiftKey, bool altKey)
{
- if (controlKey && altKey)
+ if (controlKey && shiftKey)
return DragDropEffects.Link;
if (controlKey)
return DragDropEffects.Copy;
diff --git a/Lattice.Core.DragDrop/Extensions/DropInfoExtensions.cs b/Lattice.Core.DragDrop/Extensions/DropInfoExtensions.cs
new file mode 100644
index 0000000..f362db6
--- /dev/null
+++ b/Lattice.Core.DragDrop/Extensions/DropInfoExtensions.cs
@@ -0,0 +1,47 @@
+namespace Lattice.Core.DragDrop.Extensions;
+
+///
+/// Методы расширения для DropInfo.
+///
+public static class DropInfoExtensions
+{
+ ///
+ /// Проверяет, могут ли данные быть приведены к указанному типу.
+ ///
+ public static bool CanAccept(this Models.DropInfo dropInfo)
+ where T : class
+ {
+ return dropInfo.Data is T;
+ }
+
+ ///
+ /// Пытается получить данные как указанный тип.
+ ///
+ public static T? GetDataAs(this Models.DropInfo dropInfo)
+ where T : class
+ {
+ return dropInfo.Data as T;
+ }
+
+ ///
+ /// Получает данные как указанный тип или выбрасывает исключение.
+ ///
+ public static T GetRequiredDataAs(this Models.DropInfo dropInfo)
+ where T : class
+ {
+ if (dropInfo.Data is not T data)
+ {
+ throw new InvalidCastException(
+ $"Ожидался тип {typeof(T).Name}, но получен {dropInfo.Data?.GetType().Name ?? "null"}");
+ }
+ return data;
+ }
+
+ ///
+ /// Проверяет, содержится ли позиция в указанных границах.
+ ///
+ public static bool IsInBounds(this Models.DropInfo dropInfo, Geometry.Rect bounds)
+ {
+ return bounds.Contains(dropInfo.Position);
+ }
+}
\ No newline at end of file
diff --git a/Lattice.Core.DragDrop/Models/DragInfo.cs b/Lattice.Core.DragDrop/Models/DragInfo.cs
index 6f2d85f..8547d1e 100644
--- a/Lattice.Core.DragDrop/Models/DragInfo.cs
+++ b/Lattice.Core.DragDrop/Models/DragInfo.cs
@@ -122,6 +122,15 @@ public class DragInfo : IDisposable, ICloneable
public DragInfo(object data, Enums.DragDropEffects allowedEffects, Point startPosition, object? source = null)
{
Data = data ?? throw new ArgumentNullException(nameof(data));
+
+ // Проверка допустимых значений перечисления
+ if (!Enum.IsDefined(typeof(Enums.DragDropEffects), allowedEffects))
+ {
+ throw new ArgumentException(
+ $"Недопустимое значение для {nameof(allowedEffects)}: {allowedEffects}",
+ nameof(allowedEffects));
+ }
+
AllowedEffects = allowedEffects;
StartPosition = startPosition;
Source = source;
@@ -145,7 +154,10 @@ public class DragInfo : IDisposable, ICloneable
{
ThrowIfDisposed();
- var clone = new DragInfo(Data, AllowedEffects, newPosition, Source);
+ var clone = new DragInfo(Data, AllowedEffects, newPosition, Source)
+ {
+ _disposed = false,
+ };
foreach (var kvp in _parameters)
{
@@ -209,6 +221,14 @@ public class DragInfo : IDisposable, ICloneable
{
if (_disposed) return;
+ foreach (var value in _parameters.Values)
+ {
+ if (value is IDisposable disposable)
+ {
+ disposable.Dispose();
+ }
+ }
+
_parameters.Clear();
_disposed = true;
GC.SuppressFinalize(this);
diff --git a/Lattice.Core.DragDrop/Services/DragDropService.cs b/Lattice.Core.DragDrop/Services/DragDropService.cs
index 4e21943..f0a9b99 100644
--- a/Lattice.Core.DragDrop/Services/DragDropService.cs
+++ b/Lattice.Core.DragDrop/Services/DragDropService.cs
@@ -130,7 +130,8 @@ public sealed class DragDropService : IDragDropService
{
// Инициализация таймера очистки (каждые 5 минут)
_cleanupTimer = new Timer(CleanupExpiredTargets, null,
- TimeSpan.FromMinutes(5), TimeSpan.FromMinutes(5));
+ TimeSpan.FromMinutes(Constants.DragDropConstants.TargetLifetimeMinutes / 2),
+ TimeSpan.FromMinutes(Constants.DragDropConstants.TargetLifetimeMinutes / 2));
}
#endregion
@@ -266,7 +267,7 @@ public sealed class DragDropService : IDragDropService
Models.DragInfo? dragInfo = null;
// Проверка возможности начала перетаскивания
- if (EnableAsyncOperations && source is Abstractions.IAsyncDragSource asyncSource)
+ if (EnableAsyncOperations && source is Abstractions.IDragSource asyncSource)
{
var result = await ExecuteWithTimeoutAsync(
asyncSource.CanStartDragAsync(),
@@ -280,8 +281,11 @@ public sealed class DragDropService : IDragDropService
}
else
{
- if (!source.CanStartDrag(out dragInfo) || dragInfo == null)
+ var startDragResult = await source.CanStartDragAsync();
+ if (!startDragResult.CanStart || startDragResult.DragInfo == null)
return false;
+
+ dragInfo = startDragResult.DragInfo;
}
var updatedDragInfo = dragInfo.CloneWithPosition(startPosition);
@@ -289,7 +293,7 @@ public sealed class DragDropService : IDragDropService
// Начало перетаскивания
bool started;
- if (EnableAsyncOperations && source is Abstractions.IAsyncDragSource asyncSource2)
+ if (EnableAsyncOperations && source is Abstractions.IDragSource asyncSource2)
{
started = await ExecuteWithTimeoutAsync(
asyncSource2.StartDragAsync(updatedDragInfo),
@@ -298,7 +302,7 @@ public sealed class DragDropService : IDragDropService
}
else
{
- started = source.StartDrag(updatedDragInfo);
+ started = await source.StartDragAsync(updatedDragInfo);
}
if (!started)
@@ -355,8 +359,8 @@ public sealed class DragDropService : IDragDropService
}
var updatedDragInfo = context.DragInfo.CloneWithPosition(position);
- context.DragInfo.Dispose();
context.DragInfo = updatedDragInfo;
+ context.DragInfo.Dispose();
// Поиск новой цели сброса
var newDropTarget = await FindDropTargetAsync(position, updatedDragInfo);
@@ -369,7 +373,7 @@ public sealed class DragDropService : IDragDropService
await ExecuteTargetOperationAsync(
context.CurrentDropTarget,
t => t.DragLeaveAsync(),
- t => t.DragLeave(),
+ t => t.DragLeaveAsync(),
"DragLeave");
}
@@ -395,7 +399,7 @@ public sealed class DragDropService : IDragDropService
await ExecuteTargetOperationAsync(
context.CurrentDropTarget,
t => t.DragOverAsync(dropInfo),
- t => t.DragOver(dropInfo),
+ t => t.DragOverAsync(dropInfo),
"DragOver");
}
@@ -443,7 +447,7 @@ public sealed class DragDropService : IDragDropService
await ExecuteTargetOperationAsync(
context.CurrentDropTarget,
t => t.DropAsync(dropInfo),
- t => t.Drop(dropInfo),
+ t => t.DropAsync(dropInfo),
"Drop");
if (dropInfo.Handled)
@@ -457,7 +461,7 @@ public sealed class DragDropService : IDragDropService
await ExecuteSourceOperationAsync(
context.Source,
s => s.DragCompletedAsync(context.DragInfo, effects),
- s => s.DragCompleted(context.DragInfo, effects),
+ s => s.DragCompletedAsync(context.DragInfo, effects),
"DragCompleted",
effects);
@@ -504,7 +508,7 @@ public sealed class DragDropService : IDragDropService
await ExecuteSourceOperationAsync(
context.Source,
s => s.DragCancelledAsync(context.DragInfo),
- s => s.DragCancelled(context.DragInfo),
+ s => s.DragCancelledAsync(context.DragInfo),
"DragCancelled");
_dragCancelled?.Invoke(this, new DragCancelledEventArgs(context.DragInfo));
@@ -522,30 +526,6 @@ public sealed class DragDropService : IDragDropService
#endregion
- #region Synchronous Operations (for compatibility)
-
- public bool StartDrag(Abstractions.IDragSource source, Geometry.Point startPosition)
- {
- return Task.Run(() => StartDragAsync(source, startPosition)).GetAwaiter().GetResult();
- }
-
- public void UpdateDrag(Geometry.Point position)
- {
- Task.Run(() => UpdateDragAsync(position)).GetAwaiter().GetResult();
- }
-
- public Enums.DragDropEffects EndDrag(Geometry.Point position)
- {
- return Task.Run(() => EndDragAsync(position)).GetAwaiter().GetResult();
- }
-
- public void CancelDrag()
- {
- Task.Run(CancelDragAsync).GetAwaiter().GetResult();
- }
-
- #endregion
-
#region Utility Methods
public void ClearAllDropTargets()
@@ -593,39 +573,30 @@ public sealed class DragDropService : IDragDropService
_dropTargetsLock.EnterReadLock();
try
{
- foreach (var info in _dropTargets.Values)
- {
- if (!info.Bounds.Contains(position))
- continue;
+ // Фильтруем цели по границам и сортируем по приоритету
+ var candidates = _dropTargets.Values
+ .Where(info => info.Bounds.Contains(position))
+ .OrderByDescending(info => info.Priority)
+ .ToList();
+ foreach (var info in candidates)
+ {
var dropInfo = new Models.DropInfo(
dragInfo.Data,
position,
dragInfo.AllowedEffects,
info.Target);
- bool canAccept;
-
- if (EnableAsyncOperations && info.Target is Abstractions.IAsyncDropTarget asyncTarget)
- {
- canAccept = await ExecuteWithTimeoutAsync(
- asyncTarget.CanAcceptDropAsync(dropInfo),
- "CanAcceptDropAsync",
- info.Target);
- }
- else
- {
- canAccept = info.Target.CanAcceptDrop(dropInfo);
- }
+ bool canAccept = await ExecuteWithTimeoutAsync(
+ info.Target.CanAcceptDropAsync(dropInfo),
+ "CanAcceptDropAsync",
+ info.Target);
if (canAccept)
{
info.LastAccessTime = DateTime.UtcNow;
-
- if (bestTarget == null || info.Priority > bestTarget.Priority)
- {
- bestTarget = info;
- }
+ bestTarget = info;
+ break; // Берем первую подходящую с наивысшим приоритетом
}
}
}
@@ -639,13 +610,13 @@ public sealed class DragDropService : IDragDropService
private async Task ExecuteTargetOperationAsync(
Abstractions.IDropTarget target,
- Func asyncOperation,
+ Func asyncOperation,
Action syncOperation,
string operationName)
{
try
{
- if (EnableAsyncOperations && target is Abstractions.IAsyncDropTarget asyncTarget)
+ if (EnableAsyncOperations && target is Abstractions.IDropTarget asyncTarget)
{
await ExecuteWithTimeoutAsync(
asyncOperation(asyncTarget),
@@ -666,14 +637,14 @@ public sealed class DragDropService : IDragDropService
private async Task ExecuteSourceOperationAsync(
Abstractions.IDragSource source,
- Func asyncOperation,
+ Func asyncOperation,
Action syncOperation,
string operationName,
Enums.DragDropEffects effects = Enums.DragDropEffects.None)
{
try
{
- if (EnableAsyncOperations && source is Abstractions.IAsyncDragSource asyncSource)
+ if (EnableAsyncOperations && source is Abstractions.IDragSource asyncSource)
{
await ExecuteWithTimeoutAsync(
asyncOperation(asyncSource),
@@ -745,16 +716,17 @@ public sealed class DragDropService : IDragDropService
private void CleanupExpiredTargets(object? state)
{
- var expirationTime = DateTime.UtcNow.AddMinutes(-10); // Цели старше 10 минут
+ var expirationTime = DateTime.UtcNow.AddMinutes(-Constants.DragDropConstants.TargetLifetimeMinutes);
_dropTargetsLock.EnterWriteLock();
try
{
var idsToRemove = new List();
+ var currentTarget = _currentDragOperation?.CurrentDropTarget;
foreach (var kvp in _dropTargets)
{
- if (kvp.Value.LastAccessTime < expirationTime)
+ if (kvp.Value.LastAccessTime < expirationTime && !ReferenceEquals(kvp.Value.Target, currentTarget))
{
idsToRemove.Add(kvp.Key);
}
@@ -797,6 +769,9 @@ public sealed class DragDropService : IDragDropService
{
if (_disposed) return;
+ var timer = Interlocked.Exchange(ref _cleanupTimer, null);
+ timer?.Dispose();
+
_cleanupTimer?.Dispose();
_cleanupTimer = null;
diff --git a/Lattice.Core.DragDrop/Services/IDragDropService.cs b/Lattice.Core.DragDrop/Services/IDragDropService.cs
index 9d01977..e8220d9 100644
--- a/Lattice.Core.DragDrop/Services/IDragDropService.cs
+++ b/Lattice.Core.DragDrop/Services/IDragDropService.cs
@@ -121,30 +121,6 @@ public interface IDragDropService : IDisposable
#endregion
- #region Синхронные операции (для обратной совместимости)
-
- ///
- /// Начинает операцию перетаскивания (синхронно).
- ///
- bool StartDrag(Abstractions.IDragSource source, Geometry.Point startPosition);
-
- ///
- /// Обновляет позицию перетаскивания (синхронно).
- ///
- void UpdateDrag(Geometry.Point position);
-
- ///
- /// Завершает операцию перетаскивания (синхронно).
- ///
- Enums.DragDropEffects EndDrag(Geometry.Point position);
-
- ///
- /// Отменяет операцию перетаскивания (синхронно).
- ///
- void CancelDrag();
-
- #endregion
-
#region Утилиты
///
diff --git a/Lattice.Core.DragDrop/Utilities/AsyncDragDropUtilities.cs b/Lattice.Core.DragDrop/Utilities/AsyncDragDropUtilities.cs
index a0f605f..0e31e45 100644
--- a/Lattice.Core.DragDrop/Utilities/AsyncDragDropUtilities.cs
+++ b/Lattice.Core.DragDrop/Utilities/AsyncDragDropUtilities.cs
@@ -12,7 +12,7 @@ public static class AsyncDragDropUtilities
///
/// Создает асинхронную реализацию источника перетаскивания.
///
- public static IAsyncDragSource CreateAsyncDragSource(
+ public static IDragSource CreateAsyncDragSource(
Func> dataProviderAsync,
Func>? canDragAsync = null,
Func? onCompletedAsync = null,
@@ -24,7 +24,7 @@ public static class AsyncDragDropUtilities
///
/// Создает асинхронную реализацию цели сброса.
///
- public static IAsyncDropTarget CreateAsyncDropTarget(
+ public static IDropTarget CreateAsyncDropTarget(
Func>? canAcceptAsync = null,
Func? onDragOverAsync = null,
Func? onDropAsync = null,
@@ -33,28 +33,39 @@ public static class AsyncDragDropUtilities
return new AsyncDropTargetWrapper(canAcceptAsync, onDragOverAsync, onDropAsync, onDragLeaveAsync);
}
- ///
- /// Создает адаптер для преобразования синхронного источника в асинхронный.
- ///
- public static IAsyncDragSource CreateAsyncAdapter(IDragSource syncSource)
- {
- return new SyncToAsyncDragSourceAdapter(syncSource);
- }
+
+ #region Factory Methods
///
- /// Создает адаптер для преобразования синхронной цели в асинхронную.
+ /// Создает информацию о перетаскивании.
///
- 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? 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 Обертки-реализации
///
/// Обертка для создания асинхронного источника перетаскивания.
///
- private sealed class AsyncDragSourceWrapper : IAsyncDragSource
+ private sealed class AsyncDragSourceWrapper : IDragSource
{
private readonly Func> _dataProviderAsync;
private readonly Func>? _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 StartDragAsync(DragInfo dragInfo)
+ public Task 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
}
///
/// Обертка для создания асинхронной цели сброса.
///
- private sealed class AsyncDropTargetWrapper : IAsyncDropTarget
+ private sealed class AsyncDropTargetWrapper : IDropTarget
{
private readonly Func>? _canAcceptAsync;
private readonly Func? _onDragOverAsync;
@@ -177,7 +158,7 @@ public static class AsyncDragDropUtilities
_onDragLeaveAsync = onDragLeaveAsync;
}
- public async Task CanAcceptDropAsync(DropInfo dropInfo)
+ public async Task 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
- }
-
- ///
- /// Адаптер для преобразования синхронного источника в асинхронный.
- ///
- 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 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
- }
-
- ///
- /// Адаптер для преобразования синхронной цели в асинхронную.
- ///
- private sealed class SyncToAsyncDropTargetAdapter : IAsyncDropTarget
- {
- private readonly IDropTarget _syncTarget;
-
- public SyncToAsyncDropTargetAdapter(IDropTarget syncTarget)
- {
- _syncTarget = syncTarget ?? throw new ArgumentNullException(nameof(syncTarget));
- }
-
- public async Task 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 Утилитарные методы
-
- ///
- /// Выполняет асинхронную операцию с таймаутом.
- ///
- public static async Task ExecuteWithTimeoutAsync(
- Task 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);
- }
-
- ///
- /// Выполняет асинхронную операцию с таймаутом.
- ///
- public static async Task 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;
- }
-
- ///
- /// Выполняет асинхронную операцию с таймаутом и обработкой ошибок.
- ///
- public static async Task ExecuteSafeWithTimeoutAsync(
- Task task,
- TimeSpan timeout,
- Func 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;
- }
- }
-
- ///
- /// Создает комбинированный источник из синхронного и асинхронного.
- ///
- public static IAsyncDragSource Combine(
- IDragSource syncSource,
- IAsyncDragSource asyncSource,
- bool preferAsync = true)
- {
- return new CombinedDragSource(syncSource, asyncSource, preferAsync);
- }
-
- ///
- /// Создает комбинированную цель из синхронной и асинхронной.
- ///
- public static IAsyncDropTarget Combine(
- IDropTarget syncTarget,
- IAsyncDropTarget asyncTarget,
- bool preferAsync = true)
- {
- return new CombinedDropTarget(syncTarget, asyncTarget, preferAsync);
- }
-
- #endregion
-
- #region Комбинированные реализации
-
- ///
- /// Комбинированный источник, поддерживающий как синхронный, так и асинхронный API.
- ///
- 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 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
- }
-
- ///
- /// Комбинированная цель, поддерживающая как синхронный, так и асинхронный API.
- ///
- 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 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
}
\ No newline at end of file
diff --git a/Lattice.Core.DragDrop/Utilities/DragDropUtilities.cs b/Lattice.Core.DragDrop/Utilities/DragDropUtilities.cs
deleted file mode 100644
index c1486f7..0000000
--- a/Lattice.Core.DragDrop/Utilities/DragDropUtilities.cs
+++ /dev/null
@@ -1,275 +0,0 @@
-namespace Lattice.Core.DragDrop.Utilities;
-
-///
-/// Утилиты для работы с системой перетаскивания.
-///
-public static class DragDropUtilities
-{
- #region Effect Utilities
-
- ///
- /// Проверяет, совместимы ли эффекты источника и цели.
- ///
- 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;
- }
-
- ///
- /// Получает наиболее подходящий эффект на основе доступных.
- ///
- 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
-
- ///
- /// Вычисляет расстояние между двумя точками.
- ///
- 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);
- }
-
- ///
- /// Проверяет, превысило ли перемещение пороговое значение.
- ///
- public static bool HasExceededDragThreshold(Geometry.Point startPoint, Geometry.Point currentPoint, double threshold)
- {
- var distance = CalculateDistance(startPoint, currentPoint);
- return distance >= threshold;
- }
-
- ///
- /// Определяет позицию сброса относительно прямоугольника.
- ///
- 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
-
- ///
- /// Создает информацию о перетаскивании.
- ///
- public static Models.DragInfo CreateDragInfo(
- object data,
- Geometry.Point startPosition,
- Enums.DragDropEffects allowedEffects = Enums.DragDropEffects.Copy | Enums.DragDropEffects.Move,
- object? source = null,
- Dictionary? 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;
- }
-
- ///
- /// Создает простую реализацию источника перетаскивания.
- ///
- public static Abstractions.IDragSource CreateSimpleDragSource(
- Func
/// Позиция в координатах элемента.
- protected virtual void OnInteractionStarted(Point position)
+ protected virtual async Task OnInteractionStarted(Point position)
{
if (_isDragging)
return;
@@ -115,7 +117,7 @@ public abstract class DragSourceBehaviorBase : IDragSource
/// Обрабатывает перемещение во время взаимодействия.
///
/// Позиция в координатах элемента.
- protected virtual void OnInteractionMoved(Point position)
+ protected virtual async Task OnInteractionMoved(Point position)
{
if (_isDragging)
return;
@@ -123,14 +125,14 @@ public abstract class DragSourceBehaviorBase : IDragSource
var distance = CalculateDistance(_dragStartPosition, position);
if (distance > DragDropService.DragStartThreshold)
{
- StartDragOperation();
+ await StartDragOperation();
}
}
///
/// Обрабатывает завершение взаимодействия.
///
- protected virtual void OnInteractionEnded()
+ protected virtual async Task OnInteractionEnded()
{
// Сброс состояния, если перетаскивание не началось
if (!_isDragging)
@@ -142,11 +144,11 @@ public abstract class DragSourceBehaviorBase : IDragSource
///
/// Обрабатывает отмену взаимодействия.
///
- protected virtual void OnInteractionCancelled()
+ protected virtual async Task OnInteractionCancelled()
{
if (_isDragging)
{
- DragDropService.CancelDrag();
+ await DragDropService.CancelDragAsync();
}
Reset();
}
@@ -154,7 +156,7 @@ public abstract class DragSourceBehaviorBase : IDragSource
///
/// Начинает операцию перетаскивания.
///
- protected virtual void StartDragOperation()
+ protected virtual async Task StartDragOperation()
{
if (_isDragging || AssociatedElement == null)
return;
@@ -163,7 +165,7 @@ public abstract class DragSourceBehaviorBase : IDragSource
var screenPosition = ConvertToScreenCoordinates(_dragStartPosition);
// Начинаем перетаскивание
- _isDragging = DragDropService.StartDrag(this, screenPosition);
+ _isDragging = await DragDropService.StartDragAsync(this, screenPosition);
}
///
@@ -195,30 +197,25 @@ public abstract class DragSourceBehaviorBase : IDragSource
#region IDragSource Implementation
///
- public abstract bool CanStartDrag(out DragInfo? dragInfo);
+ public abstract Task<(bool CanStart, DragInfo? DragInfo)> CanStartDragAsync(CancellationToken ct = default);
///
- public virtual bool StartDrag(DragInfo dragInfo)
+ public virtual async Task StartDragAsync(DragInfo dragInfo, CancellationToken ct = default)
{
- // Базовая реализация всегда разрешает начало перетаскивания
return true;
}
///
- public virtual void DragCompleted(DragInfo dragInfo, Core.DragDrop.Enums.DragDropEffects effects)
+ public virtual async Task DragCompletedAsync(DragInfo dragInfo, Core.DragDrop.Enums.DragDropEffects effects, CancellationToken ct = default)
{
_isDragging = false;
-
- // Оповещаем о завершении перетаскивания
OnDragCompleted(dragInfo, effects);
}
///
- public virtual void DragCancelled(DragInfo dragInfo)
+ public virtual async Task DragCancelledAsync(DragInfo dragInfo, CancellationToken ct = default)
{
_isDragging = false;
-
- // Оповещаем об отмене перетаскивания
OnDragCancelled(dragInfo);
}
diff --git a/Lattice.UI.DragDrop/Behaviors/DropTargetBehaviorBase.cs b/Lattice.UI.DragDrop/Behaviors/DropTargetBehaviorBase.cs
index a38dad3..19fa0ae 100644
--- a/Lattice.UI.DragDrop/Behaviors/DropTargetBehaviorBase.cs
+++ b/Lattice.UI.DragDrop/Behaviors/DropTargetBehaviorBase.cs
@@ -4,6 +4,8 @@ using Lattice.Core.DragDrop.Services;
using Lattice.Core.Geometry;
using Microsoft.Extensions.DependencyInjection;
using System;
+using System.Threading;
+using System.Threading.Tasks;
namespace Lattice.UI.DragDrop.Behaviors;
@@ -176,15 +178,27 @@ public abstract class DropTargetBehaviorBase : IDropTarget
#region IDropTarget Implementation
///
- public abstract bool CanAcceptDrop(DropInfo dropInfo);
+ public abstract Task CanAcceptDropAsync(DropInfo dropInfo, CancellationToken ct = default);
///
- public virtual void DragOver(DropInfo dropInfo)
+ public virtual async Task DragOverAsync(DropInfo dropInfo, CancellationToken ct = default)
{
// Базовая реализация устанавливает эффект по умолчанию
- if (CanAcceptDrop(dropInfo))
+ if (await CanAcceptDropAsync(dropInfo))
{
- dropInfo.SuggestedEffects = Core.DragDrop.Enums.DragDropEffects.Move;
+ // Установить эффект по умолчанию, если он разрешен
+ if (dropInfo.CanAcceptEffect(Core.DragDrop.Enums.DragDropEffects.Move))
+ {
+ dropInfo.SuggestedEffects = Core.DragDrop.Enums.DragDropEffects.Move;
+ }
+ else if (dropInfo.CanAcceptEffect(Core.DragDrop.Enums.DragDropEffects.Copy))
+ {
+ dropInfo.SuggestedEffects = Core.DragDrop.Enums.DragDropEffects.Copy;
+ }
+ else if (dropInfo.CanAcceptEffect(Core.DragDrop.Enums.DragDropEffects.Link))
+ {
+ dropInfo.SuggestedEffects = Core.DragDrop.Enums.DragDropEffects.Link;
+ }
}
else
{
@@ -193,10 +207,10 @@ public abstract class DropTargetBehaviorBase : IDropTarget
}
///
- public abstract void Drop(DropInfo dropInfo);
+ public abstract Task DropAsync(DropInfo dropInfo, CancellationToken ct = default);
///
- public virtual void DragLeave()
+ public virtual async Task DragLeaveAsync(CancellationToken ct = default)
{
// Базовая реализация не делает ничего
}