Drag And Drop Frameworkを使用します。ドラッグさせたいものにDragSourceを指定し、ドロップ先にDropTargetを指定します。Dropイベントのイベントハンドラーで、ドラッグ元からドラックされた要素を取り除き、ドラッグ先に追加します。
以下、例として、ListView間でドラッグ&ドロップするコードを示します。ほかのコントロールの場合でも、同じコントロール同士でも違うコントロール同士でも、同じ考え方で実装できます。
実現イメージ
DragSource
ドラッグさせたいのは、左側のListViewの項目なので、左側のListViewの項目に対してDragSourceを指定します(14~17行目)。
<Grid> <Grid.ColumnDefinitions> <ColumnDefinition Width="*"/> <ColumnDefinition Width="*"/> </Grid.ColumnDefinitions> <!-- 左側のListView --> <ListView Grid.Column="0" ItemsSource="{Binding Tasks1}" Margin="10" BorderBrush="Black" BorderThickness="2"> <ListView.ItemTemplate> <DataTemplate> <Border BorderBrush="LightGray" Background="White" BorderThickness="2" Margin="0,0,-1,2" > ... <!--DragSourceを追加--> <ig:DragDropManager.DragSource> <ig:DragSource IsDraggable="True" DragChannels="ChannelA" Drop="DragSource_Drop"> </ig:DragSource> </ig:DragDropManager.DragSource> </Border> </DataTemplate> </ListView.ItemTemplate> </ListView> <!-- 右側のListView --> <ListView Grid.Column="1" ItemsSource="{Binding Tasks2}" Margin="10" BorderBrush="Black" BorderThickness="2"> ... </ListView> </Grid>
DropTarget
右側のListViewの上でドロップさせたいので、右側のListViewに対してDropTargetを設定します(18~21行目)。
<Grid> <Grid.ColumnDefinitions> <ColumnDefinition Width="*"/> <ColumnDefinition Width="*"/> </Grid.ColumnDefinitions> <!-- 左側のListView --> <ListView Grid.Column="0" ItemsSource="{Binding Tasks1}" Margin="10" BorderBrush="Black" BorderThickness="2"> ... </ListView> <!-- 右側のListView --> <ListView Grid.Column="1" ItemsSource="{Binding Tasks2}" Margin="10" BorderBrush="Black" BorderThickness="2"> ... </ListView.ItemTemplate> <!-- DropTargetの追加 --> <ig:DragDropManager.DropTarget> <ig:DropTarget IsDropTarget="True" DropChannels="ChannelA"> </ig:DropTarget> </ig:DragDropManager.DropTarget> </ListView> </Grid>
DropTargetをListViewの項目ではなくListViewに対して設定していることに注意してください。
DragSourceもDropTargetもビジュアルツリー上にあるUI要素に対して設定する必要があります。ここでもしDropTargetをListViewの項目に設定してしまうと、ListViewの中身が空っぽの時にドロップができなくなります。それを避けるために、ListView自体にDropTargetを設定しています。
Dropイベントハンドラー
ドラッグされてきた項目のインスタンス、ドラッグ元とドロップ先の各ListView、ドラッグ元とドロップ先の各ListViewにバインドされたコレクション、をそれぞれ取り出し、ドラッグされた項目をドラッグ元のコレクションから削除し、ドロップ先のコレクションに追加します。
private void DragSource_Drop(object sender, Infragistics.DragDrop.DropEventArgs e) { // ドラッグ元のUI要素(今回の場合Border)と紐づいているTaskItemを取り出す。 Border sourcePanel = e.DragSource as Border; TaskItem sourceTask = sourcePanel.DataContext as TaskItem; // ドラッグ元のListViewを取得 // https://jp.infragistics.com/help/wpf/infragisticswpf~infragistics.windows.utilities~getancestorfromtype(dependencyobject,type,boolean) ListView sourceListView = Utilities.GetAncestorFromType(sourcePanel, typeof(ListView), false) as ListView; // ドロップ先のListViewを取得 ListView targetListView = e.DropTarget as ListView; // ドラッグ元とドロップ先のListViewにバインドされているコレクションを取得する。 ObservableCollection<TaskItem> sourceTasks = sourceListView.ItemsSource as ObservableCollection<TaskItem>; ObservableCollection<TaskItem> targetTasks = targetListView.ItemsSource as ObservableCollection<TaskItem>; // ドラッグされたTaskItemをドラッグ元のコレクションから削除し、ドロップ先のコレクションに追加する。 sourceTasks.Remove(sourceTask); targetTasks.Add(sourceTask); }
今回、使用しているListViewのItemsSourceがビューモデルのコレクションとバインドされているので、ドラッグ元の項目の削除と追加はバインディングソースのコレクションに対して実装しています。
ビュー上のコントロールの位置を単純に入れ替えるだけ、という場合も、同様に実装できます。その場合は、ドラッグされたコントロールを元の親コントロールから削除し、ドロップ先のコントロールに子コントロールとして追加してください。
サンプル
リファレンス
Infragistics Drag and Drop Framework
https://jp.infragistics.com/help/wpf/drag-and-drop-framework