WPFのGridにD&Dでファイルをドロップするサンプルコード

コンピュータ

比較的頻繁に使うコードで、ヘルパーとしてライブラリ化までしていますが、少しバージョンアップします。
扱うイベントをDropを直接扱うと、GridにListViewなどのD&Dを扱うコントロールで、イベントを奪われたり、奪ってしまう現象が発生します。
PreviewDropを使うことで回避するようにします。

ソースコード

<Window x:Class="GridPreviewDrop.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="DD Sample"
        Width="600"
        Height="400">

    <!-- ★ Grid が D&D 受付担当 -->
    <Grid x:Name="DropArea"
          Background="Transparent">

        <!-- 上に何を載せてもOK -->
        <Border Margin="20"
                BorderThickness="2"
                BorderBrush="Gray"
                CornerRadius="6"
                Padding="20">

            <StackPanel>

                <TextBlock Text="ここにファイルをドロップ"
                           FontSize="18"
                           Margin="0,0,0,10"/>

                <!-- 子コントロールが上にあっても問題なし -->
                <ListView Height="200">
                    <ListViewItem>ListView が上に載っていてもOK</ListViewItem>
                    <ListViewItem>Drag And Drop は Grid が受け取る</ListViewItem>
                </ListView>

            </StackPanel>

        </Border>
    </Grid>
</Window>
using System.Windows;

namespace GridPreviewDrop;

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();

        // ★ D&D ひな形
        Wiring.AcceptFilesPreview(
            DropArea,
            files =>
            {
                foreach (var f in files)
                {
                    MessageBox.Show(f, "Dropped");
                }
            },
            ".png", ".jpg", ".bmp"
        );
    }
}
using System.Windows;

public static class Wiring
{
    /*
     * コントロールにファイルのドラッグアンドドロップするヘルパー(Preview版)
     * 受け入れ拡張子をオプション指定する機能あり。
     */
    public static void AcceptFilesPreview(
        FrameworkElement el,
        Action<string[]> onFiles,
        params string[] exts)
    {
        el.AllowDrop = true;

        DragEventHandler? over = null;
        DragEventHandler? drop = null;

        over = (_, e) =>
        {
            if (e.Data.GetDataPresent(DataFormats.FileDrop))
                e.Effects = DragDropEffects.Copy;
            else
                e.Effects = DragDropEffects.None;

            // このイベントはここで処理済みであることを通知する
            // (以降のコントロールへイベントを伝播させない)
            e.Handled = true;
        };

        drop = (_, e) =>
        {
            if (!e.Data.GetDataPresent(DataFormats.FileDrop))
                return; // ファイルドロップ以外は処理せず、次の要素へイベントを流す

            var files = (string[])e.Data.GetData(DataFormats.FileDrop)!;

            if (exts?.Length > 0)
            {
                files = files
                    .Where(f => exts.Any(x =>
                        f.EndsWith(x, StringComparison.OrdinalIgnoreCase)))
                    .ToArray();
            }

            if (files.Length > 0)
                onFiles(files);

            // 外部ファイルのドロップはここで責任を持って処理したことを示す
            // (以降の RoutedEvent の伝播を終了させる)
            e.Handled = true;
        };

        el.PreviewDragOver += over; // Preview(Tunnel)段階で受信
        el.PreviewDrop += drop;     // Preview(Tunnel)段階で受信
    }
}

実行例

起動したウィンドウに対し画像ファイルをドラッグアンドドロップします。

メッセージボックスにドロップしたファイルのパスが表示されます。

コメント