|
@@ -4,268 +4,248 @@ using System.Windows.Controls;
|
|
|
using System.Windows.Input;
|
|
|
using System.Windows.Media;
|
|
|
|
|
|
-namespace Waaagh.Behaviors {
|
|
|
- sealed public class ItemsDraggable {
|
|
|
- #region Drag Drop Data Format String
|
|
|
- static readonly string DraggableDataFormat = "DraggableData";
|
|
|
- static readonly string DraggableCollectionFormat = "DraggableCollection";
|
|
|
- #endregion
|
|
|
+namespace Waaagh.Behaviors;
|
|
|
|
|
|
- #region Enabled
|
|
|
- static public bool GetEnabled(DependencyObject obj) {
|
|
|
- return (bool)obj.GetValue(EnabledProperty);
|
|
|
- }
|
|
|
+sealed public class ItemsDraggable {
|
|
|
+ #region Drag Drop Data Format String
|
|
|
+ static readonly string DraggableDataFormat = "DraggableData";
|
|
|
+ static readonly string DraggableCollectionFormat = "DraggableCollection";
|
|
|
+ #endregion
|
|
|
|
|
|
- static public void SetEnabled(DependencyObject obj, bool value) {
|
|
|
- obj.SetValue(EnabledProperty, value);
|
|
|
- }
|
|
|
+ #region Dragging Data Object
|
|
|
+ private static DataObject? GetDraggingData(DependencyObject obj) {
|
|
|
+ return (DataObject?)obj.GetValue(DraggingDataProperty);
|
|
|
+ }
|
|
|
|
|
|
- static public readonly DependencyProperty EnabledProperty =
|
|
|
- DependencyProperty.RegisterAttached("Enabled", typeof(bool), typeof(ItemsDraggable),
|
|
|
- new PropertyMetadata(false, OnEnabledChanged));
|
|
|
+ private static void SetDraggingData(DependencyObject obj, DataObject? value) {
|
|
|
+ obj.SetValue(DraggingDataProperty, value);
|
|
|
+ }
|
|
|
|
|
|
- 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;
|
|
|
- }
|
|
|
- }
|
|
|
+ private static readonly DependencyProperty DraggingDataProperty = DependencyProperty.RegisterAttached(
|
|
|
+ "DraggingData", typeof(DataObject), typeof(ItemsDraggable), new PropertyMetadata(null));
|
|
|
+ #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);
|
|
|
+ }
|
|
|
|
|
|
- public static Func<object, Type, IList?> GetQueryTargetCollection(DependencyObject obj) {
|
|
|
- return (Func<object, Type, IList?>)obj.GetValue(QueryTargetCollectionProperty);
|
|
|
+ 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'.");
|
|
|
+ }
|
|
|
+ // if (itemsControl.ItemsSource is IList == false) {
|
|
|
+ // throw new InvalidOperationException("The attached control must have an 'ItemsSource' implements 'IList'.");
|
|
|
+ // }
|
|
|
+ itemsControl.AllowDrop = false;
|
|
|
+ itemsControl.PreviewMouseLeftButtonDown -= ItemsDraggable_PreviewMouseLeftButtonDown;
|
|
|
+ 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.PreviewMouseLeftButtonDown += ItemsDraggable_PreviewMouseLeftButtonDown;
|
|
|
+ itemsControl.MouseMove += ItemsDraggable_MouseMove;
|
|
|
+ itemsControl.DragEnter += ItemsDraggable_DragEnter;
|
|
|
+ itemsControl.DragOver += ItemsDraggable_DragOver;
|
|
|
+ itemsControl.DragLeave += ItemsDraggable_DragLeave;
|
|
|
+ itemsControl.Drop += ItemsDraggable_Drop;
|
|
|
}
|
|
|
+ }
|
|
|
+ #endregion
|
|
|
|
|
|
- public static void SetQueryTargetCollection(DependencyObject obj, Func<object, Type, IList?> value) {
|
|
|
- obj.SetValue(QueryTargetCollectionProperty, value);
|
|
|
+ #region Drag Drop Event
|
|
|
+ private static void ItemsDraggable_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs args) {
|
|
|
+ if (sender is ItemsControl control == false) {
|
|
|
+ return;
|
|
|
}
|
|
|
+ if (control.ItemsSource is IList == false) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ if (GetElement(control, args.GetPosition(control)) is Control sourceControl == false) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ if (GetParent(sourceControl) is ItemsControl sourceItemsControl == false) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ DataObject data = new DataObject();
|
|
|
+ data.SetData(DraggableDataFormat, sourceControl.DataContext);
|
|
|
+ data.SetData(DraggableCollectionFormat, sourceItemsControl.ItemsSource);
|
|
|
+ SetDraggingData(control, data);
|
|
|
+ }
|
|
|
|
|
|
- public static readonly DependencyProperty QueryTargetCollectionProperty =
|
|
|
- DependencyProperty.RegisterAttached("QueryTargetCollection", typeof(Func<object, Type, IList?>), typeof(ItemsDraggable), new PropertyMetadata(null));
|
|
|
-
|
|
|
+ static private void ItemsDraggable_MouseMove(object sender, MouseEventArgs args) {
|
|
|
+ if (args.LeftButton != MouseButtonState.Pressed) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ if (sender is ItemsControl source == false) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ if (GetDraggingData(source) is DataObject data == false) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ SetDraggingData(source, null);
|
|
|
+ DragDrop.DoDragDrop(source, data, DragDropEffects.Move);
|
|
|
+ }
|
|
|
|
|
|
- #endregion
|
|
|
+ static private void ItemsDraggable_DragEnter(object sender, DragEventArgs args) {
|
|
|
|
|
|
- #region Drag Drop Event
|
|
|
- static private void ItemsDraggable_MouseMove(object sender, MouseEventArgs args) {
|
|
|
- if (args.LeftButton != MouseButtonState.Pressed) {
|
|
|
- return;
|
|
|
- }
|
|
|
- if (sender is ItemsControl source == false) {
|
|
|
- return;
|
|
|
- }
|
|
|
- if (source.ItemsSource is IList == false) {
|
|
|
- return;
|
|
|
- }
|
|
|
- if (GetElement(source, args.GetPosition(source)) is Control sourceControl == false) {
|
|
|
- return;
|
|
|
- }
|
|
|
- if (GetParent(sourceControl) is ItemsControl sourceItemsControl == 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_DragOver(object sender, DragEventArgs args) {
|
|
|
|
|
|
- static private void ItemsDraggable_DragEnter(object sender, DragEventArgs args) {
|
|
|
+ }
|
|
|
|
|
|
- }
|
|
|
+ static private void ItemsDraggable_DragLeave(object sender, DragEventArgs args) {
|
|
|
|
|
|
- static private void ItemsDraggable_DragOver(object sender, DragEventArgs args) {
|
|
|
+ }
|
|
|
|
|
|
+ static private void ItemsDraggable_Drop(object sender, DragEventArgs args) {
|
|
|
+ if (args.Effects == DragDropEffects.None) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ if (sender is ItemsControl control == false) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ // 获取 放置目标
|
|
|
+ if (GetElement(control, args.GetPosition(control)) is Control targetControl == false) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ if (GetParent(targetControl) is ItemsControl targetItemsControl == false) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ object? targetData = targetControl.DataContext;
|
|
|
+ IList? targetCollection = targetItemsControl.ItemsSource as IList;
|
|
|
+ if (targetCollection == null) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ // 获取 拖拽源
|
|
|
+ object? sourceData = args.Data.GetData(DraggableDataFormat) as object;
|
|
|
+ IList? sourceCollection = args.Data.GetData(DraggableCollectionFormat) as IList;
|
|
|
+ if (sourceData == null || sourceCollection == null) {
|
|
|
+ return;
|
|
|
}
|
|
|
+ // 执行 拖拽影响
|
|
|
+ DoDragDropEffects(control, args.Effects, sourceCollection, sourceData, targetCollection, targetData);
|
|
|
+ }
|
|
|
+ #endregion
|
|
|
+
|
|
|
+ #region Drag Drop Effects
|
|
|
+ public delegate void DoEffectsCallbackHandler(object sender, DoEffectsCallbackArgs args);
|
|
|
|
|
|
- static private void ItemsDraggable_DragLeave(object sender, DragEventArgs args) {
|
|
|
+ public class DoEffectsCallbackArgs {
|
|
|
+ public IList SourceCollection { get; }
|
|
|
+ public object SourceData { get; }
|
|
|
+ public IList TargetCollection { get; }
|
|
|
+ public object? TargetData { get; }
|
|
|
|
|
|
+ public DoEffectsCallbackArgs(IList sourceCollection, object sourceData, IList targetCollection, object? targetData) {
|
|
|
+ SourceCollection = sourceCollection;
|
|
|
+ SourceData = sourceData;
|
|
|
+ TargetCollection = targetCollection;
|
|
|
+ TargetData = targetData;
|
|
|
}
|
|
|
+ }
|
|
|
|
|
|
- static private void ItemsDraggable_Drop(object sender, DragEventArgs args) {
|
|
|
- if (args.Effects == DragDropEffects.None) {
|
|
|
- return;
|
|
|
- }
|
|
|
- if (sender is ItemsControl target == false) {
|
|
|
- return;
|
|
|
+ static private void DoDragDropEffects(ItemsControl control, 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;
|
|
|
}
|
|
|
- // 获取 放置目标
|
|
|
- if (GetElement(target, args.GetPosition(target)) is Control targetControl == false) {
|
|
|
- return;
|
|
|
+ case DragDropEffects.All: {
|
|
|
+ break;
|
|
|
}
|
|
|
- if (GetParent(targetControl) is ItemsControl targetItemsControl == false) {
|
|
|
- return;
|
|
|
+ case DragDropEffects.Copy: {
|
|
|
+ break;
|
|
|
}
|
|
|
- if (targetItemsControl.ItemsSource is IList targetCollection == false) {
|
|
|
- return;
|
|
|
+ case DragDropEffects.Move: {
|
|
|
+ GetDoMoveEffect(control)?.Invoke(control, new DoEffectsCallbackArgs(sourceCollection, sourceData, targetCollection, targetData));
|
|
|
+ break;
|
|
|
}
|
|
|
- // 获取 拖拽源
|
|
|
- object? sourceData = args.Data.GetData(DraggableDataFormat) as object;
|
|
|
- IList? sourceCollection = args.Data.GetData(DraggableCollectionFormat) as IList;
|
|
|
- if (sourceData == null || sourceCollection == null) {
|
|
|
- return;
|
|
|
+ case DragDropEffects.Link: {
|
|
|
+ break;
|
|
|
}
|
|
|
- // 判断 集合元素类型
|
|
|
- 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 末尾
|
|
|
+ default: {
|
|
|
+ break;
|
|
|
}
|
|
|
- // 执行 拖拽影响
|
|
|
- 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;
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
+ #region Do Move Effect Callback
|
|
|
+ public static DoEffectsCallbackHandler GetDoMoveEffect(DependencyObject obj) {
|
|
|
+ return (DoEffectsCallbackHandler)obj.GetValue(DoMoveEffectProperty);
|
|
|
+ }
|
|
|
|
|
|
- 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);
|
|
|
- }
|
|
|
+ public static void SetDoMoveEffect(DependencyObject obj, DoEffectsCallbackHandler value) {
|
|
|
+ obj.SetValue(DoMoveEffectProperty, value);
|
|
|
+ }
|
|
|
|
|
|
- #endregion
|
|
|
+ public static readonly DependencyProperty DoMoveEffectProperty = DependencyProperty.RegisterAttached(
|
|
|
+ "DoMoveEffect", typeof(DoEffectsCallbackHandler), typeof(ItemsDraggable), new PropertyMetadata(null));
|
|
|
+ #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;
|
|
|
- }
|
|
|
+ #endregion
|
|
|
|
|
|
- static private Control? GetElement(ItemsControl source, Point point) {
|
|
|
- if (GetItemContainerType(source) is Type type == false) {
|
|
|
- return null;
|
|
|
- }
|
|
|
- DependencyObject? dp = source.InputHitTest(point) as DependencyObject;
|
|
|
- while (dp != null) {
|
|
|
- if (dp.GetType().Equals(type)) {
|
|
|
- break;
|
|
|
- }
|
|
|
- dp = VisualTreeHelper.GetParent(dp);
|
|
|
+ #region Helper
|
|
|
+ static private Control? GetElement(ItemsControl source, Point point) {
|
|
|
+ if (GetItemContainerType(source) is Type type == false) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ DependencyObject? dp = source.InputHitTest(point) as DependencyObject;
|
|
|
+ while (dp != null) {
|
|
|
+ if (dp.GetType().Equals(type)) {
|
|
|
+ break;
|
|
|
}
|
|
|
- return dp as Control;
|
|
|
+ dp = VisualTreeHelper.GetParent(dp);
|
|
|
}
|
|
|
+ return dp as Control;
|
|
|
+ }
|
|
|
|
|
|
- static private ItemsControl? GetParent(Control control) {
|
|
|
- for (DependencyObject dp = VisualTreeHelper.GetParent(control); dp != null; dp = VisualTreeHelper.GetParent(dp)) {
|
|
|
- if (dp is ItemsControl parent && parent.ItemContainerGenerator.ContainerFromItem(control.DataContext) != null) {
|
|
|
- return parent;
|
|
|
- }
|
|
|
+ static private ItemsControl? GetParent(Control control) {
|
|
|
+ for (DependencyObject dp = VisualTreeHelper.GetParent(control); dp != null; dp = VisualTreeHelper.GetParent(dp)) {
|
|
|
+ if (dp is ItemsControl parent && parent.ItemContainerGenerator.ContainerFromItem(control.DataContext) != null) {
|
|
|
+ return parent;
|
|
|
}
|
|
|
- return null;
|
|
|
}
|
|
|
+ return null;
|
|
|
+ }
|
|
|
|
|
|
- static private Type? GetItemContainerType(ItemsControl source) {
|
|
|
- if (source == null) {
|
|
|
- return null;
|
|
|
- }
|
|
|
- if (source is ListView) {
|
|
|
- return typeof(ListViewItem);
|
|
|
- }
|
|
|
- else if (source is ListBox) {
|
|
|
- return typeof(ListBoxItem);
|
|
|
- }
|
|
|
- else if (source is TreeView || source is TreeViewItem) {
|
|
|
- return typeof(TreeViewItem);
|
|
|
- }
|
|
|
- // else if (source is GridView) {
|
|
|
- // return typeof(GridViewColumn);
|
|
|
- // }
|
|
|
- if (source.Items.Count > 0) {
|
|
|
- return source.ItemContainerGenerator.ContainerFromIndex(0)?.GetType();
|
|
|
- }
|
|
|
+ static private Type? GetItemContainerType(ItemsControl source) {
|
|
|
+ if (source == null) {
|
|
|
return null;
|
|
|
}
|
|
|
-
|
|
|
- static private bool CheckCollectionElementType(IList collection1, IList collection2) {
|
|
|
- return collection1.GetType().GetGenericArguments()[0] == collection2.GetType().GetGenericArguments()[0];
|
|
|
+ if (source is ListView) {
|
|
|
+ return typeof(ListViewItem);
|
|
|
}
|
|
|
- #endregion
|
|
|
+ else if (source is ListBox) {
|
|
|
+ return typeof(ListBoxItem);
|
|
|
+ }
|
|
|
+ else if (source is TreeView || source is TreeViewItem) {
|
|
|
+ return typeof(TreeViewItem);
|
|
|
+ }
|
|
|
+ // else if (source is GridView) {
|
|
|
+ // return typeof(GridViewColumn);
|
|
|
+ // }
|
|
|
+ if (source.Items.Count > 0) {
|
|
|
+ return source.ItemContainerGenerator.ContainerFromIndex(0)?.GetType();
|
|
|
+ }
|
|
|
+ return null;
|
|
|
}
|
|
|
+ #endregion
|
|
|
+
|
|
|
}
|