Files
Lattice/Lattice.UI.DragDrop.WinUI/Behaviors/WinUIDropTargetBehavior.cs
2026-01-18 16:33:35 +03:00

402 lines
16 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 Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using System;
using System.Collections.Generic;
namespace Lattice.UI.DragDrop.WinUI.Behaviors;
/// <summary>
/// Поведение цели сброса для WinUI UIElement.
/// </summary>
public static class WinUIDropTargetBehavior
{
/// <summary>
/// Идентификатор свойства для включения цели сброса.
/// </summary>
public static readonly DependencyProperty IsEnabledProperty =
DependencyProperty.RegisterAttached(
"IsEnabled",
typeof(bool),
typeof(WinUIDropTargetBehavior),
new PropertyMetadata(false, OnIsEnabledChanged));
/// <summary>
/// Идентификатор свойства для фильтрации принимаемых данных.
/// </summary>
public static readonly DependencyProperty AcceptsDataTypesProperty =
DependencyProperty.RegisterAttached(
"AcceptsDataTypes",
typeof(Type[]),
typeof(WinUIDropTargetBehavior),
new PropertyMetadata(null));
/// <summary>
/// Идентификатор свойства для обработчика сброса.
/// </summary>
public static readonly DependencyProperty DropHandlerProperty =
DependencyProperty.RegisterAttached(
"DropHandler",
typeof(Core.DragDrop.Abstractions.IDropTarget),
typeof(WinUIDropTargetBehavior),
new PropertyMetadata(null));
/// <summary>
/// Идентификатор свойства для отображения визуальной обратной связи.
/// </summary>
public static readonly DependencyProperty ShowVisualFeedbackProperty =
DependencyProperty.RegisterAttached(
"ShowVisualFeedback",
typeof(bool),
typeof(WinUIDropTargetBehavior),
new PropertyMetadata(true));
/// <summary>
/// Идентификатор свойства для стиля визуальной обратной связи.
/// </summary>
public static readonly DependencyProperty FeedbackStyleProperty =
DependencyProperty.RegisterAttached(
"FeedbackStyle",
typeof(Style),
typeof(WinUIDropTargetBehavior),
new PropertyMetadata(null));
/// <summary>
/// Идентификатор свойства для обработчика входа перетаскивания.
/// </summary>
public static readonly DependencyProperty DragEnterHandlerProperty =
DependencyProperty.RegisterAttached(
"DragEnterHandler",
typeof(Action<UIElement>),
typeof(WinUIDropTargetBehavior),
new PropertyMetadata(null));
/// <summary>
/// Идентификатор свойства для обработчика выхода перетаскивания.
/// </summary>
public static readonly DependencyProperty DragLeaveHandlerProperty =
DependencyProperty.RegisterAttached(
"DragLeaveHandler",
typeof(Action<UIElement>),
typeof(WinUIDropTargetBehavior),
new PropertyMetadata(null));
/// <summary>
/// Идентификатор свойства для обработчика сброса.
/// </summary>
public static readonly DependencyProperty DropHandlerActionProperty =
DependencyProperty.RegisterAttached(
"DropHandlerAction",
typeof(Action<UIElement, object>),
typeof(WinUIDropTargetBehavior),
new PropertyMetadata(null));
// Словарь для отслеживания текущих перетаскиваемых элементов
private static readonly Dictionary<UIElement, bool> _dragOverElements = new();
/// <summary>
/// Получает значение свойства IsEnabled для указанного элемента.
/// </summary>
public static bool GetIsEnabled(UIElement element) =>
(bool)element.GetValue(IsEnabledProperty);
/// <summary>
/// Устанавливает значение свойства IsEnabled для указанного элемента.
/// </summary>
public static void SetIsEnabled(UIElement element, bool value) =>
element.SetValue(IsEnabledProperty, value);
/// <summary>
/// Получает значение свойства AcceptsDataTypes для указанного элемента.
/// </summary>
public static Type[] GetAcceptsDataTypes(UIElement element) =>
(Type[])element.GetValue(AcceptsDataTypesProperty);
/// <summary>
/// Устанавливает значение свойства AcceptsDataTypes для указанного элемента.
/// </summary>
public static void SetAcceptsDataTypes(UIElement element, Type[] value) =>
element.SetValue(AcceptsDataTypesProperty, value);
/// <summary>
/// Получает значение свойства DropHandler для указанного элемента.
/// </summary>
public static Core.DragDrop.Abstractions.IDropTarget GetDropHandler(UIElement element) =>
(Core.DragDrop.Abstractions.IDropTarget)element.GetValue(DropHandlerProperty);
/// <summary>
/// Устанавливает значение свойства DropHandler для указанного элемента.
/// </summary>
public static void SetDropHandler(UIElement element, Core.DragDrop.Abstractions.IDropTarget value) =>
element.SetValue(DropHandlerProperty, value);
/// <summary>
/// Получает значение свойства ShowVisualFeedback для указанного элемента.
/// </summary>
public static bool GetShowVisualFeedback(UIElement element) =>
(bool)element.GetValue(ShowVisualFeedbackProperty);
/// <summary>
/// Устанавливает значение свойства ShowVisualFeedback для указанного элемента.
/// </summary>
public static void SetShowVisualFeedback(UIElement element, bool value) =>
element.SetValue(ShowVisualFeedbackProperty, value);
/// <summary>
/// Получает значение свойства FeedbackStyle для указанного элемента.
/// </summary>
public static Style GetFeedbackStyle(UIElement element) =>
(Style)element.GetValue(FeedbackStyleProperty);
/// <summary>
/// Устанавливает значение свойства FeedbackStyle для указанного элемента.
/// </summary>
public static void SetFeedbackStyle(UIElement element, Style value) =>
element.SetValue(FeedbackStyleProperty, value);
/// <summary>
/// Получает обработчик входа перетаскивания.
/// </summary>
public static Action<UIElement> GetDragEnterHandler(UIElement element) =>
(Action<UIElement>)element.GetValue(DragEnterHandlerProperty);
/// <summary>
/// Устанавливает обработчик входа перетаскивания.
/// </summary>
public static void SetDragEnterHandler(UIElement element, Action<UIElement> value) =>
element.SetValue(DragEnterHandlerProperty, value);
/// <summary>
/// Получает обработчик выхода перетаскивания.
/// </summary>
public static Action<UIElement> GetDragLeaveHandler(UIElement element) =>
(Action<UIElement>)element.GetValue(DragLeaveHandlerProperty);
/// <summary>
/// Устанавливает обработчик выхода перетаскивания.
/// </summary>
public static void SetDragLeaveHandler(UIElement element, Action<UIElement> value) =>
element.SetValue(DragLeaveHandlerProperty, value);
/// <summary>
/// Получает обработчик сброса.
/// </summary>
public static Action<UIElement, object> GetDropHandlerAction(UIElement element) =>
(Action<UIElement, object>)element.GetValue(DropHandlerActionProperty);
/// <summary>
/// Устанавливает обработчик сброса.
/// </summary>
public static void SetDropHandlerAction(UIElement element, Action<UIElement, object> 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)
);
}
}