MVVM パターンを使用して、データ バインドされた複数のリスト ボックスにドラッグ アンド ドロップを実装したいと考えています。リストボックス間でドラッグアンドドロップしようとしているのではなく、ユーザーが各リストボックスでリストボックスアイテムをドラッグ/ドロップできるようにして、並べ替え順序を再配置できるようにしたいと考えています。SOでこの投稿を見つけました。これは非常に役に立ちました。
WPF C#: ドラッグ アンド ドロップでリスト ボックス内の項目を再配置する
メソッドをより「汎用的」にして、さまざまなタイプの監視可能なコレクションにバインドされているリストボックスで機能するようにしたかったのです。これが VIEW 内の私の XAML だとします。
<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;
}
ドロップ機能では、ListBox の List コレクションに直接追加していた場合、これが機能する可能性があります。しかし、VIEWMODEL でコレクションへのバインドを行っているため、ItemsSource を介してこれらのオブジェクトを操作する必要があるというエラーが表示されます。しかし、ItemsSource を使用する場合、実行時に ItemsSource もキャストする必要がないため、OC タイプごとに可変バージョンの関数を作成する必要があります。何にキャストするかを明示的に決定する if ステートメントを使用して 1 つの関数に抑えることができますが、新しい OC が適用されるたびにそれを更新することを覚えておく必要がないため、はるかにクリーンになります。
問題は、キャストするものを正確に知らずに、アイテムを ItemsSource に追加/移動するにはどうすればよいかということです。
助けてくれてありがとう。