关于c#:一般拖放ListBoxItems

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和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);
        }
}

厄尼