DragAndDrop core
This commit is contained in:
256
Lattice.UI.DragDrop/Behaviors/DragSourceBehaviorBase.cs
Normal file
256
Lattice.UI.DragDrop/Behaviors/DragSourceBehaviorBase.cs
Normal file
@@ -0,0 +1,256 @@
|
||||
using Lattice.Core.DragDrop.Abstractions;
|
||||
using Lattice.Core.DragDrop.Models;
|
||||
using Lattice.Core.DragDrop.Services;
|
||||
using Lattice.Core.Geometry;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using System;
|
||||
|
||||
namespace Lattice.UI.DragDrop.Behaviors;
|
||||
|
||||
/// <summary>
|
||||
/// Базовый класс поведения источника перетаскивания.
|
||||
/// </summary>
|
||||
/// <typeparam name="TElement">Тип UI элемента.</typeparam>
|
||||
public abstract class DragSourceBehaviorBase<TElement> : IDragSource
|
||||
where TElement : class
|
||||
{
|
||||
private IDragDropService? _dragDropService;
|
||||
private Point _dragStartPosition;
|
||||
private bool _isDragging;
|
||||
private TElement? _associatedElement;
|
||||
|
||||
/// <summary>
|
||||
/// Получает или задает связанный элемент.
|
||||
/// </summary>
|
||||
protected TElement? AssociatedElement
|
||||
{
|
||||
get => _associatedElement;
|
||||
set
|
||||
{
|
||||
if (_associatedElement != value)
|
||||
{
|
||||
DetachFromElement();
|
||||
_associatedElement = value;
|
||||
AttachToElement();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Получает сервис перетаскивания.
|
||||
/// </summary>
|
||||
protected IDragDropService DragDropService
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_dragDropService == null)
|
||||
{
|
||||
_dragDropService = ServiceProvider.GetRequiredService<IDragDropService>();
|
||||
}
|
||||
return _dragDropService;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Получает провайдер сервисов.
|
||||
/// </summary>
|
||||
protected IServiceProvider ServiceProvider { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Инициализирует новый экземпляр класса <see cref="DragSourceBehaviorBase{TElement}"/>.
|
||||
/// </summary>
|
||||
/// <param name="serviceProvider">Провайдер сервисов.</param>
|
||||
protected DragSourceBehaviorBase(IServiceProvider serviceProvider)
|
||||
{
|
||||
ServiceProvider = serviceProvider ?? throw new ArgumentNullException(nameof(serviceProvider));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Вызывается при прикреплении к элементу.
|
||||
/// </summary>
|
||||
protected virtual void AttachToElement()
|
||||
{
|
||||
if (_associatedElement != null)
|
||||
{
|
||||
SubscribeToEvents(_associatedElement);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Вызывается при откреплении от элемента.
|
||||
/// </summary>
|
||||
protected virtual void DetachFromElement()
|
||||
{
|
||||
if (_associatedElement != null)
|
||||
{
|
||||
UnsubscribeFromEvents(_associatedElement);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Подписывается на события элемента.
|
||||
/// </summary>
|
||||
/// <param name="element">Элемент.</param>
|
||||
protected abstract void SubscribeToEvents(TElement element);
|
||||
|
||||
/// <summary>
|
||||
/// Отписывается от событий элемента.
|
||||
/// </summary>
|
||||
/// <param name="element">Элемент.</param>
|
||||
protected abstract void UnsubscribeFromEvents(TElement element);
|
||||
|
||||
/// <summary>
|
||||
/// Обрабатывает начало взаимодействия (например, нажатие мыши).
|
||||
/// </summary>
|
||||
/// <param name="position">Позиция в координатах элемента.</param>
|
||||
protected virtual void OnInteractionStarted(Point position)
|
||||
{
|
||||
if (_isDragging)
|
||||
return;
|
||||
|
||||
_dragStartPosition = position;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Обрабатывает перемещение во время взаимодействия.
|
||||
/// </summary>
|
||||
/// <param name="position">Позиция в координатах элемента.</param>
|
||||
protected virtual void OnInteractionMoved(Point position)
|
||||
{
|
||||
if (_isDragging)
|
||||
return;
|
||||
|
||||
var distance = CalculateDistance(_dragStartPosition, position);
|
||||
if (distance > DragDropService.DragStartThreshold)
|
||||
{
|
||||
StartDragOperation();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Обрабатывает завершение взаимодействия.
|
||||
/// </summary>
|
||||
protected virtual void OnInteractionEnded()
|
||||
{
|
||||
// Сброс состояния, если перетаскивание не началось
|
||||
if (!_isDragging)
|
||||
{
|
||||
Reset();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Обрабатывает отмену взаимодействия.
|
||||
/// </summary>
|
||||
protected virtual void OnInteractionCancelled()
|
||||
{
|
||||
if (_isDragging)
|
||||
{
|
||||
DragDropService.CancelDrag();
|
||||
}
|
||||
Reset();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Начинает операцию перетаскивания.
|
||||
/// </summary>
|
||||
protected virtual void StartDragOperation()
|
||||
{
|
||||
if (_isDragging || AssociatedElement == null)
|
||||
return;
|
||||
|
||||
// Получаем начальную позицию в экранных координатах
|
||||
var screenPosition = ConvertToScreenCoordinates(_dragStartPosition);
|
||||
|
||||
// Начинаем перетаскивание
|
||||
_isDragging = DragDropService.StartDrag(this, screenPosition);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Преобразует координаты элемента в экранные координаты.
|
||||
/// </summary>
|
||||
/// <param name="point">Точка в координатах элемента.</param>
|
||||
/// <returns>Точка в экранных координатах.</returns>
|
||||
protected abstract Point ConvertToScreenCoordinates(Point point);
|
||||
|
||||
/// <summary>
|
||||
/// Вычисляет расстояние между двумя точками.
|
||||
/// </summary>
|
||||
protected virtual double CalculateDistance(Point p1, Point p2)
|
||||
{
|
||||
var dx = p2.X - p1.X;
|
||||
var dy = p2.Y - p1.Y;
|
||||
return Math.Sqrt(dx * dx + dy * dy);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Сбрасывает состояние поведения.
|
||||
/// </summary>
|
||||
protected virtual void Reset()
|
||||
{
|
||||
_isDragging = false;
|
||||
_dragStartPosition = default;
|
||||
}
|
||||
|
||||
#region IDragSource Implementation
|
||||
|
||||
/// <inheritdoc/>
|
||||
public abstract bool CanStartDrag(out DragInfo? dragInfo);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public virtual bool StartDrag(DragInfo dragInfo)
|
||||
{
|
||||
// Базовая реализация всегда разрешает начало перетаскивания
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public virtual void DragCompleted(DragInfo dragInfo, Core.DragDrop.Enums.DragDropEffects effects)
|
||||
{
|
||||
_isDragging = false;
|
||||
|
||||
// Оповещаем о завершении перетаскивания
|
||||
OnDragCompleted(dragInfo, effects);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public virtual void DragCancelled(DragInfo dragInfo)
|
||||
{
|
||||
_isDragging = false;
|
||||
|
||||
// Оповещаем об отмене перетаскивания
|
||||
OnDragCancelled(dragInfo);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Virtual Methods for Derived Classes
|
||||
|
||||
/// <summary>
|
||||
/// Вызывается при успешном завершении перетаскивания.
|
||||
/// </summary>
|
||||
/// <param name="dragInfo">Информация о перетаскивании.</param>
|
||||
/// <param name="effects">Примененные эффекты.</param>
|
||||
protected virtual void OnDragCompleted(DragInfo dragInfo, Core.DragDrop.Enums.DragDropEffects effects)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Вызывается при отмене перетаскивания.
|
||||
/// </summary>
|
||||
/// <param name="dragInfo">Информация о перетаскивании.</param>
|
||||
protected virtual void OnDragCancelled(DragInfo dragInfo)
|
||||
{
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Освобождает ресурсы.
|
||||
/// </summary>
|
||||
public virtual void Detach()
|
||||
{
|
||||
DetachFromElement();
|
||||
_associatedElement = null;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user