|
@@ -0,0 +1,218 @@
|
|
|
+using System.Collections;
|
|
|
+using System.Windows;
|
|
|
+using System.Windows.Controls;
|
|
|
+using System.Windows.Input;
|
|
|
+
|
|
|
+namespace Waaagh.Behaviors {
|
|
|
+ sealed public class ItemsDraggable {
|
|
|
+ #region Drag Drop Data Format String
|
|
|
+ static readonly string DraggableDataFormat = "DraggableData";
|
|
|
+ static readonly string DraggableCollectionFormat = "DraggableCollection";
|
|
|
+ #endregion
|
|
|
+
|
|
|
+ #region Enabled
|
|
|
+ static public bool GetEnabled(DependencyObject obj) {
|
|
|
+ return (bool)obj.GetValue(EnabledProperty);
|
|
|
+ }
|
|
|
+
|
|
|
+ static public void SetEnabled(DependencyObject obj, bool value) {
|
|
|
+ obj.SetValue(EnabledProperty, value);
|
|
|
+ }
|
|
|
+
|
|
|
+ static public readonly DependencyProperty EnabledProperty =
|
|
|
+ DependencyProperty.RegisterAttached("Enabled", typeof(bool), typeof(ItemsDraggable),
|
|
|
+ new PropertyMetadata(false, OnEnabledChanged));
|
|
|
+
|
|
|
+ static private void OnEnabledChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args) {
|
|
|
+ if (sender is ItemsControl itemsControl == false) {
|
|
|
+ throw new InvalidOperationException("The attached control is not an 'ItemsControl'.");
|
|
|
+ }
|
|
|
+ itemsControl.AllowDrop = false;
|
|
|
+ itemsControl.MouseMove -= ItemsDraggable_MouseMove;
|
|
|
+ itemsControl.DragEnter -= ItemsDraggable_DragEnter;
|
|
|
+ itemsControl.DragOver -= ItemsDraggable_DragOver;
|
|
|
+ itemsControl.DragLeave -= ItemsDraggable_DragLeave;
|
|
|
+ itemsControl.Drop -= ItemsDraggable_Drop;
|
|
|
+ if (GetEnabled(itemsControl) == true) {
|
|
|
+ itemsControl.AllowDrop = true;
|
|
|
+ itemsControl.MouseMove += ItemsDraggable_MouseMove;
|
|
|
+ itemsControl.DragEnter += ItemsDraggable_DragEnter;
|
|
|
+ itemsControl.DragOver += ItemsDraggable_DragOver;
|
|
|
+ itemsControl.DragLeave += ItemsDraggable_DragLeave;
|
|
|
+ itemsControl.Drop += ItemsDraggable_Drop;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ public static Func<object, Type, IList?> GetQueryTargetCollection(DependencyObject obj) {
|
|
|
+ return (Func<object, Type, IList?>)obj.GetValue(QueryTargetCollectionProperty);
|
|
|
+ }
|
|
|
+
|
|
|
+ public static void SetQueryTargetCollection(DependencyObject obj, Func<object, Type, IList?> value) {
|
|
|
+ obj.SetValue(QueryTargetCollectionProperty, value);
|
|
|
+ }
|
|
|
+
|
|
|
+ public static readonly DependencyProperty QueryTargetCollectionProperty =
|
|
|
+ DependencyProperty.RegisterAttached("QueryTargetCollection", typeof(Func<object, Type, IList?>), typeof(ItemsDraggable), new PropertyMetadata(null));
|
|
|
+
|
|
|
+
|
|
|
+ #endregion
|
|
|
+
|
|
|
+ #region Drag Drop Event
|
|
|
+ static private void ItemsDraggable_MouseMove(object sender, MouseEventArgs args) {
|
|
|
+ if (args.LeftButton != MouseButtonState.Pressed) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ if (sender is ItemsControl sourceItemsControl == false) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ if (sourceItemsControl.ItemsSource is IList == false) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ if (GetDragDropEventSource(ref sourceItemsControl, out Control sourceControl, args.GetPosition) == false) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ DataObject data = new DataObject();
|
|
|
+ data.SetData(DraggableDataFormat, sourceControl.DataContext);
|
|
|
+ data.SetData(DraggableCollectionFormat, sourceItemsControl.ItemsSource);
|
|
|
+ DragDrop.DoDragDrop(sourceItemsControl, data, DragDropEffects.Move);
|
|
|
+ }
|
|
|
+
|
|
|
+ static private void ItemsDraggable_DragEnter(object sender, DragEventArgs args) {
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ static private void ItemsDraggable_DragOver(object sender, DragEventArgs args) {
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ static private void ItemsDraggable_DragLeave(object sender, DragEventArgs args) {
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ static private void ItemsDraggable_Drop(object sender, DragEventArgs args) {
|
|
|
+ if (args.Effects == DragDropEffects.None) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ if (sender is ItemsControl targetItemsControl == false) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ // 获取 拖拽源
|
|
|
+ object? sourceData = args.Data.GetData(DraggableDataFormat) as object;
|
|
|
+ IList? sourceCollection = args.Data.GetData(DraggableCollectionFormat) as IList;
|
|
|
+ if (sourceData == null || sourceCollection == null) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ // 获取 放置目标
|
|
|
+ if (GetDragDropEventSource(ref targetItemsControl, out Control targetControl, args.GetPosition) == false) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ if (targetItemsControl.ItemsSource is IList targetCollection == false) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ // 判断 集合元素类型
|
|
|
+ object? targetData = targetControl.DataContext;
|
|
|
+ if (CheckCollectionElementType(sourceCollection, targetCollection) == false) {
|
|
|
+ IList? collection = GetQueryTargetCollection(targetItemsControl)?.Invoke(targetControl.DataContext, sourceData.GetType());
|
|
|
+ if (collection == null || CheckCollectionElementType(sourceCollection, collection) == false) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ targetCollection = collection;
|
|
|
+ targetData = null; // 置 targetData 为 null, 尝试将 sourceData 添加到 targetCollection 末尾
|
|
|
+ }
|
|
|
+ // 执行 拖拽影响
|
|
|
+ DoDragDropEffects(args.Effects, sourceCollection, sourceData, targetCollection, targetData);
|
|
|
+ }
|
|
|
+ #endregion
|
|
|
+
|
|
|
+ #region Drag Drop Effects
|
|
|
+ static private void DoDragDropEffects(DragDropEffects effects, IList sourceCollection, object sourceData, IList targetCollection, object? targetData) {
|
|
|
+ if (sourceCollection == null || sourceData == null || targetCollection == null) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ if (object.ReferenceEquals(sourceData, targetData)) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ switch (effects) {
|
|
|
+ case DragDropEffects.Scroll: {
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ case DragDropEffects.All: {
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ case DragDropEffects.Copy: {
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ case DragDropEffects.Move: {
|
|
|
+ DoMoveEffect(sourceCollection, sourceData, targetCollection, targetData);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ case DragDropEffects.Link: {
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ default: {
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ static private void DoMoveEffect(IList sourceCollection, object sourceData, IList targetCollection, object? targetData) {
|
|
|
+ int oldIndex = sourceCollection.IndexOf(sourceData);
|
|
|
+ int newIndex = targetCollection.IndexOf(targetData);
|
|
|
+ if (newIndex == -1) {
|
|
|
+ newIndex = targetCollection.Count - 1;
|
|
|
+ }
|
|
|
+ if (object.ReferenceEquals(sourceCollection, targetCollection)) {
|
|
|
+ if (oldIndex == newIndex) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ // 同个集合, 移除元素后下标可能变化
|
|
|
+ if (oldIndex < newIndex) {
|
|
|
+ newIndex -= 1;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ sourceCollection.RemoveAt(oldIndex);
|
|
|
+ targetCollection.Insert(newIndex + 1, sourceData);
|
|
|
+ }
|
|
|
+
|
|
|
+ #endregion
|
|
|
+
|
|
|
+ #region Helper
|
|
|
+ static private bool GetDragDropEventSource(ref ItemsControl parent, out Control child, Func<IInputElement, Point> getPosition) {
|
|
|
+ if (parent != null) {
|
|
|
+ ItemContainerGenerator generator = parent.ItemContainerGenerator;
|
|
|
+ for (int i = generator.Items.Count - 1; i >= 0; --i) {
|
|
|
+ if (generator.ContainerFromIndex(i) is Control control == false) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ Point position = getPosition(control);
|
|
|
+ if (position.X < 0 || position.X > control.ActualWidth ||
|
|
|
+ position.Y < 0 || position.Y > control.ActualHeight) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ if (control is ContentControl) {
|
|
|
+ child = control;
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ if (control is ItemsControl itemsControl) {
|
|
|
+ ItemsControl prevParent = parent;
|
|
|
+ parent = itemsControl;
|
|
|
+ if (GetDragDropEventSource(ref parent, out child, getPosition) == false) {
|
|
|
+ child = parent;
|
|
|
+ parent = prevParent;
|
|
|
+ }
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ child = default!;
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ static private bool CheckCollectionElementType(IList collection1, IList collection2) {
|
|
|
+ return collection1.GetType().GetGenericArguments()[0] == collection2.GetType().GetGenericArguments()[0];
|
|
|
+ }
|
|
|
+ #endregion
|
|
|
+ }
|
|
|
+}
|