Перетаскивание списков ListBoxItems в целом

Я хочу реализовать перетаскивание нескольких списков, привязанных к данным, с использованием шаблона MVVM. Я не пытаюсь перетащить между списками, но хочу, чтобы пользователь мог перетащить/удалить listboxitems в каждом списке, чтобы они могли изменить порядок сортировки. Я нашел этот пост на SO, который был ОЧЕНЬ полезен:

WPF С#: переупорядочить элементы в списке с помощью перетаскивания

Я хотел попробовать и сделать методы более "родовыми", чтобы он работал на любом списке, который привязан к различным типам наблюдаемых коллекций. Так скажите, что это мой XAML в VIEW:

<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 <Type1 > и ObservalbeCollection <Type2 > . Вот метод в VIEW, который захватывает событие перемещения мыши и проверяет, является ли это перетаскиванием:

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

Это кажется "общим". Далее был бы метод, также в VIEW, обрабатывает капли, и вот где я застрял:

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):

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 это может работать, если я непосредственно добавляю в списки ListBox List. Но так как я делаю привязки к коллекциям в VIEWMODEL, это будет ошибкой, говоря, что я должен работать с этими объектами, хотя с ItemsSource. Но если я использую ItemSource, мне придется создавать изменчивые версии этой функции для каждого типа OC, так как я бы не стал отличать ItemsSource во время выполнения. Я мог бы сохранить его до 1 функции, используя оператор if, который определяет, что его явно использовать, но было бы намного чище не забывать обновлять это, если для каждого нового OC оно применяется.

Вопрос в том, как я могу добавить/переместить элементы в ItemsSource, не зная точно, что тоже делать?

Спасибо за любую помощь.

Ответ 1

Спасибо за помощь ребятам. Оказывается, это намного проще, чем я думал, просто пришлось отдать его в IList, так как OC реализует его, и он работал как шарм. Вот полный код для тех, кому это может понадобиться. Это может быть применено к любому списку, поддерживаемому любой коллекцией, реализующей IList, включая Наблюдаемые Коллекции и общие Списки, среди прочего:

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

Эрни

Ответ 2

Это ОЧЕНЬ удобный инструмент/фреймворк. Gong WPF Drag and Drop