Просмотр исходного кода

添加 ItemsDraggable 附加属性, 拖拽 ItemsControl 子元素以变更顺序或移动其它集合

lintx 2 месяцев назад
Родитель
Сommit
0f794800c9
2 измененных файлов с 219 добавлено и 1 удалено
  1. 219 0
      Waaagh/Behaviors/ItemsDraggable.cs
  2. 0 1
      Waaagh/Waaagh.csproj

+ 219 - 0
Waaagh/Behaviors/ItemsDraggable.cs

@@ -0,0 +1,219 @@
+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?> GetTryGetSubCollection(DependencyObject obj) {
+            return (Func<object, Type, IList?>)obj.GetValue(TryGetSubCollectionProperty);
+        }
+
+        public static void SetTryGetSubCollection(DependencyObject obj, Func<object, Type, IList?> value) {
+            obj.SetValue(TryGetSubCollectionProperty, value);
+        }
+
+        public static readonly DependencyProperty TryGetSubCollectionProperty =
+            DependencyProperty.RegisterAttached("TryGetSubCollection", 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 = GetTryGetSubCollection(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
+    }
+}

+ 0 - 1
Waaagh/Waaagh.csproj

@@ -30,7 +30,6 @@
 	</ItemGroup>
 
 	<ItemGroup>
-		<Folder Include="Behaviors\" />
 		<Folder Include="CustomControl\Units\" />
 		<Folder Include="Themes\CustomControl\Units\" />
 		<Folder Include="Views\Units\" />