using System;
using System.Diagnostics.CodeAnalysis;
using System.Threading.Tasks;
using Avalonia.Controls;
using Avalonia.Input;
using Avalonia.Interactivity;
using Avalonia.Xaml.Interactivity;
namespace Avalonia.Xaml.Interactions.DragAndDrop;
///
///
///
public class TypedDragBehavior : StyledElementBehavior
{
private Point _dragStartPoint;
private PointerEventArgs? _triggerEvent;
private object? _value;
private bool _lock;
///
///
///
public static readonly StyledProperty DataTypeProperty =
AvaloniaProperty.Register(nameof(DataType));
///
///
///
public static readonly StyledProperty HandlerProperty =
AvaloniaProperty.Register(nameof(Handler));
///
///
///
public Type? DataType
{
get => GetValue(DataTypeProperty);
set => SetValue(DataTypeProperty, value);
}
///
///
///
public IDragHandler? Handler
{
get => GetValue(HandlerProperty);
set => SetValue(HandlerProperty, value);
}
///
protected override void OnAttachedToVisualTree()
{
AssociatedObject?.AddHandler(InputElement.PointerPressedEvent, AssociatedObject_PointerPressed, RoutingStrategies.Direct | RoutingStrategies.Tunnel | RoutingStrategies.Bubble);
AssociatedObject?.AddHandler(InputElement.PointerReleasedEvent, AssociatedObject_PointerReleased, RoutingStrategies.Direct | RoutingStrategies.Tunnel | RoutingStrategies.Bubble);
AssociatedObject?.AddHandler(InputElement.PointerMovedEvent, AssociatedObject_PointerMoved, RoutingStrategies.Direct | RoutingStrategies.Tunnel | RoutingStrategies.Bubble);
}
///
protected override void OnDetachedFromVisualTree()
{
AssociatedObject?.RemoveHandler(InputElement.PointerPressedEvent, AssociatedObject_PointerPressed);
AssociatedObject?.RemoveHandler(InputElement.PointerReleasedEvent, AssociatedObject_PointerReleased);
AssociatedObject?.RemoveHandler(InputElement.PointerMovedEvent, AssociatedObject_PointerMoved);
}
private async Task DoDragDrop(PointerEventArgs triggerEvent, object? value)
{
var data = new DataObject();
data.Set(ContextDropBehavior.DataFormat, value!);
var effect = DragDropEffects.None;
if (triggerEvent.KeyModifiers.HasFlag(KeyModifiers.Alt))
{
effect |= DragDropEffects.Link;
}
else if (triggerEvent.KeyModifiers.HasFlag(KeyModifiers.Shift))
{
effect |= DragDropEffects.Move;
}
else if (triggerEvent.KeyModifiers.HasFlag(KeyModifiers.Control))
{
effect |= DragDropEffects.Copy;
}
else
{
effect |= DragDropEffects.Move;
}
await DragDrop.DoDragDrop(triggerEvent, data, effect);
}
private void AssociatedObject_PointerPressed(object? sender, PointerPressedEventArgs e)
{
var properties = e.GetCurrentPoint(AssociatedObject).Properties;
if (properties.IsLeftButtonPressed)
{
if (e.Source is Control control
&& AssociatedObject?.DataContext == control.DataContext
&& DataType is not null
&& DataType.IsInstanceOfType(control.DataContext))
{
_dragStartPoint = e.GetPosition(null);
_triggerEvent = e;
_value = control.DataContext;
_lock = true;
}
}
}
private void AssociatedObject_PointerReleased(object? sender, PointerReleasedEventArgs e)
{
var properties = e.GetCurrentPoint(AssociatedObject).Properties;
if (properties.PointerUpdateKind == PointerUpdateKind.LeftButtonReleased && _triggerEvent is not null)
{
_triggerEvent = null;
_value = null;
_lock = false;
}
}
private async void AssociatedObject_PointerMoved(object? sender, PointerEventArgs e)
{
var properties = e.GetCurrentPoint(AssociatedObject).Properties;
if (properties.IsLeftButtonPressed && _triggerEvent is not null)
{
var point = e.GetPosition(null);
var diff = _dragStartPoint - point;
if (Math.Abs(diff.X) > 3 || Math.Abs(diff.Y) > 3)
{
if (_lock)
{
_lock = false;
}
else
{
return;
}
Handler?.BeforeDragDrop(sender, _triggerEvent, _value);
await DoDragDrop(_triggerEvent, _value);
Handler?.AfterDragDrop(sender, _triggerEvent, _value);
_triggerEvent = null;
_value = null;
}
}
}
}