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

228 lines
7.4 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 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;
}
}