Files
Lattice/Lattice.UI.DragDrop/Behaviors/DragSourceBehaviorBase.cs
2026-01-18 16:33:35 +03:00

256 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;
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;
}
}