diff --git a/Lattice.UI.DragDrop/Abstractions/IDragDropHost.cs b/Lattice.UI.DragDrop/Abstractions/IDragDropHost.cs
index 999d9d6..427c590 100644
--- a/Lattice.UI.DragDrop/Abstractions/IDragDropHost.cs
+++ b/Lattice.UI.DragDrop/Abstractions/IDragDropHost.cs
@@ -5,37 +5,77 @@ namespace Lattice.UI.DragDrop.Abstractions;
///
/// Хост для отображения визуальных элементов перетаскивания.
///
+///
+///
+/// Интерфейс предоставляет абстракцию для управления визуальными элементами
+/// перетаскивания в различных UI-фреймворках (WPF, Avalonia, MAUI и т.д.).
+///
+///
+/// Реализация должна обеспечивать корректное отображение визуальных элементов
+/// поверх других элементов UI и их своевременное удаление при завершении операций.
+///
+///
public interface IDragDropHost
{
///
- /// Показывает визуальное представление перетаскивания.
+ /// Показывает визуальное представление перетаскиваемого элемента.
///
- /// Визуальное представление.
- /// Начальная позиция.
+ /// Визуальное представление элемента для перетаскивания.
+ /// Начальная позиция визуального элемента в экранных координатах.
+ ///
+ ///
+ /// Метод должен отобразить переданный визуальный элемент в указанной позиции.
+ /// Визуальный элемент должен следовать за курсором мыши при обновлении через .
+ ///
+ ///
+ /// Визуальный элемент должен отображаться поверх всех других элементов интерфейса.
+ ///
+ ///
void ShowDragVisual(object dragVisual, Point position);
///
/// Обновляет позицию визуального представления перетаскивания.
///
- /// Визуальное представление.
- /// Новая позиция.
+ /// Визуальное представление, позиция которого должна быть обновлена.
+ /// Новая позиция в экранных координатах.
+ ///
+ /// Метод должен обновлять позицию уже отображаемого визуального элемента
+ /// с минимальной задержкой для плавного перемещения.
+ ///
void UpdateDragVisualPosition(object dragVisual, Point position);
///
/// Скрывает визуальное представление перетаскивания.
///
- /// Визуальное представление.
+ /// Визуальное представление для скрытия.
+ ///
+ /// После вызова этого метода визуальный элемент должен быть полностью
+ /// удален из визуального дерева и его ресурсы освобождены.
+ ///
void HideDragVisual(object dragVisual);
///
/// Показывает визуальную обратную связь для цели сброса.
///
- /// Элемент обратной связи.
+ /// Элемент обратной связи для отображения.
+ ///
+ ///
+ /// Метод должен отобразить элемент обратной связи (например, подсветку, рамку или индикатор позиции)
+ /// для визуального указания возможности сброса на целевой элемент.
+ ///
+ ///
+ /// Элемент обратной связи должен отображаться поверх целевого элемента, но под перетаскиваемым визуальным элементом.
+ ///
+ ///
void ShowDropAdorner(IDropVisualAdorner adorner);
///
/// Скрывает визуальную обратную связь для цели сброса.
///
- /// Элемент обратной связи.
+ /// Элемент обратной связи для скрытия.
+ ///
+ /// После вызова этого метода элемент обратной связи должен быть
+ /// удален из визуального дерева и его ресурсы освобождены.
+ ///
void HideDropAdorner(IDropVisualAdorner adorner);
}
\ No newline at end of file
diff --git a/Lattice.UI.DragDrop/Abstractions/IDragVisualProvider.cs b/Lattice.UI.DragDrop/Abstractions/IDragVisualProvider.cs
index dbdc393..e307125 100644
--- a/Lattice.UI.DragDrop/Abstractions/IDragVisualProvider.cs
+++ b/Lattice.UI.DragDrop/Abstractions/IDragVisualProvider.cs
@@ -6,26 +6,62 @@ namespace Lattice.UI.DragDrop.Abstractions;
///
/// Поставщик визуального представления для перетаскиваемого элемента.
///
+///
+///
+/// Интерфейс предоставляет абстракцию для создания и управления визуальными
+/// представлениями элементов при операции перетаскивания.
+///
+///
+/// Реализации могут предоставлять различные стили визуального представления:
+/// от простого клонирования оригинального элемента до сложных анимированных представлений.
+///
+///
public interface IDragVisualProvider
{
///
- /// Создает визуальное представление для перетаскивания.
+ /// Создает визуальное представление для перетаскивания на основе информации о перетаскивании.
///
- /// Информация о перетаскивании.
+ /// Информация о перетаскивании, содержащая данные и метаданные операции.
/// Начальная позиция в экранных координатах.
- /// Объект, представляющий визуальное отображение.
- object CreateDragVisual(DragInfo dragInfo, Point initialPosition);
+ /// Объект, представляющий визуальное отображение для перетаскивания.
+ ///
+ ///
+ /// Созданный визуальный элемент должен:
+ /// 1. Отображать репрезентативное представление перетаскиваемых данных
+ /// 2. Иметь прозрачный фон или альфа-канал для плавного отображения
+ /// 3. Быть легковесным для обеспечения плавной анимации
+ /// 4. Поддерживать возможность изменения позиции через
+ ///
+ ///
+ /// Метод может возвращать null, если визуальное представление не требуется.
+ ///
+ ///
+ object? CreateDragVisual(DragInfo dragInfo, Point initialPosition);
///
- /// Обновляет позицию визуального представления.
+ /// Обновляет позицию визуального представления перетаскивания.
///
- /// Визуальное представление.
- /// Новая позиция.
+ /// Визуальное представление, созданное методом .
+ /// Новая позиция в экранных координатах.
+ ///
+ /// Метод должен обновить позицию визуального элемента максимально эффективно,
+ /// так как он вызывается часто во время операции перетаскивания.
+ ///
void UpdateDragVisualPosition(object dragVisual, Point position);
///
/// Освобождает ресурсы визуального представления.
///
- /// Визуальное представление.
+ /// Визуальное представление для освобождения.
+ ///
+ ///
+ /// Метод должен освободить все ресурсы, связанные с визуальным представлением,
+ /// включая графические ресурсы, подписки на события и временные данные.
+ ///
+ ///
+ /// Этот метод гарантированно вызывается после завершения операции перетаскивания,
+ /// независимо от её успешности.
+ ///
+ ///
void ReleaseDragVisual(object dragVisual);
}
\ No newline at end of file
diff --git a/Lattice.UI.DragDrop/Abstractions/IDropVisualAdorner.cs b/Lattice.UI.DragDrop/Abstractions/IDropVisualAdorner.cs
index 1affbdd..49f0b47 100644
--- a/Lattice.UI.DragDrop/Abstractions/IDropVisualAdorner.cs
+++ b/Lattice.UI.DragDrop/Abstractions/IDropVisualAdorner.cs
@@ -6,23 +6,54 @@ namespace Lattice.UI.DragDrop.Abstractions;
///
/// Визуальный элемент, показывающий обратную связь при наведении на цель сброса.
///
+///
+///
+/// Интерфейс предоставляет абстракцию для визуальных индикаторов, которые
+/// показывают пользователю возможность сброса данных на целевом элементе.
+///
+///
+/// Реализации могут предоставлять различные типы визуальной обратной связи:
+/// подсветку элемента, отображение индикатора позиции, изменение курсора и т.д.
+///
+///
public interface IDropVisualAdorner
{
///
/// Показывает визуальную обратную связь для цели сброса.
///
- /// Информация о сбросе.
- /// Границы цели.
+ /// Информация о потенциальном сбросе, включая данные и позицию.
+ /// Границы целевого элемента в экранных координатах.
+ ///
+ ///
+ /// Метод должен отобразить визуальную обратную связь, соответствующую типу данных
+ /// и контексту сброса. Обратная связь должна явно указывать на возможность
+ /// сброса и ожидаемый эффект (копирование, перемещение и т.д.).
+ ///
+ ///
+ /// Обратная связь должна учитывать свойства
+ /// и для кастомизации отображения.
+ ///
+ ///
void Show(DropInfo dropInfo, Rect targetBounds);
///
/// Обновляет позицию и состояние визуальной обратной связи.
///
- /// Информация о сбросе.
+ /// Текущая информация о сбросе, включая обновленную позицию и состояние.
+ ///
+ /// Метод вызывается при каждом перемещении курсора над целью и должен
+ /// обновлять визуальную обратную связь в соответствии с новой позицией
+ /// и состоянием операции.
+ ///
void Update(DropInfo dropInfo);
///
/// Скрывает визуальную обратную связь.
///
+ ///
+ /// Метод должен скрыть и очистить все визуальные элементы обратной связи.
+ /// После вызова этого метода ресурсы могут быть освобождены или переиспользованы
+ /// для следующих операций.
+ ///
void Hide();
}
\ No newline at end of file
diff --git a/Lattice.UI.DragDrop/Behaviors/DragSourceBehaviorBase.cs b/Lattice.UI.DragDrop/Behaviors/DragSourceBehaviorBase.cs
index d21c917..4a2c91f 100644
--- a/Lattice.UI.DragDrop/Behaviors/DragSourceBehaviorBase.cs
+++ b/Lattice.UI.DragDrop/Behaviors/DragSourceBehaviorBase.cs
@@ -10,9 +10,20 @@ using System.Threading.Tasks;
namespace Lattice.UI.DragDrop.Behaviors;
///
-/// Базовый класс поведения источника перетаскивания.
+/// Базовый класс поведения источника перетаскивания для UI элементов.
///
-/// Тип UI элемента.
+/// Тип UI элемента, к которому прикрепляется поведение.
+///
+///
+/// Этот класс предоставляет базовую реализацию поведения перетаскивания для UI элементов.
+/// Он обрабатывает события мыши/тач, управляет порогом начала перетаскивания и
+/// интегрируется с сервисом из ядра.
+///
+///
+/// Производные классы должны реализовать абстрактные методы для конкретной
+/// UI-платформы и предоставить логику создания информации о перетаскивании.
+///
+///
public abstract class DragSourceBehaviorBase : IDragSource
where TElement : class
{
@@ -20,10 +31,15 @@ public abstract class DragSourceBehaviorBase : IDragSource
private Point _dragStartPosition;
private bool _isDragging;
private TElement? _associatedElement;
+ private CancellationTokenSource? _dragCancellationTokenSource;
///
- /// Получает или задает связанный элемент.
+ /// Получает или задает связанный UI элемент.
///
+ ///
+ /// Элемент UI, к которому прикреплено поведение перетаскивания.
+ /// При изменении значения автоматически выполняется переподключение событий.
+ ///
protected TElement? AssociatedElement
{
get => _associatedElement;
@@ -39,8 +55,12 @@ public abstract class DragSourceBehaviorBase : IDragSource
}
///
- /// Получает сервис перетаскивания.
+ /// Получает сервис перетаскивания из контейнера зависимостей.
///
+ ///
+ /// Экземпляр , используемый для управления операциями перетаскивания.
+ /// При первом обращении выполняется получение сервиса из .
+ ///
protected IDragDropService DragDropService
{
get
@@ -54,22 +74,34 @@ public abstract class DragSourceBehaviorBase : IDragSource
}
///
- /// Получает провайдер сервисов.
+ /// Получает провайдер сервисов для разрешения зависимостей.
///
protected IServiceProvider ServiceProvider { get; }
+ ///
+ /// Получает значение, указывающее, выполняется ли в данный момент операция перетаскивания.
+ ///
+ protected bool IsDragging => _isDragging;
+
///
/// Инициализирует новый экземпляр класса .
///
- /// Провайдер сервисов.
+ /// Провайдер сервисов для разрешения зависимостей.
+ ///
+ /// Выбрасывается, когда равен null.
+ ///
protected DragSourceBehaviorBase(IServiceProvider serviceProvider)
{
ServiceProvider = serviceProvider ?? throw new ArgumentNullException(nameof(serviceProvider));
}
///
- /// Вызывается при прикреплении к элементу.
+ /// Вызывается при прикреплении поведения к элементу.
///
+ ///
+ /// Реализация по умолчанию подписывается на события элемента через .
+ /// Производные классы могут переопределить этот метод для дополнительной инициализации.
+ ///
protected virtual void AttachToElement()
{
if (_associatedElement != null)
@@ -79,8 +111,12 @@ public abstract class DragSourceBehaviorBase : IDragSource
}
///
- /// Вызывается при откреплении от элемента.
+ /// Вызывается при откреплении поведения от элемента.
///
+ ///
+ /// Реализация по умолчанию отписывается от событий элемента через .
+ /// Производные классы могут переопределить этот метод для дополнительной очистки.
+ ///
protected virtual void DetachFromElement()
{
if (_associatedElement != null)
@@ -90,36 +126,68 @@ public abstract class DragSourceBehaviorBase : IDragSource
}
///
- /// Подписывается на события элемента.
+ /// Подписывается на события элемента, необходимые для отслеживания начала перетаскивания.
///
- /// Элемент.
+ /// Элемент, к событиям которого нужно подписаться.
+ ///
+ /// Производные классы должны реализовать этот метод для подписки на события конкретной
+ /// UI-платформы (например, MouseDown для WPF, PointerPressed для Avalonia).
+ ///
protected abstract void SubscribeToEvents(TElement element);
///
/// Отписывается от событий элемента.
///
- /// Элемент.
+ /// Элемент, от событий которого нужно отписаться.
+ ///
+ /// Производные классы должны реализовать этот метод для корректной отписки
+ /// от событий, на которые была выполнена подписка в .
+ ///
protected abstract void UnsubscribeFromEvents(TElement element);
///
- /// Обрабатывает начало взаимодействия (например, нажатие мыши).
+ /// Обрабатывает начало взаимодействия с элементом (например, нажатие кнопки мыши).
///
- /// Позиция в координатах элемента.
- protected virtual async Task OnInteractionStarted(Point position)
+ /// Позиция взаимодействия в координатах элемента.
+ /// Задача, представляющая асинхронную операцию.
+ ///
+ ///
+ /// Этот метод вызывается из обработчиков событий UI-платформы при начале
+ /// взаимодействия, которое может привести к перетаскиванию.
+ ///
+ ///
+ /// Реализация по умолчанию сохраняет начальную позицию для последующей
+ /// проверки порога перетаскивания.
+ ///
+ ///
+ protected virtual Task OnInteractionStarted(Point position)
{
if (_isDragging)
- return;
+ return Task.CompletedTask;
_dragStartPosition = position;
+ _dragCancellationTokenSource = new CancellationTokenSource();
+ return Task.CompletedTask;
}
///
- /// Обрабатывает перемещение во время взаимодействия.
+ /// Обрабатывает перемещение во время взаимодействия с элементом.
///
- /// Позиция в координатах элемента.
+ /// Текущая позиция взаимодействия в координатах элемента.
+ /// Задача, представляющая асинхронную операцию.
+ ///
+ ///
+ /// Этот метод вызывается при перемещении курсора/тач-точки во время удержания
+ /// взаимодействия (например, перемещение мыши с нажатой кнопкой).
+ ///
+ ///
+ /// Реализация по умолчанию проверяет, превышено ли расстояние от начальной
+ /// точки порога перетаскивания, и если да - начинает операцию перетаскивания.
+ ///
+ ///
protected virtual async Task OnInteractionMoved(Point position)
{
- if (_isDragging)
+ if (_isDragging || AssociatedElement == null)
return;
var distance = CalculateDistance(_dragStartPosition, position);
@@ -130,20 +198,41 @@ public abstract class DragSourceBehaviorBase : IDragSource
}
///
- /// Обрабатывает завершение взаимодействия.
+ /// Обрабатывает завершение взаимодействия с элементом.
///
- protected virtual async Task OnInteractionEnded()
+ /// Задача, представляющая асинхронную операцию.
+ ///
+ ///
+ /// Этот метод вызывается при завершении взаимодействия (например, отпускании кнопки мыши).
+ ///
+ ///
+ /// Реализация по умолчанию сбрасывает состояние поведения, если перетаскивание не было начато.
+ ///
+ ///
+ protected virtual Task OnInteractionEnded()
{
// Сброс состояния, если перетаскивание не началось
if (!_isDragging)
{
Reset();
}
+ return Task.CompletedTask;
}
///
- /// Обрабатывает отмену взаимодействия.
+ /// Обрабатывает отмену взаимодействия с элементом.
///
+ /// Задача, представляющая асинхронную операцию.
+ ///
+ ///
+ /// Этот метод вызывается при отмене взаимодействия (например, нажатии клавиши Escape
+ /// или выходе за пределы допустимой области).
+ ///
+ ///
+ /// Реализация по умолчанию отменяет текущую операцию перетаскивания, если она активна,
+ /// и сбрасывает состояние поведения.
+ ///
+ ///
protected virtual async Task OnInteractionCancelled()
{
if (_isDragging)
@@ -156,16 +245,35 @@ public abstract class DragSourceBehaviorBase : IDragSource
///
/// Начинает операцию перетаскивания.
///
+ /// Задача, представляющая асинхронную операцию.
+ ///
+ ///
+ /// Этот метод преобразует начальную позицию в экранные координаты и вызывает
+ /// сервис перетаскивания для начала операции.
+ ///
+ ///
+ /// Операция начинается только если поведение прикреплено к элементу и
+ /// не выполняется другая операция перетаскивания.
+ ///
+ ///
protected virtual async Task StartDragOperation()
{
- if (_isDragging || AssociatedElement == null)
+ if (_isDragging || AssociatedElement == null || _dragCancellationTokenSource == null)
return;
// Получаем начальную позицию в экранных координатах
var screenPosition = ConvertToScreenCoordinates(_dragStartPosition);
// Начинаем перетаскивание
- _isDragging = await DragDropService.StartDragAsync(this, screenPosition);
+ try
+ {
+ _isDragging = await DragDropService.StartDragAsync(this, screenPosition);
+ }
+ catch (OperationCanceledException)
+ {
+ // Операция была отменена
+ Reset();
+ }
}
///
@@ -173,11 +281,18 @@ public abstract class DragSourceBehaviorBase : IDragSource
///
/// Точка в координатах элемента.
/// Точка в экранных координатах.
+ ///
+ /// Производные классы должны реализовать этот метод для преобразования
+ /// координат в соответствии с конкретной UI-платформой.
+ ///
protected abstract Point ConvertToScreenCoordinates(Point point);
///
/// Вычисляет расстояние между двумя точками.
///
+ /// Первая точка.
+ /// Вторая точка.
+ /// Расстояние между точками.
protected virtual double CalculateDistance(Point p1, Point p2)
{
var dx = p2.X - p1.X;
@@ -188,32 +303,33 @@ public abstract class DragSourceBehaviorBase : IDragSource
///
/// Сбрасывает состояние поведения.
///
+ ///
+ /// Этот метод очищает все временные данные и отменяет токены отмены,
+ /// связанные с текущей операцией перетаскивания.
+ ///
protected virtual void Reset()
{
_isDragging = false;
_dragStartPosition = default;
+
+ _dragCancellationTokenSource?.Dispose();
+ _dragCancellationTokenSource = null;
}
#region IDragSource Implementation
///
- public abstract Task<(bool CanStart, DragInfo? DragInfo)> CanStartDragAsync(CancellationToken ct = default);
+ public abstract Task TryStartDragAsync(Point startPosition, CancellationToken cancellationToken = default);
///
- public virtual async Task StartDragAsync(DragInfo dragInfo, CancellationToken ct = default)
- {
- return true;
- }
-
- ///
- public virtual async Task DragCompletedAsync(DragInfo dragInfo, Core.DragDrop.Enums.DragDropEffects effects, CancellationToken ct = default)
+ public async Task OnDragCompletedAsync(DragInfo dragInfo, Lattice.Core.DragDrop.Enums.DragDropEffects effects, CancellationToken cancellationToken = default)
{
_isDragging = false;
OnDragCompleted(dragInfo, effects);
}
///
- public virtual async Task DragCancelledAsync(DragInfo dragInfo, CancellationToken ct = default)
+ public async Task OnDragCancelledAsync(DragInfo dragInfo, CancellationToken cancellationToken = default)
{
_isDragging = false;
OnDragCancelled(dragInfo);
@@ -224,18 +340,27 @@ public abstract class DragSourceBehaviorBase : IDragSource
#region Virtual Methods for Derived Classes
///
- /// Вызывается при успешном завершении перетаскивания.
+ /// Вызывается при успешном завершении операции перетаскивания.
///
- /// Информация о перетаскивании.
- /// Примененные эффекты.
- protected virtual void OnDragCompleted(DragInfo dragInfo, Core.DragDrop.Enums.DragDropEffects effects)
+ /// Информация о перетаскивании, использованная в операции.
+ /// Эффекты, примененные при завершении операции.
+ ///
+ /// Производные классы могут переопределить этот метод для выполнения
+ /// дополнительных действий после успешного завершения перетаскивания,
+ /// например, удаления исходного элемента при перемещении.
+ ///
+ protected virtual void OnDragCompleted(DragInfo dragInfo, Lattice.Core.DragDrop.Enums.DragDropEffects effects)
{
}
///
- /// Вызывается при отмене перетаскивания.
+ /// Вызывается при отмене операции перетаскивания.
///
- /// Информация о перетаскивании.
+ /// Информация о перетаскивании, использованная в операции.
+ ///
+ /// Производные классы могут переопределить этот метод для выполнения
+ /// действий по восстановлению состояния после отмены перетаскивания.
+ ///
protected virtual void OnDragCancelled(DragInfo dragInfo)
{
}
@@ -243,11 +368,16 @@ public abstract class DragSourceBehaviorBase : IDragSource
#endregion
///
- /// Освобождает ресурсы.
+ /// Открепляет поведение от элемента и освобождает ресурсы.
///
+ ///
+ /// После вызова этого метода поведение больше не будет обрабатывать события
+ /// элемента и может быть безопасно удалено.
+ ///
public virtual void Detach()
{
DetachFromElement();
_associatedElement = null;
+ Reset();
}
}
\ No newline at end of file
diff --git a/Lattice.UI.DragDrop/Behaviors/DropTargetBehaviorBase.cs b/Lattice.UI.DragDrop/Behaviors/DropTargetBehaviorBase.cs
index 19fa0ae..135f7b1 100644
--- a/Lattice.UI.DragDrop/Behaviors/DropTargetBehaviorBase.cs
+++ b/Lattice.UI.DragDrop/Behaviors/DropTargetBehaviorBase.cs
@@ -10,9 +10,20 @@ using System.Threading.Tasks;
namespace Lattice.UI.DragDrop.Behaviors;
///
-/// Базовый класс поведения цели сброса.
+/// Базовый класс поведения цели сброса для UI элементов.
///
-/// Тип UI элемента.
+/// Тип UI элемента, к которому прикрепляется поведение.
+///
+///
+/// Этот класс предоставляет базовую реализацию поведения цели сброса для UI элементов.
+/// Он автоматически регистрирует элемент в сервисе перетаскивания, обновляет его границы
+/// при изменении размера или позиции и предоставляет методы для обработки событий сброса.
+///
+///
+/// Производные классы должны реализовать абстрактные методы для конкретной
+/// UI-платформы и предоставить логику проверки и обработки сбрасываемых данных.
+///
+///
public abstract class DropTargetBehaviorBase : IDropTarget
where TElement : class
{
@@ -22,8 +33,12 @@ public abstract class DropTargetBehaviorBase : IDropTarget
private Rect _currentBounds;
///
- /// Получает или задает связанный элемент.
+ /// Получает или задает связанный UI элемент.
///
+ ///
+ /// Элемент UI, к которому прикреплено поведение цели сброса.
+ /// При изменении значения автоматически выполняется перерегистрация в сервисе перетаскивания.
+ ///
protected TElement? AssociatedElement
{
get => _associatedElement;
@@ -41,16 +56,28 @@ public abstract class DropTargetBehaviorBase : IDropTarget
///
/// Получает или задает приоритет цели сброса.
///
+ ///
+ /// Цели с более высоким приоритетом проверяются первыми при нахождении курсора
+ /// в области нескольких целей. Значение по умолчанию: 0.
+ ///
public int Priority { get; set; }
///
/// Получает или задает группу цели сброса.
///
+ ///
+ /// Имя группы для группового управления целями сброса. Может использоваться
+ /// для массовой отмены регистрации целей или применения общих настроек.
+ ///
public string? Group { get; set; }
///
- /// Получает сервис перетаскивания.
+ /// Получает сервис перетаскивания из контейнера зависимостей.
///
+ ///
+ /// Экземпляр , используемый для регистрации цели сброса.
+ /// При первом обращении выполняется получение сервиса из .
+ ///
protected IDragDropService DragDropService
{
get
@@ -64,27 +91,47 @@ public abstract class DropTargetBehaviorBase : IDropTarget
}
///
- /// Получает провайдер сервисов.
+ /// Получает провайдер сервисов для разрешения зависимостей.
///
protected IServiceProvider ServiceProvider { get; }
///
/// Получает текущие границы элемента в экранных координатах.
///
+ ///
+ /// Прямоугольник, описывающий границы элемента в экранных координатах.
+ /// Значение автоматически обновляется при изменении размера или позиции элемента.
+ ///
protected Rect CurrentBounds => _currentBounds;
+ ///
+ /// Получает уникальный идентификатор регистрации цели в сервисе перетаскивания.
+ ///
+ ///
+ /// Идентификатор, возвращенный методом ,
+ /// или null, если цель не зарегистрирована.
+ ///
+ protected string? RegistrationId => _registrationId;
+
///
/// Инициализирует новый экземпляр класса .
///
- /// Провайдер сервисов.
+ /// Провайдер сервисов для разрешения зависимостей.
+ ///
+ /// Выбрасывается, когда равен null.
+ ///
protected DropTargetBehaviorBase(IServiceProvider serviceProvider)
{
ServiceProvider = serviceProvider ?? throw new ArgumentNullException(nameof(serviceProvider));
}
///
- /// Вызывается при прикреплении к элементу.
+ /// Вызывается при прикреплении поведения к элементу.
///
+ ///
+ /// Реализация по умолчанию подписывается на события элемента, обновляет границы
+ /// и регистрирует цель в сервисе перетаскивания.
+ ///
protected virtual void AttachToElement()
{
if (_associatedElement != null)
@@ -96,8 +143,12 @@ public abstract class DropTargetBehaviorBase : IDropTarget
}
///
- /// Вызывается при откреплении от элемента.
+ /// Вызывается при откреплении поведения от элемента.
///
+ ///
+ /// Реализация по умолчанию отписывается от событий элемента и отменяет
+ /// регистрацию цели в сервисе перетаскивания.
+ ///
protected virtual void DetachFromElement()
{
if (_associatedElement != null)
@@ -108,20 +159,32 @@ public abstract class DropTargetBehaviorBase : IDropTarget
}
///
- /// Подписывается на события элемента.
+ /// Подписывается на события элемента, необходимые для отслеживания изменений размера и позиции.
///
- /// Элемент.
+ /// Элемент, к событиям которого нужно подписаться.
+ ///
+ /// Производные классы должны реализовать этот метод для подписки на события конкретной
+ /// UI-платформы (например, SizeChanged, LayoutUpdated для WPF).
+ ///
protected abstract void SubscribeToEvents(TElement element);
///
/// Отписывается от событий элемента.
///
- /// Элемент.
+ /// Элемент, от событий которого нужно отписаться.
+ ///
+ /// Производные классы должны реализовать этот метод для корректной отписки
+ /// от событий, на которые была выполнена подписка в .
+ ///
protected abstract void UnsubscribeFromEvents(TElement element);
///
/// Обновляет границы элемента в экранных координатах.
///
+ ///
+ /// Этот метод вызывается при изменении размера или позиции элемента для
+ /// обновления области, в которой цель может принимать сбрасываемые данные.
+ ///
protected virtual void UpdateBounds()
{
if (_associatedElement != null)
@@ -139,13 +202,27 @@ public abstract class DropTargetBehaviorBase : IDropTarget
///
/// Получает границы элемента в экранных координатах.
///
- /// Элемент.
- /// Границы в экранных координатах.
+ /// Элемент, границы которого нужно получить.
+ /// Границы элемента в экранных координатах.
+ ///
+ /// Производные классы должны реализовать этот метод для получения границ
+ /// элемента в соответствии с конкретной UI-платформой.
+ ///
protected abstract Rect GetScreenBounds(TElement element);
///
/// Регистрирует цель в сервисе перетаскивания.
///
+ ///
+ ///
+ /// Этот метод регистрирует текущий объект как цель сброса в сервисе перетаскивания
+ /// с указанными приоритетом и группой.
+ ///
+ ///
+ /// Регистрация выполняется только если поведение прикреплено к элементу и
+ /// цель еще не зарегистрирована.
+ ///
+ ///
protected virtual void RegisterToService()
{
if (_associatedElement != null && _registrationId == null)
@@ -158,6 +235,10 @@ public abstract class DropTargetBehaviorBase : IDropTarget
///
/// Отменяет регистрацию цели в сервисе перетаскивания.
///
+ ///
+ /// Этот метод отменяет регистрацию цели сброса, освобождая ресурсы
+ /// в сервисе перетаскивания и предотвращая дальнейшую обработку событий.
+ ///
protected virtual void UnregisterFromService()
{
if (_registrationId != null)
@@ -170,6 +251,10 @@ public abstract class DropTargetBehaviorBase : IDropTarget
///
/// Вызывается при изменении размера или позиции элемента.
///
+ ///
+ /// Производные классы должны вызывать этот метод из обработчиков событий
+ /// изменения размера или позиции элемента для обновления границ цели.
+ ///
protected virtual void OnElementLayoutChanged()
{
UpdateBounds();
@@ -181,7 +266,7 @@ public abstract class DropTargetBehaviorBase : IDropTarget
public abstract Task CanAcceptDropAsync(DropInfo dropInfo, CancellationToken ct = default);
///
- public virtual async Task DragOverAsync(DropInfo dropInfo, CancellationToken ct = default)
+ public virtual async Task OnDragOverAsync(DropInfo dropInfo, CancellationToken ct = default)
{
// Базовая реализация устанавливает эффект по умолчанию
if (await CanAcceptDropAsync(dropInfo))
@@ -207,22 +292,58 @@ public abstract class DropTargetBehaviorBase : IDropTarget
}
///
- public abstract Task DropAsync(DropInfo dropInfo, CancellationToken ct = default);
+ public abstract Task OnDropAsync(DropInfo dropInfo, CancellationToken cancellationToken = default);
///
- public virtual async Task DragLeaveAsync(CancellationToken ct = default)
+ public virtual Task OnDragLeaveAsync(CancellationToken cancellationToken = default)
{
- // Базовая реализация не делает ничего
+ // Базовая реализация не выполняет действий при выходе курсора из области цели
+ return Task.CompletedTask;
}
#endregion
///
- /// Освобождает ресурсы.
+ /// Открепляет поведение от элемента и освобождает все связанные ресурсы.
///
+ ///
+ ///
+ /// Этот метод выполняет следующие действия:
+ ///
+ ///
+ /// - Отписывается от всех событий связанного элемента
+ /// - Отменяет регистрацию цели в сервисе перетаскивания
+ /// - Освобождает ссылку на связанный элемент
+ /// - Очищает все временные данные и состояние
+ ///
+ ///
+ /// После вызова этого метода поведение больше не будет обрабатывать события
+ /// элемента и не будет реагировать на операции перетаскивания. Поведение
+ /// можно безопасно удалить после вызова этого метода.
+ ///
+ ///
+ /// Важно: Этот метод должен быть вызван перед удалением
+ /// элемента из визуального дерева или перед заменой поведения, чтобы
+ /// предотвратить утечки памяти и непредсказуемое поведение системы.
+ ///
+ ///
+ ///
+ /// // Пример использования
+ /// var dropBehavior = new MyDropTargetBehavior(serviceProvider);
+ /// dropBehavior.AssociatedElement = myElement;
+ ///
+ /// // ... использование поведения ...
+ ///
+ /// // Перед удалением элемента или поведения
+ /// dropBehavior.Detach();
+ ///
+ ///
+ ///
public virtual void Detach()
{
DetachFromElement();
_associatedElement = null;
+ _registrationId = null;
+ _currentBounds = default;
}
}
\ No newline at end of file
diff --git a/Lattice.UI.DragDrop/Extensions/ServiceCollectionExtensions.cs b/Lattice.UI.DragDrop/Extensions/ServiceCollectionExtensions.cs
index 54715da..b6223bb 100644
--- a/Lattice.UI.DragDrop/Extensions/ServiceCollectionExtensions.cs
+++ b/Lattice.UI.DragDrop/Extensions/ServiceCollectionExtensions.cs
@@ -1,52 +1,194 @@
-using Lattice.UI.DragDrop.Abstractions;
+using Lattice.Core.DragDrop.Services;
+using Lattice.UI.DragDrop.Abstractions;
using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.DependencyInjection.Extensions;
namespace Lattice.UI.DragDrop.Extensions;
///
-/// Методы расширения для регистрации сервисов перетаскивания.
+/// Методы расширения для регистрации сервисов перетаскивания в контейнере зависимостей.
///
public static class ServiceCollectionExtensions
{
///
- /// Добавляет сервисы перетаскивания.
+ /// Добавляет сервисы перетаскивания в контейнер зависимостей.
///
+ /// Коллекция сервисов для регистрации.
+ /// Коллекция сервисов с зарегистрированными сервисами перетаскивания.
+ ///
+ ///
+ /// Этот метод регистрирует основные сервисы перетаскивания, необходимые для работы системы:
+ ///
+ ///
+ /// - Основной сервис управления перетаскиванием ()
+ /// - Абстракции для визуального представления и обратной связи
+ /// - Провайдеры по умолчанию с безопасной реализацией
+ ///
+ ///
+ /// Для полноценной работы в конкретной UI-платформе (WPF, Avalonia, MAUI и т.д.)
+ /// необходимо переопределить регистрации платформенными реализациями.
+ ///
+ ///
public static IServiceCollection AddLatticeDragDrop(this IServiceCollection services)
{
- // Регистрируем абстракции, которые будут реализованы в платформенных проектах
- services.AddSingleton(typeof(IDragVisualProvider), typeof(DefaultDragVisualProvider));
- services.AddSingleton(typeof(IDropVisualAdorner), typeof(DefaultDropVisualAdorner));
- services.AddSingleton(typeof(IDragDropHost), typeof(DefaultDragDropHost));
+ // Регистрация основного сервиса перетаскивания
+ services.TryAddSingleton();
+
+ // Регистрация UI-специфичных абстракций с реализациями по умолчанию
+ // Эти реализации безопасны (ничего не делают), но могут быть переопределены платформенными реализациями
+ services.TryAddSingleton();
+ services.TryAddSingleton();
+ services.TryAddSingleton();
return services;
}
///
- /// Реализация по умолчанию для платформ, которые еще не имеют своей реализации.
+ /// Добавляет или заменяет реализацию поставщика визуального представления перетаскивания.
///
+ /// Тип поставщика визуального представления.
+ /// Коллекция сервисов.
+ /// Коллекция сервисов.
+ ///
+ /// Используйте этот метод для регистрации платформенной реализации поставщика визуального представления.
+ ///
+ public static IServiceCollection AddDragVisualProvider(this IServiceCollection services)
+ where TProvider : class, IDragVisualProvider
+ {
+ services.Replace(ServiceDescriptor.Singleton());
+ return services;
+ }
+
+ ///
+ /// Добавляет или заменяет реализацию визуальной обратной связи для цели сброса.
+ ///
+ /// Тип элемента визуальной обратной связи.
+ /// Коллекция сервисов.
+ /// Коллекция сервисов.
+ ///
+ /// Используйте этот метод для регистрации платформенной реализации визуальной обратной связи.
+ ///
+ public static IServiceCollection AddDropVisualAdorner(this IServiceCollection services)
+ where TAdorner : class, IDropVisualAdorner
+ {
+ services.Replace(ServiceDescriptor.Singleton());
+ return services;
+ }
+
+ ///
+ /// Добавляет или заменяет реализацию хоста для отображения визуальных элементов.
+ ///
+ /// Тип хоста визуальных элементов.
+ /// Коллекция сервисов.
+ /// Коллекция сервисов.
+ ///
+ /// Используйте этот метод для регистрации платформенной реализации хоста визуальных элементов.
+ ///
+ public static IServiceCollection AddDragDropHost(this IServiceCollection services)
+ where THost : class, IDragDropHost
+ {
+ services.Replace(ServiceDescriptor.Singleton());
+ return services;
+ }
+
+ #region Default Implementations
+
+ ///
+ /// Безопасная реализация поставщика визуального представления по умолчанию.
+ ///
+ ///
+ /// Эта реализация ничего не делает и используется как заглушка до тех пор,
+ /// пока не будет предоставлена платформенная реализация.
+ ///
private class DefaultDragVisualProvider : IDragVisualProvider
{
- public object CreateDragVisual(Core.DragDrop.Models.DragInfo dragInfo, Core.Geometry.Point initialPosition)
- => new object();
+ ///
+ public object? CreateDragVisual(Core.DragDrop.Models.DragInfo dragInfo, Core.Geometry.Point initialPosition)
+ {
+ // Безопасная реализация: не создает визуальное представление
+ return null;
+ }
- public void UpdateDragVisualPosition(object dragVisual, Core.Geometry.Point position) { }
+ ///
+ public void UpdateDragVisualPosition(object dragVisual, Core.Geometry.Point position)
+ {
+ // Ничего не делаем, так как визуальное представление не было создано
+ }
- public void ReleaseDragVisual(object dragVisual) { }
+ ///
+ public void ReleaseDragVisual(object dragVisual)
+ {
+ // Ничего не делаем, так как визуальное представление не было создано
+ }
}
+ ///
+ /// Безопасная реализация визуальной обратной связи по умолчанию.
+ ///
+ ///
+ /// Эта реализация ничего не делает и используется как заглушка до тех пор,
+ /// пока не будет предоставлена платформенная реализация.
+ ///
private class DefaultDropVisualAdorner : IDropVisualAdorner
{
- public void Show(Core.DragDrop.Models.DropInfo dropInfo, Core.Geometry.Rect targetBounds) { }
- public void Update(Core.DragDrop.Models.DropInfo dropInfo) { }
- public void Hide() { }
+ ///
+ public void Show(Core.DragDrop.Models.DropInfo dropInfo, Core.Geometry.Rect targetBounds)
+ {
+ // Безопасная реализация: не отображает визуальную обратную связь
+ }
+
+ ///
+ public void Update(Core.DragDrop.Models.DropInfo dropInfo)
+ {
+ // Ничего не делаем, так как обратная связь не отображается
+ }
+
+ ///
+ public void Hide()
+ {
+ // Ничего не делаем, так как обратная связь не отображается
+ }
}
+ ///
+ /// Безопасная реализация хоста визуальных элементов по умолчанию.
+ ///
+ ///
+ /// Эта реализация ничего не делает и используется как заглушка до тех пор,
+ /// пока не будет предоставлена платформенная реализация.
+ ///
private class DefaultDragDropHost : IDragDropHost
{
- public void ShowDragVisual(object dragVisual, Core.Geometry.Point position) { }
- public void UpdateDragVisualPosition(object dragVisual, Core.Geometry.Point position) { }
- public void HideDragVisual(object dragVisual) { }
- public void ShowDropAdorner(IDropVisualAdorner adorner) { }
- public void HideDropAdorner(IDropVisualAdorner adorner) { }
+ ///
+ public void ShowDragVisual(object dragVisual, Core.Geometry.Point position)
+ {
+ // Безопасная реализация: не отображает визуальный элемент
+ }
+
+ ///
+ public void UpdateDragVisualPosition(object dragVisual, Core.Geometry.Point position)
+ {
+ // Ничего не делаем, так как визуальный элемент не отображается
+ }
+
+ ///
+ public void HideDragVisual(object dragVisual)
+ {
+ // Ничего не делаем, так как визуальный элемент не отображается
+ }
+
+ ///
+ public void ShowDropAdorner(IDropVisualAdorner adorner)
+ {
+ // Безопасная реализация: не отображает обратную связь
+ }
+
+ ///
+ public void HideDropAdorner(IDropVisualAdorner adorner)
+ {
+ // Ничего не делаем, так как обратная связь не отображается
+ }
}
+
+ #endregion
}
\ No newline at end of file
diff --git a/Lattice.UI.DragDrop/README.md b/Lattice.UI.DragDrop/README.md
index 4de7289..eb2039d 100644
--- a/Lattice.UI.DragDrop/README.md
+++ b/Lattice.UI.DragDrop/README.md
@@ -1,136 +1,378 @@
# Lattice.UI.DragDrop
-
-
-
+UI-слой библиотеки перетаскивания (drag-and-drop) для платформ .NET.
-Кроссплатформенные абстракции для системы перетаскивания в Lattice UI Framework.
+## 📋 Обзор
-## 📦 О проекте
+Lattice.UI.DragDrop предоставляет абстракции и базовые реализации для интеграции системы перетаскивания Lattice.Core.DragDrop с различными UI-фреймворками (WPF, Avalonia, MAUI и т.д.).
-`Lattice.UI.DragDrop` предоставляет платформонезависимые интерфейсы и базовые классы для реализации drag-and-drop функциональности.
-Этот проект служит основой для конкретных реализаций на различных платформах (WinUI, Uno Platform, MAUI и т.д.).
+### ✨ Основные возможности
-## 🎯 Особенности
+- ✅ **Кросс-платформенные абстракции** - единый API для всех UI-фреймворков
+- ✅ **Готовые базовые классы** для быстрой реализации поведения перетаскивания
+- ✅ **Поддержка визуальной обратной связи** через плагинную архитектуру
+- ✅ **Интеграция с DI-контейнерами** через Microsoft.Extensions.DependencyInjection
+- ✅ **Безопасные реализации по умолчанию** для упрощения разработки
+- ✅ **Расширяемость** через наследование и переопределение
-- **Абстрактные интерфейсы** для источников перетаскивания и целей сброса
-- **Базовые классы поведения** для упрощения реализации
-- **Платформонезависимая архитектура**
-- **Поддержка сложных сценариев** (переупорядочивание, вложенное перетаскивание)
-- **Расширяемая система событий**
+## 🏗️ Архитектура
-## 🔧 Интерфейсы
+### Основные компоненты
-### IDragVisualProvider
-```csharp
-public interface IDragVisualProvider
-{
- object CreateDragVisual(DragInfo dragInfo, Point initialPosition);
- void UpdateDragVisualPosition(object dragVisual, Point position);
- void ReleaseDragVisual(object dragVisual);
-}
-```
+#### 1. **IDragDropHost**
+Абстракция для управления визуальными элементами перетаскивания на уровне платформы.
-### IDropVisualAdorner
-```csharp
-public interface IDropVisualAdorner
-{
- void Show(DropInfo dropInfo, Rect targetBounds);
- void Update(DropInfo dropInfo);
- void Hide();
-}
-```
+#### 2. **IDragVisualProvider**
+Поставщик визуального представления для перетаскиваемых элементов.
-## 📦 Установка
+#### 3. **IDropVisualAdorner**
+Элемент визуальной обратной связи при наведении на цель сброса.
-Добавьте проект как ссылку в ваше решение или установите как NuGet пакет:
+#### 4. **DragSourceBehaviorBase\**
+Базовый класс для реализации поведения источника перетаскивания.
-```xml
-
-```
-
-## 🔗 Зависимости
-
-- `Lattice.Core.DragDrop` >= 1.0.0
-- `Lattice.Core.Geometry` >= 1.0.0
-- `Microsoft.Extensions.DependencyInjection.Abstractions` >= 8.0.0
+#### 5. **DropTargetBehaviorBase\**
+Базовый класс для реализации поведения цели сброса.
## 🚀 Быстрый старт
-### 1. Регистрация сервисов
+### 1. Установка
```csharp
-using Lattice.UI.DragDrop.Extensions;
+// В методе ConfigureServices вашего приложения
+services.AddLatticeDragDrop();
+```
+### 2. Регистрация платформенных реализаций
+
+```csharp
+// Для WPF
+services.AddDragVisualProvider();
+services.AddDropVisualAdorner();
+services.AddDragDropHost();
+
+// Для Avalonia
+services.AddDragVisualProvider();
+services.AddDropVisualAdorner();
+services.AddDragDropHost();
+```
+
+### 3. Создание поведения источника перетаскивания
+
+```csharp
+public class MyDragSourceBehavior : DragSourceBehaviorBase
+{
+ public MyDragSourceBehavior(IServiceProvider serviceProvider)
+ : base(serviceProvider)
+ {
+ }
+
+ protected override void SubscribeToEvents(Control element)
+ {
+ element.MouseDown += OnMouseDown;
+ element.MouseMove += OnMouseMove;
+ element.MouseUp += OnMouseUp;
+ }
+
+ protected override void UnsubscribeFromEvents(Control element)
+ {
+ element.MouseDown -= OnMouseDown;
+ element.MouseMove -= OnMouseMove;
+ element.MouseUp -= OnMouseUp;
+ }
+
+ public override async Task TryStartDragAsync(Point startPosition, CancellationToken ct)
+ {
+ // Проверяем, можно ли начать перетаскивание
+ if (!CanDrag())
+ return null;
+
+ // Создаем информацию о перетаскивании
+ return new DragInfo(
+ data: GetDragData(),
+ allowedEffects: DragDropEffects.Copy | DragDropEffects.Move,
+ startPosition: startPosition,
+ source: this
+ );
+ }
+
+ // ... остальная реализация
+}
+```
+
+### 4. Создание поведения цели сброса
+
+```csharp
+public class MyDropTargetBehavior : DropTargetBehaviorBase
+{
+ public MyDropTargetBehavior(IServiceProvider serviceProvider)
+ : base(serviceProvider)
+ {
+ }
+
+ protected override void SubscribeToEvents(Panel element)
+ {
+ element.SizeChanged += OnSizeChanged;
+ element.LayoutUpdated += OnLayoutUpdated;
+ }
+
+ protected override void UnsubscribeFromEvents(Panel element)
+ {
+ element.SizeChanged -= OnSizeChanged;
+ element.LayoutUpdated -= OnLayoutUpdated;
+ }
+
+ public override async Task CanAcceptDropAsync(DropInfo dropInfo, CancellationToken ct)
+ {
+ // Проверяем тип данных
+ if (dropInfo.Data is not MyDataType data)
+ return false;
+
+ // Проверяем бизнес-правила
+ return await ValidateDropAsync(data, ct);
+ }
+
+ public override async Task OnDropAsync(DropInfo dropInfo, CancellationToken ct)
+ {
+ var data = (MyDataType)dropInfo.Data;
+ await ProcessDropAsync(data, ct);
+ dropInfo.MarkAsHandled();
+ }
+
+ // ... остальная реализация
+}
+```
+
+## 🔌 Интеграция с UI-фреймворками
+
+### WPF
+
+```csharp
+public static class WpfDragDropExtensions
+{
+ public static IServiceCollection AddWpfDragDrop(this IServiceCollection services)
+ {
+ return services
+ .AddLatticeDragDrop()
+ .AddDragVisualProvider()
+ .AddDropVisualAdorner()
+ .AddDragDropHost();
+ }
+}
+```
+
+### Avalonia
+
+```csharp
+public static class AvaloniaDragDropExtensions
+{
+ public static IServiceCollection AddAvaloniaDragDrop(this IServiceCollection services)
+ {
+ return services
+ .AddLatticeDragDrop()
+ .AddDragVisualProvider()
+ .AddDropVisualAdorner()
+ .AddDragDropHost();
+ }
+}
+```
+
+## 🎨 Визуальная обратная связь
+
+### Создание кастомного визуального представления
+
+```csharp
+public class CustomDragVisualProvider : IDragVisualProvider
+{
+ public object CreateDragVisual(DragInfo dragInfo, Point initialPosition)
+ {
+ var border = new Border
+ {
+ Background = new SolidColorBrush(Colors.LightBlue),
+ BorderBrush = new SolidColorBrush(Colors.Blue),
+ BorderThickness = new Thickness(1),
+ CornerRadius = new CornerRadius(4),
+ Padding = new Thickness(8),
+ Child = new TextBlock
+ {
+ Text = $"Dragging: {dragInfo.Data}",
+ Foreground = new SolidColorBrush(Colors.Black)
+ },
+ Opacity = 0.8
+ };
+
+ return border;
+ }
+
+ public void UpdateDragVisualPosition(object dragVisual, Point position)
+ {
+ if (dragVisual is FrameworkElement element)
+ {
+ element.SetValue(Canvas.LeftProperty, position.X);
+ element.SetValue(Canvas.TopProperty, position.Y);
+ }
+ }
+
+ public void ReleaseDragVisual(object dragVisual)
+ {
+ if (dragVisual is IDisposable disposable)
+ disposable.Dispose();
+ }
+}
+```
+
+### Создание кастомной обратной связи
+
+```csharp
+public class CustomDropVisualAdorner : IDropVisualAdorner
+{
+ private Border? _adorner;
+
+ public void Show(DropInfo dropInfo, Rect targetBounds)
+ {
+ _adorner = new Border
+ {
+ Background = new SolidColorBrush(Colors.Transparent),
+ BorderBrush = new SolidColorBrush(Colors.Green),
+ BorderThickness = new Thickness(2),
+ CornerRadius = new CornerRadius(4)
+ };
+
+ // Установка позиции и размера
+ Canvas.SetLeft(_adorner, targetBounds.X);
+ Canvas.SetTop(_adorner, targetBounds.Y);
+ _adorner.Width = targetBounds.Width;
+ _adorner.Height = targetBounds.Height;
+
+ // Добавление в визуальное дерево
+ AdornerLayer.GetAdornerLayer(targetElement)?.Add(_adorner);
+ }
+
+ public void Update(DropInfo dropInfo)
+ {
+ if (_adorner != null)
+ {
+ // Обновление стиля в зависимости от эффекта
+ var brush = dropInfo.SuggestedEffects.HasFlag(DragDropEffects.Move)
+ ? Colors.Green
+ : Colors.Blue;
+
+ _adorner.BorderBrush = new SolidColorBrush(brush);
+ }
+ }
+
+ public void Hide()
+ {
+ if (_adorner != null)
+ {
+ var layer = AdornerLayer.GetAdornerLayer(_adorner);
+ layer?.Remove(_adorner);
+ _adorner = null;
+ }
+ }
+}
+```
+
+## ⚙️ Конфигурация
+
+### Настройка сервисов
+
+```csharp
public void ConfigureServices(IServiceCollection services)
{
+ // Базовая регистрация
services.AddLatticeDragDrop();
+
+ // Платформенные реализации
+ services.AddDragVisualProvider();
+ services.AddDropVisualAdorner();
+ services.AddDragDropHost();
+
+ // Регистрация поведений
+ services.AddTransient, MyDragSourceBehavior>();
+ services.AddTransient, MyDropTargetBehavior>();
}
```
-### 2. Создание кастомного поведения
+### Использование с ViewModel
```csharp
-using Lattice.UI.DragDrop.Behaviors;
-
-public class MyDragSource : DragSourceBehaviorBase
+public class MainViewModel
{
- protected override void SubscribeToEvents(MyElement element)
+ private readonly IServiceProvider _serviceProvider;
+
+ public MainViewModel(IServiceProvider serviceProvider)
{
- // Подписка на события элемента
+ _serviceProvider = serviceProvider;
}
-
- public override bool CanStartDrag(out DragInfo? dragInfo)
+
+ public void AttachDragBehavior(UIElement element)
{
- // Реализация проверки возможности перетаскивания
+ var behavior = _serviceProvider.GetRequiredService>();
+ behavior.AssociatedElement = element;
+ }
+
+ public void AttachDropBehavior(Panel panel)
+ {
+ var behavior = _serviceProvider.GetRequiredService>();
+ behavior.AssociatedElement = panel;
}
}
```
-## 📚 API Reference
+## 📝 Best Practices
-### Основные типы
+### 1. **Эффективность визуального представления**
+- Создавайте легковесные визуальные элементы для плавной анимации
+- Используйте кэширование для часто используемых представлений
+- Избегайте сложных преобразований и эффектов
-| Тип | Описание |
-|-----|----------|
-| `DragSourceBehaviorBase` | Базовый класс для поведения источника |
-| `DropTargetBehaviorBase` | Базовый класс для поведения цели |
-| `IDragVisualProvider` | Поставщик визуального представления |
-| `IDropVisualAdorner` | Визуальный элемент обратной связи |
+### 2. **Обработка событий**
+- Всегда отписывайтесь от событий при откреплении поведения
+- Используйте слабые ссылки для предотвращения утечек памяти
+- Обрабатывайте исключения в обработчиках событий
-### Расширения DI
+### 3. **Ресурсы**
+- Освобождайте графические ресурсы в методах Release/Dispose
+- Используйте пулы объектов для часто создаваемых элементов
+- Мониторьте использование памяти при активном перетаскивании
-- `AddLatticeDragDrop()` - регистрация сервисов перетаскивания
+### 4. **Пользовательский опыт**
+- Предоставляйте понятную визуальную обратную связь
+- Поддерживайте настраиваемые стили и темы
+- Обеспечивайте плавность анимации даже на слабых устройствах
-## 🔄 Интеграция с платформенными проектами
+## 🔄 Миграция
-Этот проект предназначен для наследования платформенными реализациями:
+### С версии 1.x на 2.0
-1. **WinUI**: `Lattice.UI.DragDrop.WinUI`
-2. **Uno Platform**: `Lattice.UI.DragDrop.Uno` (планируется)
-3. **MAUI**: `Lattice.UI.DragDrop.Maui` (планируется)
+1. **Обновление интерфейсов**:
+ - Методы переименованы в соответствии с Core
+ - Добавлена поддержка CancellationToken
-## 🧪 Тестирование
+2. **Изменения в базовых классах**:
+ - `DragSourceBehaviorBase` теперь реализует новый интерфейс `IDragSource`
+ - `DropTargetBehaviorBase` теперь реализует новый интерфейс `IDropTarget`
-Проект включает модульные тесты для всех публичных API:
-
-```bash
-dotnet test Lattice.UI.DragDrop.Tests
-```
+3. **Регистрация сервисов**:
+ - Метод `AddLatticeDragDrop` теперь регистрирует только базовые сервисы
+ - Для платформенных реализаций используйте методы `AddDragVisualProvider`, `AddDropVisualAdorner`, `AddDragDropHost`
## 📄 Лицензия
-MIT License. Подробности в файле [LICENSE](LICENSE).
+Библиотека распространяется под лицензией MIT. Подробности см. в файле LICENSE.
-## 🤝 Участие в разработке
+## 🤝 Вклад в разработку
-1. Форкните репозиторий
-2. Создайте ветку для вашей функции
-3. Сделайте коммит изменений
-4. Отправьте пул-реквест
+Для интеграции с новой UI-платформой необходимо:
+1. Реализовать интерфейсы `IDragDropHost`, `IDragVisualProvider`, `IDropVisualAdorner`
+2. Создать производные классы от `DragSourceBehaviorBase` и `DropTargetBehaviorBase`
+3. Предоставить метод расширения для регистрации всех компонентов
-## 📞 Поддержка
+## 🐛 Отчеты об ошибках
-- Документация: [lattice-framework.github.io](https://lattice-framework.github.io)
-- Issues: [GitHub Issues](https://github.com/lattice-framework/ui-dragdrop/issues)
-- Обсуждения: [GitHub Discussions](https://github.com/lattice-framework/ui-dragdrop/discussions)
\ No newline at end of file
+Для сообщения об ошибках используйте Issues на GitHub. Пожалуйста, указывайте:
+- Платформу и версию UI-фреймворка
+- Шаги для воспроизведения
+- Ожидаемое и фактическое поведение
+- Пример кода для демонстрации проблемы
\ No newline at end of file