123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411 |
- using System;
- using System.Collections;
- using Avalonia.Controls;
- using Avalonia.Controls.Primitives;
- using Avalonia.Input;
- using Avalonia.Interactivity;
- using Avalonia.Layout;
- using Avalonia.Media.Transformation;
- using Avalonia.Xaml.Interactivity;
- namespace Avalonia.Xaml.Interactions.Draggable;
- /// <summary>
- ///
- /// </summary>
- public class ItemDragBehavior : StyledElementBehavior<Control>
- {
- private bool _enableDrag;
- private bool _dragStarted;
- private Point _start;
- private int _draggedIndex;
- private int _targetIndex;
- private ItemsControl? _itemsControl;
- private Control? _draggedContainer;
- private bool _captured;
- /// <summary>
- ///
- /// </summary>
- public static readonly StyledProperty<Orientation> OrientationProperty =
- AvaloniaProperty.Register<ItemDragBehavior, Orientation>(nameof(Orientation));
- /// <summary>
- ///
- /// </summary>
- public static readonly StyledProperty<double> HorizontalDragThresholdProperty =
- AvaloniaProperty.Register<ItemDragBehavior, double>(nameof(HorizontalDragThreshold), 3);
- /// <summary>
- ///
- /// </summary>
- public static readonly StyledProperty<double> VerticalDragThresholdProperty =
- AvaloniaProperty.Register<ItemDragBehavior, double>(nameof(VerticalDragThreshold), 3);
- /// <summary>
- ///
- /// </summary>
- public Orientation Orientation
- {
- get => GetValue(OrientationProperty);
- set => SetValue(OrientationProperty, value);
- }
- /// <summary>
- ///
- /// </summary>
- public double HorizontalDragThreshold
- {
- get => GetValue(HorizontalDragThresholdProperty);
- set => SetValue(HorizontalDragThresholdProperty, value);
- }
- /// <summary>
- ///
- /// </summary>
- public double VerticalDragThreshold
- {
- get => GetValue(VerticalDragThresholdProperty);
- set => SetValue(VerticalDragThresholdProperty, value);
- }
- /// <inheritdoc />
- protected override void OnAttachedToVisualTree()
- {
- if (AssociatedObject is not null)
- {
- AssociatedObject.AddHandler(InputElement.PointerReleasedEvent, PointerReleased, RoutingStrategies.Tunnel);
- AssociatedObject.AddHandler(InputElement.PointerPressedEvent, PointerPressed, RoutingStrategies.Tunnel);
- AssociatedObject.AddHandler(InputElement.PointerMovedEvent, PointerMoved, RoutingStrategies.Tunnel);
- AssociatedObject.AddHandler(InputElement.PointerCaptureLostEvent, PointerCaptureLost, RoutingStrategies.Tunnel);
- }
- }
- /// <inheritdoc />
- protected override void OnDetachedFromVisualTree()
- {
- if (AssociatedObject is not null)
- {
- AssociatedObject.RemoveHandler(InputElement.PointerReleasedEvent, PointerReleased);
- AssociatedObject.RemoveHandler(InputElement.PointerPressedEvent, PointerPressed);
- AssociatedObject.RemoveHandler(InputElement.PointerMovedEvent, PointerMoved);
- AssociatedObject.RemoveHandler(InputElement.PointerCaptureLostEvent, PointerCaptureLost);
- }
- }
- private void PointerPressed(object? sender, PointerPressedEventArgs e)
- {
- var properties = e.GetCurrentPoint(AssociatedObject).Properties;
- if (properties.IsLeftButtonPressed
- && AssociatedObject?.Parent is ItemsControl itemsControl)
- {
- _enableDrag = true;
- _dragStarted = false;
- _start = e.GetPosition(itemsControl);
- _draggedIndex = -1;
- _targetIndex = -1;
- _itemsControl = itemsControl;
- _draggedContainer = AssociatedObject;
- if (_draggedContainer is not null)
- {
- SetDraggingPseudoClasses(_draggedContainer, true);
- }
- AddTransforms(_itemsControl);
- _captured = true;
- }
- }
- private void PointerReleased(object? sender, PointerReleasedEventArgs e)
- {
- if (_captured)
- {
- if (e.InitialPressMouseButton == MouseButton.Left)
- {
- Released();
- }
- _captured = false;
- }
- }
- private void PointerCaptureLost(object? sender, PointerCaptureLostEventArgs e)
- {
- Released();
- _captured = false;
- }
- private void Released()
- {
- if (!_enableDrag)
- {
- return;
- }
- RemoveTransforms(_itemsControl);
- if (_itemsControl is not null)
- {
- foreach (var control in _itemsControl.GetRealizedContainers())
- {
- SetDraggingPseudoClasses(control, true);
- }
- }
- if (_dragStarted)
- {
- if (_draggedIndex >= 0 && _targetIndex >= 0 && _draggedIndex != _targetIndex)
- {
- MoveDraggedItem(_itemsControl, _draggedIndex, _targetIndex);
- }
- }
- if (_itemsControl is not null)
- {
- foreach (var control in _itemsControl.GetRealizedContainers())
- {
- SetDraggingPseudoClasses(control, false);
- }
- }
- if (_draggedContainer is not null)
- {
- SetDraggingPseudoClasses(_draggedContainer, false);
- }
- _draggedIndex = -1;
- _targetIndex = -1;
- _enableDrag = false;
- _dragStarted = false;
- _itemsControl = null;
- _draggedContainer = null;
- }
- private void AddTransforms(ItemsControl? itemsControl)
- {
- if (itemsControl?.Items is null)
- {
- return;
- }
- var i = 0;
- foreach (var _ in itemsControl.Items)
- {
- var container = itemsControl.ContainerFromIndex(i);
- if (container is not null)
- {
- SetTranslateTransform(container, 0, 0);
- }
-
- i++;
- }
- }
- private void RemoveTransforms(ItemsControl? itemsControl)
- {
- if (itemsControl?.Items is null)
- {
- return;
- }
- var i = 0;
- foreach (var _ in itemsControl.Items)
- {
- var container = itemsControl.ContainerFromIndex(i);
- if (container is not null)
- {
- SetTranslateTransform(container, 0, 0);
- }
-
- i++;
- }
- }
- private void MoveDraggedItem(ItemsControl? itemsControl, int draggedIndex, int targetIndex)
- {
- if (itemsControl?.ItemsSource is IList itemsSource)
- {
- var draggedItem = itemsSource[draggedIndex];
- itemsSource.RemoveAt(draggedIndex);
- itemsSource.Insert(targetIndex, draggedItem);
- if (itemsControl is SelectingItemsControl selectingItemsControl)
- {
- selectingItemsControl.SelectedIndex = targetIndex;
- }
- }
- else
- {
- if (itemsControl?.Items is {IsReadOnly: false} itemCollection)
- {
- var draggedItem = itemCollection[draggedIndex];
- itemCollection.RemoveAt(draggedIndex);
- itemCollection.Insert(targetIndex, draggedItem);
- if (itemsControl is SelectingItemsControl selectingItemsControl)
- {
- selectingItemsControl.SelectedIndex = targetIndex;
- }
- }
- }
- }
- private void PointerMoved(object? sender, PointerEventArgs e)
- {
- var properties = e.GetCurrentPoint(AssociatedObject).Properties;
- if (_captured
- && properties.IsLeftButtonPressed)
- {
- if (_itemsControl?.Items is null || _draggedContainer?.RenderTransform is null || !_enableDrag)
- {
- return;
- }
- var orientation = Orientation;
- var position = e.GetPosition(_itemsControl);
- var delta = orientation == Orientation.Horizontal ? position.X - _start.X : position.Y - _start.Y;
- if (!_dragStarted)
- {
- var diff = _start - position;
- var horizontalDragThreshold = HorizontalDragThreshold;
- var verticalDragThreshold = VerticalDragThreshold;
- if (orientation == Orientation.Horizontal)
- {
- if (Math.Abs(diff.X) > horizontalDragThreshold)
- {
- _dragStarted = true;
- }
- else
- {
- return;
- }
- }
- else
- {
- if (Math.Abs(diff.Y) > verticalDragThreshold)
- {
- _dragStarted = true;
- }
- else
- {
- return;
- }
- }
- }
- if (orientation == Orientation.Horizontal)
- {
- SetTranslateTransform(_draggedContainer, delta, 0);
- }
- else
- {
- SetTranslateTransform(_draggedContainer, 0, delta);
- }
- _draggedIndex = _itemsControl.IndexFromContainer(_draggedContainer);
- _targetIndex = -1;
- var draggedBounds = _draggedContainer.Bounds;
- var draggedStart = orientation == Orientation.Horizontal ? draggedBounds.X : draggedBounds.Y;
- var draggedDeltaStart = orientation == Orientation.Horizontal
- ? draggedBounds.X + delta
- : draggedBounds.Y + delta;
- var draggedDeltaEnd = orientation == Orientation.Horizontal
- ? draggedBounds.X + delta + draggedBounds.Width
- : draggedBounds.Y + delta + draggedBounds.Height;
- var i = 0;
- foreach (var _ in _itemsControl.Items)
- {
- var targetContainer = _itemsControl.ContainerFromIndex(i);
- if (targetContainer?.RenderTransform is null || ReferenceEquals(targetContainer, _draggedContainer))
- {
- i++;
- continue;
- }
- var targetBounds = targetContainer.Bounds;
- var targetStart = orientation == Orientation.Horizontal ? targetBounds.X : targetBounds.Y;
- var targetMid = orientation == Orientation.Horizontal
- ? targetBounds.X + targetBounds.Width / 2
- : targetBounds.Y + targetBounds.Height / 2;
- var targetIndex = _itemsControl.IndexFromContainer(targetContainer);
- if (targetStart > draggedStart && draggedDeltaEnd >= targetMid)
- {
- if (orientation == Orientation.Horizontal)
- {
- SetTranslateTransform(targetContainer, -draggedBounds.Width, 0);
- }
- else
- {
- SetTranslateTransform(targetContainer, 0, -draggedBounds.Height);
- }
- _targetIndex = _targetIndex == -1 ? targetIndex :
- targetIndex > _targetIndex ? targetIndex : _targetIndex;
- }
- else if (targetStart < draggedStart && draggedDeltaStart <= targetMid)
- {
- if (orientation == Orientation.Horizontal)
- {
- SetTranslateTransform(targetContainer, draggedBounds.Width, 0);
- }
- else
- {
- SetTranslateTransform(targetContainer, 0, draggedBounds.Height);
- }
- _targetIndex = _targetIndex == -1 ? targetIndex :
- targetIndex < _targetIndex ? targetIndex : _targetIndex;
- }
- else
- {
- if (orientation == Orientation.Horizontal)
- {
- SetTranslateTransform(targetContainer, 0, 0);
- }
- else
- {
- SetTranslateTransform(targetContainer, 0, 0);
- }
- }
- i++;
- }
- }
- }
- private void SetDraggingPseudoClasses(Control control, bool isDragging)
- {
- if (isDragging)
- {
- ((IPseudoClasses)control.Classes).Add(":dragging");
- }
- else
- {
- ((IPseudoClasses)control.Classes).Remove(":dragging");
- }
- }
- private void SetTranslateTransform(Control control, double x, double y)
- {
- var transformBuilder = new TransformOperations.Builder(1);
- transformBuilder.AppendTranslate(x, y);
- control.RenderTransform = transformBuilder.Build();
- }
- }
|