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; /// /// Поведение источника перетаскивания для WinUI FrameworkElement. /// public class WinUIDragSourceBehavior : DragSourceBehaviorBase { /// /// Прикрепленное свойство для данных перетаскивания. /// public static readonly DependencyProperty DragDataProperty = DependencyProperty.RegisterAttached( "DragData", typeof(object), typeof(WinUIDragSourceBehavior), new PropertyMetadata(null)); /// /// Прикрепленное свойство для включения перетаскивания. /// public static readonly DependencyProperty IsEnabledProperty = DependencyProperty.RegisterAttached( "IsEnabled", typeof(bool), typeof(WinUIDragSourceBehavior), new PropertyMetadata(false, OnIsEnabledChanged)); /// /// Получает значение DragData. /// public static object GetDragData(FrameworkElement element) { return element.GetValue(DragDataProperty); } /// /// Устанавливает значение DragData. /// public static void SetDragData(FrameworkElement element, object value) { element.SetValue(DragDataProperty, value); } /// /// Получает значение IsEnabled. /// public static bool GetIsEnabled(FrameworkElement element) { return (bool)element.GetValue(IsEnabledProperty); } /// /// Устанавливает значение IsEnabled. /// 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); } /// 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); } } /// /// Вспомогательный класс для получения IServiceProvider. /// 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; } }