Drag and Drop ListBoxItems generically
我想使用MVVM模式在多个数据绑定的列表框上实现拖放。我不想在列表框之间拖放,而是希望用户能够在每个列表框中拖放列表框项,以便重新排列排序顺序。我在上面找到了这篇文章,这对我很有帮助:
wpf c:通过拖放重新排列列表框中的项目
我想尝试使这些方法更"通用",以便它可以在绑定到不同类型的可观察集合的任何列表框上工作。假设这是视图中的XAML:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | <Window x:Class="WpfApplication1.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="MainWindow" Height="350" Width="525"> <Window.Resources> <Style TargetType="{x:Type ListBoxItem}" x:Key="ListBoxItemDragDrop"> <Setter Property="AllowDrop" Value="True" /> <EventSetter Event="PreviewMouseMove" Handler="ListBoxItem_PreviewMouseMoveEvent" /> <EventSetter Event="Drop" Handler="listbox1_Drop" /> </Style> </Window.Resources> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition /> <ColumnDefinition /> </Grid.ColumnDefinitions> <ListBox Name="listbox1" ItemsSource="{Binding OCofType1}" ItemContainerStyle="{StaticResource ListBoxItemDragDrop}" /> <ListBox Name="listbox2" Grid.Column="1" ItemsSource="{Binding OCofType2}" ItemContainerStyle="{StaticResource ListBoxItemDragDrop}"/> </Grid> </Window> |
其中oc绑定为observalbecollection
1 2 3 4 5 6 7 8 9 | void ListBoxItem_PreviewMouseMoveEvent(object sender, MouseEventArgs e) { if (e.LeftButton == MouseButtonState.Pressed && sender is ListBoxItem) { ListBoxItem draggedItem = (ListBoxItem)sender; DragDrop.DoDragDrop(draggedItem, draggedItem.DataContext, DragDropEffects.Move); draggedItem.IsSelected = true; } } |
这似乎足够"通用"。接下来是方法,同样在视图中,处理放置,这就是我被卡住的地方:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | void ListBoxItem_Drop(object sender, DragEventArgs e) { object Target = ((ListBoxItem)(sender)).DataContext; object Dropped = e.Data.GetData(Target.GetType()); int RemoveIndex = listbox1.Items.IndexOf(Dropped); int TargetIndex = listbox1.Items.IndexOf(Target); ListBox container = ((DependencyObject)sender).GetAncestor<ListBox>(); if (RemoveIndex < TargetIndex) { //THESE WILL NOT WORK IF I AM DOING BINDINGS THROUGH THE ITEMSSOURCE //container.Items.Insert(RemoveIndex + 1, Dropped); //container.Items.RemoveAt(RemoveIndex); //SO HAVE TO USE THE ITEMSSOURCE DIRECTLY BUT HOW WITHOUT A SPECIFIC CAST TO THE OC<TYPE#> container.ItemsSource.Insert(RemoveIndex + 1, Dropped); //ERROR: IENUMERATOR DOES NOT CONTAIN A DEFINITION FOR INSERT.... container.ItemsSource.RemoveAt(RemoveIndex); //ERROR: IENUMERATOR DOES NOT CONTAIN A DEFINITION FOR REMOVEAT.... } else if (container.Items.Count > RemoveIndex) { //THESE WILL NOT WORK IF I AM DOING BINDINGS THROUGH THE ITEMSSOURCE //container.Items.Insert(TargetIndex, Dropped); //container.Items.RemoveAt(RemoveIndex + 1); //SO HAVE TO USE THE ITEMSSOURCE DIRECTLY BUT HOW WITHOUT A SPECIFIC CAST TO THE OC<TYPE#> container.ItemsSource.Insert(TargetIndex, Dropped); //ERROR: IENUMERATOR DOES NOT CONTAIN A DEFINITION FOR INSERT.... container.ItemsSource.RemoveAt(RemoveIndex + 1); //ERROR: IENUMERATOR DOES NOT CONTAIN A DEFINITION FOR REMOVEAT.... } } |
查找祖先函数是这样的(从SO上的另一篇文章):
1 2 3 4 5 6 7 8 9 10 11 12 | static T FindAnchestor<T>(DependencyObject current) where T : DependencyObject { do { if (current is T) return (T)current; current = VisualTreeHelper.GetParent(current); } while (current != null); return null; } |
在drop函数中,如果我直接添加到列表框的列表集合中,这可能有效。但是,由于我正在对ViewModel中的集合进行绑定,因此会错误地说我必须通过itemssource处理这些对象。但是,如果我使用itemssource,我将不得不为每个oc类型生成函数的多个版本,因为我不会在运行时对itemssource进行强制转换。我可以使用一个if语句将其保持为1个函数,该语句决定显式地将其强制转换为什么,但它将非常干净,不必记住为应用它的每个新OC更新if。
问题是,在不知道要强制转换什么内容的情况下,如何向itemsource添加/移动项?
谢谢你的帮助。
这是一个非常方便的工具/框架。Gong WPF拖放
谢谢你们的帮助。结果比我想的简单多了,因为oc实现了它,而且它像一个魅力一样工作,所以不得不把它投射给一个ilist。这里是所有可能需要它的人的完整代码。这可以应用于由实现IList的任何集合(包括可观察集合和通用列表)支持的任何列表框,以及其他列表:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | void ListBoxItem_Drop(object sender, DragEventArgs e) { object Target = ((ListBoxItem)(sender)).DataContext; object Dropped = e.Data.GetData(Target.GetType()); ListBox container = ((DependencyObject)sender).GetAncestor<ListBox>(); int RemoveIndex = container.Items.IndexOf(Dropped); int TargetIndex = container.Items.IndexOf(Target); IList IList = (IList)container.ItemsSource; if (RemoveIndex < TargetIndex) { IList.Insert(TargetIndex + 1, Dropped); IList.RemoveAt(RemoveIndex); } else if (IList.Count > RemoveIndex) { IList.Insert(TargetIndex, Dropped); IList.RemoveAt(RemoveIndex + 1); } } |
厄尼