Files
Lattice/Lattice.UI.DragDrop/Behaviors/DragSourceBehaviorBase.cs

253 lines
7.9 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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;
using System.Threading;
using System.Threading.Tasks;
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 async Task OnInteractionStarted(Point position)
{
if (_isDragging)
return;
_dragStartPosition = position;
}
/// <summary>
/// Обрабатывает перемещение во время взаимодействия.
/// </summary>
/// <param name="position">Позиция в координатах элемента.</param>
protected virtual async Task OnInteractionMoved(Point position)
{
if (_isDragging)
return;
var distance = CalculateDistance(_dragStartPosition, position);
if (distance > DragDropService.DragStartThreshold)
{
await StartDragOperation();
}
}
/// <summary>
/// Обрабатывает завершение взаимодействия.
/// </summary>
protected virtual async Task OnInteractionEnded()
{
// Сброс состояния, если перетаскивание не началось
if (!_isDragging)
{
Reset();
}
}
/// <summary>
/// Обрабатывает отмену взаимодействия.
/// </summary>
protected virtual async Task OnInteractionCancelled()
{
if (_isDragging)
{
await DragDropService.CancelDragAsync();
}
Reset();
}
/// <summary>
/// Начинает операцию перетаскивания.
/// </summary>
protected virtual async Task StartDragOperation()
{
if (_isDragging || AssociatedElement == null)
return;
// Получаем начальную позицию в экранных координатах
var screenPosition = ConvertToScreenCoordinates(_dragStartPosition);
// Начинаем перетаскивание
_isDragging = await DragDropService.StartDragAsync(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 Task<(bool CanStart, DragInfo? DragInfo)> CanStartDragAsync(CancellationToken ct = default);
/// <inheritdoc/>
public virtual async Task<bool> StartDragAsync(DragInfo dragInfo, CancellationToken ct = default)
{
return true;
}
/// <inheritdoc/>
public virtual async Task DragCompletedAsync(DragInfo dragInfo, Core.DragDrop.Enums.DragDropEffects effects, CancellationToken ct = default)
{
_isDragging = false;
OnDragCompleted(dragInfo, effects);
}
/// <inheritdoc/>
public virtual async Task DragCancelledAsync(DragInfo dragInfo, CancellationToken ct = default)
{
_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;
}
}