C#
WPF
ドラッグ&ドロップ
D&D

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

More than 1 year has passed since last update.

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

Nugetにgong-wpf-dragdropがあったのでそれを使います。

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

View

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を行いたい要素で

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

と記述します。

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

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

ViewModel

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ファイルがあれば受け付けるようにします。

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

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