using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; using System; using System.Collections.Generic; namespace Lattice.UI.DragDrop.WinUI.Behaviors; /// /// Поведение цели сброса для WinUI UIElement. /// public static class WinUIDropTargetBehavior { /// /// Идентификатор свойства для включения цели сброса. /// public static readonly DependencyProperty IsEnabledProperty = DependencyProperty.RegisterAttached( "IsEnabled", typeof(bool), typeof(WinUIDropTargetBehavior), new PropertyMetadata(false, OnIsEnabledChanged)); /// /// Идентификатор свойства для фильтрации принимаемых данных. /// public static readonly DependencyProperty AcceptsDataTypesProperty = DependencyProperty.RegisterAttached( "AcceptsDataTypes", typeof(Type[]), typeof(WinUIDropTargetBehavior), new PropertyMetadata(null)); /// /// Идентификатор свойства для обработчика сброса. /// public static readonly DependencyProperty DropHandlerProperty = DependencyProperty.RegisterAttached( "DropHandler", typeof(Core.DragDrop.Abstractions.IDropTarget), typeof(WinUIDropTargetBehavior), new PropertyMetadata(null)); /// /// Идентификатор свойства для отображения визуальной обратной связи. /// public static readonly DependencyProperty ShowVisualFeedbackProperty = DependencyProperty.RegisterAttached( "ShowVisualFeedback", typeof(bool), typeof(WinUIDropTargetBehavior), new PropertyMetadata(true)); /// /// Идентификатор свойства для стиля визуальной обратной связи. /// public static readonly DependencyProperty FeedbackStyleProperty = DependencyProperty.RegisterAttached( "FeedbackStyle", typeof(Style), typeof(WinUIDropTargetBehavior), new PropertyMetadata(null)); /// /// Идентификатор свойства для обработчика входа перетаскивания. /// public static readonly DependencyProperty DragEnterHandlerProperty = DependencyProperty.RegisterAttached( "DragEnterHandler", typeof(Action), typeof(WinUIDropTargetBehavior), new PropertyMetadata(null)); /// /// Идентификатор свойства для обработчика выхода перетаскивания. /// public static readonly DependencyProperty DragLeaveHandlerProperty = DependencyProperty.RegisterAttached( "DragLeaveHandler", typeof(Action), typeof(WinUIDropTargetBehavior), new PropertyMetadata(null)); /// /// Идентификатор свойства для обработчика сброса. /// public static readonly DependencyProperty DropHandlerActionProperty = DependencyProperty.RegisterAttached( "DropHandlerAction", typeof(Action), typeof(WinUIDropTargetBehavior), new PropertyMetadata(null)); // Словарь для отслеживания текущих перетаскиваемых элементов private static readonly Dictionary _dragOverElements = new(); /// /// Получает значение свойства IsEnabled для указанного элемента. /// public static bool GetIsEnabled(UIElement element) => (bool)element.GetValue(IsEnabledProperty); /// /// Устанавливает значение свойства IsEnabled для указанного элемента. /// public static void SetIsEnabled(UIElement element, bool value) => element.SetValue(IsEnabledProperty, value); /// /// Получает значение свойства AcceptsDataTypes для указанного элемента. /// public static Type[] GetAcceptsDataTypes(UIElement element) => (Type[])element.GetValue(AcceptsDataTypesProperty); /// /// Устанавливает значение свойства AcceptsDataTypes для указанного элемента. /// public static void SetAcceptsDataTypes(UIElement element, Type[] value) => element.SetValue(AcceptsDataTypesProperty, value); /// /// Получает значение свойства DropHandler для указанного элемента. /// public static Core.DragDrop.Abstractions.IDropTarget GetDropHandler(UIElement element) => (Core.DragDrop.Abstractions.IDropTarget)element.GetValue(DropHandlerProperty); /// /// Устанавливает значение свойства DropHandler для указанного элемента. /// public static void SetDropHandler(UIElement element, Core.DragDrop.Abstractions.IDropTarget value) => element.SetValue(DropHandlerProperty, value); /// /// Получает значение свойства ShowVisualFeedback для указанного элемента. /// public static bool GetShowVisualFeedback(UIElement element) => (bool)element.GetValue(ShowVisualFeedbackProperty); /// /// Устанавливает значение свойства ShowVisualFeedback для указанного элемента. /// public static void SetShowVisualFeedback(UIElement element, bool value) => element.SetValue(ShowVisualFeedbackProperty, value); /// /// Получает значение свойства FeedbackStyle для указанного элемента. /// public static Style GetFeedbackStyle(UIElement element) => (Style)element.GetValue(FeedbackStyleProperty); /// /// Устанавливает значение свойства FeedbackStyle для указанного элемента. /// public static void SetFeedbackStyle(UIElement element, Style value) => element.SetValue(FeedbackStyleProperty, value); /// /// Получает обработчик входа перетаскивания. /// public static Action GetDragEnterHandler(UIElement element) => (Action)element.GetValue(DragEnterHandlerProperty); /// /// Устанавливает обработчик входа перетаскивания. /// public static void SetDragEnterHandler(UIElement element, Action value) => element.SetValue(DragEnterHandlerProperty, value); /// /// Получает обработчик выхода перетаскивания. /// public static Action GetDragLeaveHandler(UIElement element) => (Action)element.GetValue(DragLeaveHandlerProperty); /// /// Устанавливает обработчик выхода перетаскивания. /// public static void SetDragLeaveHandler(UIElement element, Action value) => element.SetValue(DragLeaveHandlerProperty, value); /// /// Получает обработчик сброса. /// public static Action GetDropHandlerAction(UIElement element) => (Action)element.GetValue(DropHandlerActionProperty); /// /// Устанавливает обработчик сброса. /// public static void SetDropHandlerAction(UIElement element, Action value) => element.SetValue(DropHandlerActionProperty, value); private static void OnIsEnabledChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { if (d is UIElement element) { if ((bool)e.NewValue) { EnableDrop(element); } else { DisableDrop(element); } } } private static void EnableDrop(UIElement element) { element.AllowDrop = true; element.DragEnter += OnDragEnter; element.DragOver += OnDragOver; element.DragLeave += OnDragLeave; element.Drop += OnDrop; } private static void DisableDrop(UIElement element) { element.AllowDrop = false; element.DragEnter -= OnDragEnter; element.DragOver -= OnDragOver; element.DragLeave -= OnDragLeave; element.Drop -= OnDrop; } private static void OnDragEnter(object sender, DragEventArgs e) { var element = sender as UIElement; if (element == null) return; // Проверяем, можно ли принять данные if (CanAcceptData(element, e.DataView)) { e.AcceptedOperation = Windows.ApplicationModel.DataTransfer.DataPackageOperation.Copy; // Визуальная обратная связь ShowVisualFeedback(element, true); // Вызываем пользовательский обработчик GetDragEnterHandler(element)?.Invoke(element); // Добавляем в словарь _dragOverElements[element] = true; } else { e.AcceptedOperation = Windows.ApplicationModel.DataTransfer.DataPackageOperation.None; } e.Handled = true; } private static void OnDragOver(object sender, DragEventArgs e) { var element = sender as UIElement; if (element == null) return; if (CanAcceptData(element, e.DataView)) { e.AcceptedOperation = Windows.ApplicationModel.DataTransfer.DataPackageOperation.Copy; // Обновляем визуальную обратную связь на основе позиции var position = e.GetPosition(element); UpdateVisualFeedback(element, position); } else { e.AcceptedOperation = Windows.ApplicationModel.DataTransfer.DataPackageOperation.None; } e.Handled = true; } private static void OnDragLeave(object sender, DragEventArgs e) { var element = sender as UIElement; if (element == null) return; // Скрываем визуальную обратную связь ShowVisualFeedback(element, false); // Вызываем пользовательский обработчик GetDragLeaveHandler(element)?.Invoke(element); // Удаляем из словаря _dragOverElements.Remove(element); } private static void OnDrop(object sender, DragEventArgs e) { var element = sender as UIElement; if (element == null) return; // Скрываем визуальную обратную связь ShowVisualFeedback(element, false); // Получаем данные var data = ExtractData(e.DataView); // Получаем обработчик или используем встроенный var handler = GetDropHandler(element); if (handler != null) { // Создаем DropInfo для обработчика var dropInfo = CreateDropInfo(element, e, data); handler.Drop(dropInfo); } // Вызываем пользовательский обработчик var actionHandler = GetDropHandlerAction(element); if (actionHandler != null && data != null) { actionHandler.Invoke(element, data); } // Удаляем из словаря _dragOverElements.Remove(element); e.Handled = true; } private static bool CanAcceptData(UIElement element, Windows.ApplicationModel.DataTransfer.DataPackageView dataView) { // Проверяем фильтр типов данных var acceptedTypes = GetAcceptsDataTypes(element); if (acceptedTypes != null && acceptedTypes.Length > 0) { // В реальной реализации нужно проверять доступные форматы данных // Здесь упрощенная проверка return dataView.AvailableFormats.Count > 0; } // Если нет фильтра, принимаем все return true; } private static object? ExtractData(Windows.ApplicationModel.DataTransfer.DataPackageView dataView) { // Упрощенная реализация извлечения данных // В реальном приложении нужно обрабатывать разные форматы данных try { if (dataView.Contains(Windows.ApplicationModel.DataTransfer.StandardDataFormats.Text)) { // В WinUI 3 нужно использовать async/await, но это упрощенный пример // В реальном коде нужно использовать async методы return "Text data from drag"; } } catch { // Игнорируем ошибки извлечения данных } return null; } private static void ShowVisualFeedback(UIElement element, bool show) { if (!GetShowVisualFeedback(element)) return; // Для элементов, которые являются Control, используем VisualStateManager if (element is Control control) { // Пытаемся перейти к состоянию "DragOver" или "Normal" try { VisualStateManager.GoToState(control, show ? "DragOver" : "Normal", true); } catch { // Если состояния не определены, меняем свойства напрямую control.Background = show ? new Microsoft.UI.Xaml.Media.SolidColorBrush(Windows.UI.Color.FromArgb(30, 0, 120, 215)) : null; } } else { // Для обычных UIElement меняем свойства напрямую element.Opacity = show ? 0.8 : 1.0; } } private static void UpdateVisualFeedback(UIElement element, Windows.Foundation.Point position) { // Можно добавить логику для различных зон сброса // Например, подсветка разных частей элемента } private static Core.DragDrop.Models.DropInfo CreateDropInfo(UIElement element, DragEventArgs e, object? data) { var position = e.GetPosition(element); var screenPosition = element.TransformToVisual(null).TransformPoint(position); return new Core.DragDrop.Models.DropInfo( data: data, position: new Core.Geometry.Point(screenPosition.X, screenPosition.Y), allowedEffects: Core.DragDrop.Enums.DragDropEffects.Copy | Core.DragDrop.Enums.DragDropEffects.Move, target: GetDropHandler(element) ); } }