WPF+MVVMでドラッグ&ドロップの実装


はじめに

WPF+MVVMでリストにドラッグ&ドロップを実装するための方法です。

方法

実装にあたっては、MVVMの作法に則るために、コードビハインド(xaml.cs)には何も書きたくありません。
このためには、GongSolutions.WPF.DragDropを利用するのが楽です。
これはNugetからインストールすることもできます。

使い方

View側

まず、xamlの名前空間に下記を追加します。

SampleWindow.xaml
xmlns:dd="urn:gong-wpf-dragdrop"

さらに、ドラッグアンドドロップをしたい ListBox などに対して、次のプロパティを追加します。

SampleWindow.xaml
<ListBox ItemsSource="{Binding Files}" 
         dd:DragDrop.IsDropTarget="True" 
         dd:DragDrop.DropHandler="{Binding}"/>
  • 注意
    • Files は、ViewModelの定義に応じて適切なプロパティ名に変更する
    • DropHandler="{Binding}" は、DropHandler に現在の DataContext をバインドするという意味

ViewModel側

ViewModel では GongSolutions.Wpf.DragDrop.IDropTarget インターフェースを実装すればOKです。
下記の例では、リストにzipファイルのパスを列挙する機能を実装しています。

SampleWindowViewModel.cs
using GongSolutions.Wpf.DragDrop;
using System.Collections.ObjectModel;
using System.Linq;
using System.Windows;

class SampleWindowViewModel : IDropTarget
{
    public ObservableCollection<string> Files { get; } = new ObservableCollection<string>();

    public void DragOver(IDropInfo dropInfo)
    {
        var files = ((DataObject)dropInfo.Data).GetFileDropList().Cast<string>();
        dropInfo.Effects = files.Any(fname => fname.EndsWith(".zip"))
            ? DragDropEffects.Copy : DragDropEffects.None;
    }

    public void Drop(IDropInfo dropInfo)
    {
        var files = ((DataObject)dropInfo.Data).GetFileDropList().Cast<string>()
            .Where(fname => fname.EndsWith(".zip")).ToList();

        if (files.Count == 0) return;

        foreach (var file in files)
            Files.Add(file);
    }
}
  • 注意
    • IDropTargetSystem.Windows.Forms にも同名のインターフェースが存在するため、参照先を間違えないようにする
    • DragOver はドラッグした状態でリストにマウスオーバーしたときに実行されるメソッド
    • DropDragOver 状態でマウスボタンを離したときに実行されるメソッド

以上で、ドラッグ&ドロップ機能を持つ ListBox の実装が完了しました。

まとめ

GongSolutions.WPF.DragDropを利用すれば、MVVMの作法で簡単にドラッグ&ドロップを実装できます。