using System;
using System.Threading.Tasks;
using Avalonia.Controls;
using Avalonia.Input;
using Avalonia.Interactivity;
using Avalonia.LogicalTree;
using Avalonia.Xaml.Interactivity;
namespace Avalonia.Xaml.Interactions.DragAndDrop;
///
///
///
public class ContextDragBehavior : StyledElementBehavior
{
private Point _dragStartPoint;
private PointerEventArgs? _triggerEvent;
private bool _lock;
private bool _captured;
///
///
///
public static readonly StyledProperty ContextProperty =
AvaloniaProperty.Register(nameof(Context));
///
///
///
public static readonly StyledProperty HandlerProperty =
AvaloniaProperty.Register(nameof(Handler));
///
///
///
public static readonly StyledProperty HorizontalDragThresholdProperty =
AvaloniaProperty.Register(nameof(HorizontalDragThreshold), 3);
///
///
///
public static readonly StyledProperty VerticalDragThresholdProperty =
AvaloniaProperty.Register(nameof(VerticalDragThreshold), 3);
///
///
///
public object? Context
{
get => GetValue(ContextProperty);
set => SetValue(ContextProperty, value);
}
///
///
///
public IDragHandler? Handler
{
get => GetValue(HandlerProperty);
set => SetValue(HandlerProperty, value);
}
///
///
///
public double HorizontalDragThreshold
{
get => GetValue(HorizontalDragThresholdProperty);
set => SetValue(HorizontalDragThresholdProperty, value);
}
///
///
///
public double VerticalDragThreshold
{
get => GetValue(VerticalDragThresholdProperty);
set => SetValue(VerticalDragThresholdProperty, 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);
AssociatedObject?.AddHandler(InputElement.PointerCaptureLostEvent, AssociatedObject_CaptureLost, 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);
AssociatedObject?.RemoveHandler(InputElement.PointerCaptureLostEvent, AssociatedObject_CaptureLost);
}
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 Released()
{
_triggerEvent = null;
_lock = false;
}
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)
{
if ((control as ISelectable ?? control.Parent as ISelectable ?? control.FindLogicalAncestorOfType())?.IsSelected ?? false) e.Handled = true; //avoid deselection on drag
_dragStartPoint = e.GetPosition(null);
_triggerEvent = e;
_lock = true;
_captured = true;
}
}
}
private void AssociatedObject_PointerReleased(object? sender, PointerReleasedEventArgs e)
{
if (_captured)
{
if (e.InitialPressMouseButton == MouseButton.Left && _triggerEvent is not null)
{
Released();
}
_captured = false;
}
}
private async void AssociatedObject_PointerMoved(object? sender, PointerEventArgs e)
{
var properties = e.GetCurrentPoint(AssociatedObject).Properties;
if (_captured
&& properties.IsLeftButtonPressed &&
_triggerEvent is not null)
{
var point = e.GetPosition(null);
var diff = _dragStartPoint - point;
var horizontalDragThreshold = HorizontalDragThreshold;
var verticalDragThreshold = VerticalDragThreshold;
if (Math.Abs(diff.X) > horizontalDragThreshold || Math.Abs(diff.Y) > verticalDragThreshold)
{
if (_lock)
{
_lock = false;
}
else
{
return;
}
var context = Context ?? AssociatedObject?.DataContext;
Handler?.BeforeDragDrop(sender, _triggerEvent, context);
await DoDragDrop(_triggerEvent, context);
Handler?.AfterDragDrop(sender, _triggerEvent, context);
_triggerEvent = null;
}
}
}
private void AssociatedObject_CaptureLost(object? sender, PointerCaptureLostEventArgs e)
{
Released();
_captured = false;
}
}