[WPF]エクスプローラーからD&Dされたファイルパスを取得する

WPFでドラッグ&ドロップを実装するにはコードビハインドやビヘイビアを使用するなどがあるけど、僕はめんどくさがりなのでライブラリを使います。

Nugetに[gong-wpf-dragdrop](https://www.nuget.org/packages/gong-wpf-dragdrop/)があったのでそれを使います。

今回はエクスプローラーからファイルをD&Dされたcsvファイルのパスを取得し、それをListBoxに表示するMVVMのアプリを作ります。
csvファイル以外は表示しないとします。

## View

```xml:MainWindow.xaml
<Window x:Class="DragAndDropTest.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:DragAndDropTest"
        xmlns:dd="clr-namespace:GongSolutions.Wpf.DragDrop;assembly=GongSolutions.Wpf.DragDrop"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525">
    <Window.DataContext>
        <local:MainWindowViewModel />
    </Window.DataContext>
    <Grid>
        <ListBox ItemsSource="{Binding Files}" 
                 dd:DragDrop.IsDropTarget="True" 
                 dd:DragDrop.DropHandler="{Binding}" />
    </Grid>
</Window>
```

GongSolutions.WPF.DragDrop を使用するにはまずxamlに名前空間を追加します。

```
xmlns:dd="clr-namespace:GongSolutions.Wpf.DragDrop;assembly=GongSolutions.Wpf.DragDrop"
```

そして、D&Dを行いたい要素で

```xml
<ListBox ItemsSource="{Binding Files}" 
         dd:DragDrop.IsDropTarget="True" 
         dd:DragDrop.DropHandler="{Binding}" />
```

と記述します。

- DragDrop.IsDropTarget : ドロップ先のターゲット
- DragDrop.DropHandler : ドロップイベントのハンドラ

を表しています。今回はドロップされるだけなのでターゲットのみ宣言します。DropHandlerについては後ほど詳細を書きます。

## ViewModel
```csharp:MainWindowViewModel
using GongSolutions.Wpf.DragDrop;
using System.Windows;
using System.Linq;
using System.IO;
using System.Collections.ObjectModel;

namespace DragAndDropTest
{
    public class MainWindowViewModel : IDropTarget
    {
        public ObservableCollection<string> Files { get; set; }

        public MainWindowViewModel()
        {
            Files = new ObservableCollection<string>();
        }

        public void DragOver(IDropInfo dropInfo)
        {
            var dragFileList = ((DataObject)dropInfo.Data).GetFileDropList().Cast<string>();
            dropInfo.Effects = dragFileList.Any(_ =>
            {
                return IsCsv(_);
            }) ? DragDropEffects.Copy : DragDropEffects.None;
        }

        public void Drop(IDropInfo dropInfo)
        {
            var dragFileList = ((DataObject)dropInfo.Data).GetFileDropList().Cast<string>();
            dropInfo.Effects = dragFileList.Any(_ =>
            {
                return IsCsv(_);
            }) ? DragDropEffects.Copy : DragDropEffects.None;

            foreach(var file in dragFileList)
            {
                if (IsCsv(file))
                {
                    Files.Add(file);
                }
            }
        }

        private bool IsCsv(string data)
        {
            var extension = Path.GetExtension(data);
            return extension != null && extension == ".csv";
        }
    }
}
```

IDropTargetがDropHandlerを実装するためのインターフェースです。DragOver() と Drop() を実装します。
今回はcsvファイルのみ受け付けるため、csvファイルかを判定する IsCsv() を実装しています。

まずDragOver()でcsvファイルがあれば受け付けるようにします。

```csharp
var dragFileList = ((DataObject)dropInfo.Data).GetFileDropList().Cast<string>();
dropInfo.Effects = dragFileList.Any(_ =>
{
    return IsCsv(_);
}) ? DragDropEffects.Copy : DragDropEffects.None;
```
GetFileDropList() でドロップされたファイルを取得しています。
そして、IsCsv()がtrueの時にDragDropEffectsがCopy(受け付ける)になり、falseの時にNone(受け付けない)にしています。

次にDrop()も同様にcsvファイルがあれば受け付けるようにし、foreach()内でIsCsv()を呼び、csvファイルでFiles.Add()でcsvファイルパスを追加すればListBoxにファイルパスが表示されます。

![スクショ.png](https://qiita-image-store.s3.amazonaws.com/0/127280/908cadaf-81c9-2ef9-a4e5-45a407912051.png)

これでD&Dが簡単に実装できました。