290 lines
9.3 KiB
C#
290 lines
9.3 KiB
C#
using Lattice.Core.DragDrop.Models;
|
||
using Lattice.Core.Geometry;
|
||
using Lattice.UI.DragDrop.Behaviors;
|
||
using Microsoft.UI.Xaml;
|
||
using Microsoft.UI.Xaml.Controls;
|
||
using Microsoft.UI.Xaml.Input;
|
||
using System;
|
||
using System.Threading.Tasks;
|
||
|
||
namespace Lattice.UI.DragDrop.WinUI.Behaviors;
|
||
|
||
/// <summary>
|
||
/// Поведение источника перетаскивания для WinUI FrameworkElement.
|
||
/// </summary>
|
||
public class WinUIDragSourceBehavior : DragSourceBehaviorBase<FrameworkElement>
|
||
{
|
||
/// <summary>
|
||
/// Прикрепленное свойство для данных перетаскивания.
|
||
/// </summary>
|
||
public static readonly DependencyProperty DragDataProperty =
|
||
DependencyProperty.RegisterAttached(
|
||
"DragData",
|
||
typeof(object),
|
||
typeof(WinUIDragSourceBehavior),
|
||
new PropertyMetadata(null));
|
||
|
||
/// <summary>
|
||
/// Прикрепленное свойство для включения перетаскивания.
|
||
/// </summary>
|
||
public static readonly DependencyProperty IsEnabledProperty =
|
||
DependencyProperty.RegisterAttached(
|
||
"IsEnabled",
|
||
typeof(bool),
|
||
typeof(WinUIDragSourceBehavior),
|
||
new PropertyMetadata(false, OnIsEnabledChanged));
|
||
|
||
/// <summary>
|
||
/// Получает значение DragData.
|
||
/// </summary>
|
||
public static object GetDragData(FrameworkElement element)
|
||
{
|
||
return element.GetValue(DragDataProperty);
|
||
}
|
||
|
||
/// <summary>
|
||
/// Устанавливает значение DragData.
|
||
/// </summary>
|
||
public static void SetDragData(FrameworkElement element, object value)
|
||
{
|
||
element.SetValue(DragDataProperty, value);
|
||
}
|
||
|
||
/// <summary>
|
||
/// Получает значение IsEnabled.
|
||
/// </summary>
|
||
public static bool GetIsEnabled(FrameworkElement element)
|
||
{
|
||
return (bool)element.GetValue(IsEnabledProperty);
|
||
}
|
||
|
||
/// <summary>
|
||
/// Устанавливает значение IsEnabled.
|
||
/// </summary>
|
||
public static void SetIsEnabled(FrameworkElement element, bool value)
|
||
{
|
||
element.SetValue(IsEnabledProperty, value);
|
||
}
|
||
|
||
private static void OnIsEnabledChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||
{
|
||
if (d is FrameworkElement element)
|
||
{
|
||
// Получаем или создаем экземпляр поведения через Attached Property
|
||
var behavior = GetBehavior(element);
|
||
|
||
if ((bool)e.NewValue)
|
||
{
|
||
if (behavior == null)
|
||
{
|
||
behavior = new WinUIDragSourceBehavior();
|
||
SetBehavior(element, behavior);
|
||
}
|
||
|
||
behavior.AssociatedElement = element;
|
||
}
|
||
else
|
||
{
|
||
if (behavior != null)
|
||
{
|
||
behavior.Detach();
|
||
SetBehavior(element, null);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
public WinUIDragSourceBehavior()
|
||
: base(ServiceProviderHelper.GetServiceProvider())
|
||
{
|
||
}
|
||
|
||
protected override void SubscribeToEvents(FrameworkElement element)
|
||
{
|
||
element.PointerPressed += OnPointerPressed;
|
||
element.PointerMoved += OnPointerMoved;
|
||
element.PointerReleased += OnPointerReleased;
|
||
element.PointerCanceled += OnPointerCanceled;
|
||
element.PointerCaptureLost += OnPointerCaptureLost;
|
||
element.LostFocus += OnLostFocus;
|
||
}
|
||
|
||
protected override void UnsubscribeFromEvents(FrameworkElement element)
|
||
{
|
||
element.PointerPressed -= OnPointerPressed;
|
||
element.PointerMoved -= OnPointerMoved;
|
||
element.PointerReleased -= OnPointerReleased;
|
||
element.PointerCanceled -= OnPointerCanceled;
|
||
element.PointerCaptureLost -= OnPointerCaptureLost;
|
||
element.LostFocus -= OnLostFocus;
|
||
}
|
||
|
||
private void OnPointerPressed(object sender, PointerRoutedEventArgs e)
|
||
{
|
||
if (AssociatedElement == null) return;
|
||
|
||
var point = e.GetCurrentPoint(AssociatedElement);
|
||
OnInteractionStarted(new Point(point.Position.X, point.Position.Y));
|
||
}
|
||
|
||
private void OnPointerMoved(object sender, PointerRoutedEventArgs e)
|
||
{
|
||
if (AssociatedElement == null) return;
|
||
|
||
var point = e.GetCurrentPoint(AssociatedElement);
|
||
OnInteractionMoved(new Point(point.Position.X, point.Position.Y));
|
||
}
|
||
|
||
private void OnPointerReleased(object sender, PointerRoutedEventArgs e)
|
||
{
|
||
OnInteractionEnded();
|
||
}
|
||
|
||
private void OnPointerCanceled(object sender, PointerRoutedEventArgs e)
|
||
{
|
||
OnInteractionCancelled();
|
||
}
|
||
|
||
private void OnPointerCaptureLost(object sender, PointerRoutedEventArgs e)
|
||
{
|
||
OnInteractionCancelled();
|
||
}
|
||
|
||
private void OnLostFocus(object sender, RoutedEventArgs e)
|
||
{
|
||
OnInteractionCancelled();
|
||
}
|
||
|
||
protected override Point ConvertToScreenCoordinates(Point point)
|
||
{
|
||
if (AssociatedElement == null)
|
||
return point;
|
||
|
||
var transform = AssociatedElement.TransformToVisual(null);
|
||
var screenPoint = transform.TransformPoint(new Windows.Foundation.Point(point.X, point.Y));
|
||
return new Point(screenPoint.X, screenPoint.Y);
|
||
}
|
||
|
||
/// <inheritdoc/>
|
||
public override async Task<(bool CanStart, DragInfo? DragInfo)> CanStartDragAsync()
|
||
{
|
||
if (AssociatedElement == null)
|
||
{
|
||
return (false, null);
|
||
}
|
||
|
||
var data = GetDragData(AssociatedElement);
|
||
if (data == null)
|
||
{
|
||
// Пробуем получить данные из Tag или других источников
|
||
data = AssociatedElement.Tag ?? AssociatedElement.DataContext;
|
||
}
|
||
|
||
if (data == null)
|
||
{
|
||
return (false, null);
|
||
}
|
||
|
||
// Получаем начальную позицию в экранных координатах
|
||
var startPosition = ConvertToScreenCoordinates(_dragStartPosition);
|
||
|
||
// Создаем DragInfo с учетом вашего конструктора
|
||
var dragInfo = new DragInfo(
|
||
data: data,
|
||
allowedEffects: Core.DragDrop.Enums.DragDropEffects.Move |
|
||
Core.DragDrop.Enums.DragDropEffects.Copy,
|
||
startPosition: startPosition,
|
||
source: this
|
||
);
|
||
|
||
return (true, dragInfo);
|
||
}
|
||
|
||
protected override void OnDragCompleted(DragInfo dragInfo, Core.DragDrop.Enums.DragDropEffects effects)
|
||
{
|
||
base.OnDragCompleted(dragInfo, effects);
|
||
|
||
// Визуальная обратная связь при завершении
|
||
SetVisualState(AssociatedElement, "Normal");
|
||
}
|
||
|
||
protected override void OnDragCancelled(DragInfo dragInfo)
|
||
{
|
||
base.OnDragCancelled(dragInfo);
|
||
|
||
// Визуальная обратная связь при отмене
|
||
SetVisualState(AssociatedElement, "Normal");
|
||
}
|
||
|
||
private void SetVisualState(FrameworkElement? element, string stateName)
|
||
{
|
||
if (element is Control control)
|
||
{
|
||
try
|
||
{
|
||
VisualStateManager.GoToState(control, stateName, true);
|
||
}
|
||
catch
|
||
{
|
||
// Fallback
|
||
control.Opacity = 1.0;
|
||
}
|
||
}
|
||
else if (element != null)
|
||
{
|
||
// Альтернативная визуальная обратная связь для не-Control элементов
|
||
element.Opacity = 1.0;
|
||
}
|
||
}
|
||
|
||
// Attached property для хранения экземпляра поведения
|
||
private static readonly DependencyProperty BehaviorProperty =
|
||
DependencyProperty.RegisterAttached(
|
||
"Behavior",
|
||
typeof(WinUIDragSourceBehavior),
|
||
typeof(WinUIDragSourceBehavior),
|
||
new PropertyMetadata(null));
|
||
|
||
private static WinUIDragSourceBehavior GetBehavior(FrameworkElement element)
|
||
{
|
||
return (WinUIDragSourceBehavior)element.GetValue(BehaviorProperty);
|
||
}
|
||
|
||
private static void SetBehavior(FrameworkElement element, WinUIDragSourceBehavior? value)
|
||
{
|
||
element.SetValue(BehaviorProperty, value);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// Вспомогательный класс для получения IServiceProvider.
|
||
/// </summary>
|
||
internal static class ServiceProviderHelper
|
||
{
|
||
private static IServiceProvider? _serviceProvider;
|
||
|
||
public static IServiceProvider GetServiceProvider()
|
||
{
|
||
if (_serviceProvider == null)
|
||
{
|
||
// Ищем IServiceProvider в Application.Current.Resources
|
||
if (Application.Current.Resources.TryGetValue("ServiceProvider", out var provider) &&
|
||
provider is IServiceProvider serviceProvider)
|
||
{
|
||
_serviceProvider = serviceProvider;
|
||
}
|
||
else
|
||
{
|
||
throw new InvalidOperationException(
|
||
"IServiceProvider не найден. Убедитесь, что ServiceProvider зарегистрирован в ресурсах приложения.");
|
||
}
|
||
}
|
||
|
||
return _serviceProvider;
|
||
}
|
||
|
||
public static void SetServiceProvider(IServiceProvider serviceProvider)
|
||
{
|
||
_serviceProvider = serviceProvider;
|
||
}
|
||
} |