228 lines
7.4 KiB
C#
228 lines
7.4 KiB
C#
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 DropTargetBehaviorBase<TElement> : IDropTarget
|
||
where TElement : class
|
||
{
|
||
private IDragDropService? _dragDropService;
|
||
private string? _registrationId;
|
||
private TElement? _associatedElement;
|
||
private Rect _currentBounds;
|
||
|
||
/// <summary>
|
||
/// Получает или задает связанный элемент.
|
||
/// </summary>
|
||
protected TElement? AssociatedElement
|
||
{
|
||
get => _associatedElement;
|
||
set
|
||
{
|
||
if (_associatedElement != value)
|
||
{
|
||
UnregisterFromService();
|
||
_associatedElement = value;
|
||
RegisterToService();
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// Получает или задает приоритет цели сброса.
|
||
/// </summary>
|
||
public int Priority { get; set; }
|
||
|
||
/// <summary>
|
||
/// Получает или задает группу цели сброса.
|
||
/// </summary>
|
||
public string? Group { get; set; }
|
||
|
||
/// <summary>
|
||
/// Получает сервис перетаскивания.
|
||
/// </summary>
|
||
protected IDragDropService DragDropService
|
||
{
|
||
get
|
||
{
|
||
if (_dragDropService == null)
|
||
{
|
||
_dragDropService = ServiceProvider.GetRequiredService<IDragDropService>();
|
||
}
|
||
return _dragDropService;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// Получает провайдер сервисов.
|
||
/// </summary>
|
||
protected IServiceProvider ServiceProvider { get; }
|
||
|
||
/// <summary>
|
||
/// Получает текущие границы элемента в экранных координатах.
|
||
/// </summary>
|
||
protected Rect CurrentBounds => _currentBounds;
|
||
|
||
/// <summary>
|
||
/// Инициализирует новый экземпляр класса <see cref="DropTargetBehaviorBase{TElement}"/>.
|
||
/// </summary>
|
||
/// <param name="serviceProvider">Провайдер сервисов.</param>
|
||
protected DropTargetBehaviorBase(IServiceProvider serviceProvider)
|
||
{
|
||
ServiceProvider = serviceProvider ?? throw new ArgumentNullException(nameof(serviceProvider));
|
||
}
|
||
|
||
/// <summary>
|
||
/// Вызывается при прикреплении к элементу.
|
||
/// </summary>
|
||
protected virtual void AttachToElement()
|
||
{
|
||
if (_associatedElement != null)
|
||
{
|
||
SubscribeToEvents(_associatedElement);
|
||
UpdateBounds();
|
||
RegisterToService();
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// Вызывается при откреплении от элемента.
|
||
/// </summary>
|
||
protected virtual void DetachFromElement()
|
||
{
|
||
if (_associatedElement != null)
|
||
{
|
||
UnsubscribeFromEvents(_associatedElement);
|
||
UnregisterFromService();
|
||
}
|
||
}
|
||
|
||
/// <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>
|
||
protected virtual void UpdateBounds()
|
||
{
|
||
if (_associatedElement != null)
|
||
{
|
||
_currentBounds = GetScreenBounds(_associatedElement);
|
||
|
||
// Обновляем регистрацию в сервисе
|
||
if (_registrationId != null)
|
||
{
|
||
DragDropService.UpdateDropTargetBounds(_registrationId, _currentBounds);
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// Получает границы элемента в экранных координатах.
|
||
/// </summary>
|
||
/// <param name="element">Элемент.</param>
|
||
/// <returns>Границы в экранных координатах.</returns>
|
||
protected abstract Rect GetScreenBounds(TElement element);
|
||
|
||
/// <summary>
|
||
/// Регистрирует цель в сервисе перетаскивания.
|
||
/// </summary>
|
||
protected virtual void RegisterToService()
|
||
{
|
||
if (_associatedElement != null && _registrationId == null)
|
||
{
|
||
UpdateBounds();
|
||
_registrationId = DragDropService.RegisterDropTarget(this, _currentBounds, Priority, Group);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// Отменяет регистрацию цели в сервисе перетаскивания.
|
||
/// </summary>
|
||
protected virtual void UnregisterFromService()
|
||
{
|
||
if (_registrationId != null)
|
||
{
|
||
DragDropService.UnregisterDropTarget(_registrationId);
|
||
_registrationId = null;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// Вызывается при изменении размера или позиции элемента.
|
||
/// </summary>
|
||
protected virtual void OnElementLayoutChanged()
|
||
{
|
||
UpdateBounds();
|
||
}
|
||
|
||
#region IDropTarget Implementation
|
||
|
||
/// <inheritdoc/>
|
||
public abstract Task<bool> CanAcceptDropAsync(DropInfo dropInfo, CancellationToken ct = default);
|
||
|
||
/// <inheritdoc/>
|
||
public virtual async Task DragOverAsync(DropInfo dropInfo, CancellationToken ct = default)
|
||
{
|
||
// Базовая реализация устанавливает эффект по умолчанию
|
||
if (await CanAcceptDropAsync(dropInfo))
|
||
{
|
||
// Установить эффект по умолчанию, если он разрешен
|
||
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
|
||
{
|
||
dropInfo.SuggestedEffects = Core.DragDrop.Enums.DragDropEffects.None;
|
||
}
|
||
}
|
||
|
||
/// <inheritdoc/>
|
||
public abstract Task DropAsync(DropInfo dropInfo, CancellationToken ct = default);
|
||
|
||
/// <inheritdoc/>
|
||
public virtual async Task DragLeaveAsync(CancellationToken ct = default)
|
||
{
|
||
// Базовая реализация не делает ничего
|
||
}
|
||
|
||
#endregion
|
||
|
||
/// <summary>
|
||
/// Освобождает ресурсы.
|
||
/// </summary>
|
||
public virtual void Detach()
|
||
{
|
||
DetachFromElement();
|
||
_associatedElement = null;
|
||
}
|
||
} |