<TabControl assist:SelectorDragDropAttach.IsItemsDragDropEnabled="True" AlternationCount="{Binding ClassInfos.Count}" ContentTemplate="{StaticResource contentTemplate}" ItemContainerStyle="{StaticResource TabItemStyle}" ItemsSource="{Binding ClassInfos}" SelectedIndex="0" />实现效果如下:
public static class SelectorDragDropAttach { public static bool GetIsItemsDragDropEnabled(Selector scrollViewer) { return (bool)scrollViewer.GetValue(IsItemsDragDropEnabledProperty); } public static void SetIsItemsDragDropEnabled(Selector scrollViewer, bool value) { scrollViewer.SetValue(IsItemsDragDropEnabledProperty, value); } public static readonly DependencyProperty IsItemsDragDropEnabledProperty = DependencyProperty.RegisterAttached("IsItemsDragDropEnabled", typeof(bool), typeof(SelectorDragDropAttach), new PropertyMetadata(false, OnIsItemsDragDropEnabledChanged)); private static readonly DependencyProperty SelectorDragDropProperty = DependencyProperty.RegisterAttached("SelectorDragDrop", typeof(SelectorDragDrop), typeof(SelectorDragDropAttach), new PropertyMetadata(null)); // 堆代码 duidaima.com private static void OnIsItemsDragDropEnabledChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { bool b = (bool)e.NewValue; Selector selector = d as Selector; var selectorDragDrop = selector?.GetValue(SelectorDragDropProperty) as SelectorDragDrop; if (selectorDragDrop != null) selectorDragDrop.Selector = null; if (b == false) { selector?.SetValue(SelectorDragDropProperty, null); return; } selector?.SetValue(SelectorDragDropProperty, new SelectorDragDrop(selector)); } }其中SelectorDragDrop就是处理拖拽排序的对象,接下来看下几个主要事件的处理逻辑。
void selector_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e) { if (this.IsMouseOverScrollbar) { //Set the flag to false when cursor is over scrollbar. this.canInitiateDrag = false; return; } int index = this.IndexUnderDragCursor; this.canInitiateDrag = index > -1; if (this.canInitiateDrag) { // Remember the location and index of the SelectorItem the user clicked on for later. this.ptMouseDown = GetMousePosition(this.selector); this.indexToSelect = index; } else { this.ptMouseDown = new Point(-10000, -10000); this.indexToSelect = -1; } }在PreviewMouseMove事件中根据需要拖拽操作的元素创建一个AdornerLayer,实现鼠标拖着元素移动的效果。其实拖拽移动的只是这个AdornerLayer,真实的元素并未移动。
void selector_PreviewMouseMove(object sender, MouseEventArgs e) { if (!this.CanStartDragOperation) return; // Select the item the user clicked on. if (this.selector.SelectedIndex != this.indexToSelect) this.selector.SelectedIndex = this.indexToSelect; // If the item at the selected index is null, there's nothing // we can do, so just return; if (this.selector.SelectedItem == null) return; UIElement itemToDrag = this.GetSelectorItem(this.selector.SelectedIndex); if (itemToDrag == null) return; AdornerLayer adornerLayer = this.ShowDragAdornerResolved ? this.InitializeAdornerLayer(itemToDrag) : null; this.InitializeDragOperation(itemToDrag); this.PerformDragOperation(); this.FinishDragOperation(itemToDrag, adornerLayer); }DragEnter,DragLeave,DragEnter事件中处理AdornerLayer的位置以及是否显示。Drop事件中确定了拖拽操作目标位置以及渲染的数据元素,然后移动元数据,通过数据顺序的变化更新界面的排序。从代码中可以看到列表控件的ItemsSource不能为空,否则拖拽无效。这也是后边将提到的一个缺点。
void selector_Drop(object sender, DragEventArgs e) { if (this.ItemUnderDragCursor != null) this.ItemUnderDragCursor = null; e.Effects = DragDropEffects.None; var itemsSource = this.selector.ItemsSource; if (itemsSource == null) return; int itemsCount = 0; Type type = null; foreach (object obj in itemsSource) { type = obj.GetType(); itemsCount++; } if (itemsCount < 1) return; if (!e.Data.GetDataPresent(type)) return; object data = e.Data.GetData(type); if (data == null) return; int oldIndex = -1; int index = 0; foreach (object obj in itemsSource) { if (obj == data) { oldIndex = index; break; } index++; } int newIndex = this.IndexUnderDragCursor; if (newIndex < 0) { if (itemsCount == 0) newIndex = 0; else if (oldIndex < 0) newIndex = itemsCount; else return; } if (oldIndex == newIndex) return; if (this.ProcessDrop != null) { // Let the client code process the drop. ProcessDropEventArgs args = new ProcessDropEventArgs(itemsSource, data, oldIndex, newIndex, e.AllowedEffects); this.ProcessDrop(this, args); e.Effects = args.Effects; } else { dynamic dItemsSource = itemsSource; if (oldIndex > -1) dItemsSource.Move(oldIndex, newIndex); else dItemsSource.Insert(newIndex, data); e.Effects = DragDropEffects.Move; } }优点与缺点
3.支持多种列表控件扩展。派生自Selector的ListBox,TabControl,ListView,ComboBox都可使用该方法。
3.不支持同时拖拽多个元素。