読者です 読者をやめる 読者になる 読者になる

SourceChord

C#とXAML好きなプログラマの備忘録。最近はWPF系の話題が中心です。

ファイルドロップ時にコマンドを実行する添付ビヘイビア

C# WPF

添付ビヘイビアの仕組みが結構便利だと思ったので、ファイルドロップ時にコマンドを実行させるための添付ビヘイビアを作ってみました。

f:id:minami_SC:20140316174146p:plain:w400
FileDropAttachedBehavior.Commandという添付プロパティにRelayCommandをセットすると、ファイルドロップ時にコマンドが呼び出されるようになります。
こうやっておくと、XAMLで添付ビヘイビアを追加するだけでファイルドロップにすぐ対応できて便利かも。

FileDropAttachedBehavior.cs
    public class FileDropAttachedBehavior
    {
        public static ICommand GetCommand(DependencyObject obj)
        {
            return (ICommand)obj.GetValue(CommandProperty);
        }
        public static void SetCommand(DependencyObject obj, ICommand value)
        {
            obj.SetValue(CommandProperty, value);
        }
        // Using a DependencyProperty as the backing store for Command.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty CommandProperty =
            DependencyProperty.RegisterAttached("Command", typeof(ICommand), typeof(FileDropAttachedBehavior), new PropertyMetadata(null, OnCommandChanged));

        private static void OnCommandChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            // Commandプロパティが設定されたら、ファイルドロップを受け付けるための設定を行う
            var element = d as UIElement;
            if (element == null)
                return;

            var cmd = GetCommand(element);
            if (cmd != null)
            {
                element.AllowDrop = true;
                element.PreviewDragOver += element_PreviewDragOver;
                element.Drop += element_Drop;
            }
            else
            {
                element.AllowDrop = false;
                element.PreviewDragOver -= element_PreviewDragOver;
                element.Drop -= element_Drop;
            }
        }

        static void element_PreviewDragOver(object sender, DragEventArgs e)
        {
            // ドロップ使用とするものがファイルの時のみ受け付ける。
            if (e.Data.GetData(DataFormats.FileDrop) != null)
            {
                e.Effects = DragDropEffects.Copy;
            }
            else
            {
                e.Effects = DragDropEffects.None;
            }
            e.Handled = true;
        }

        static void element_Drop(object sender, DragEventArgs e)
        {
            var element = sender as UIElement;
            if (element == null)
                return;

            // ドロップされたファイルパスを引数としてコマンド実行
            var cmd = GetCommand(element);
            var fileInfos = e.Data.GetData(DataFormats.FileDrop) as string[];
            if (fileInfos != null && cmd.CanExecute(null))
                cmd.Execute(fileInfos);
        }
    }
MainWindow.xaml

XAMLは、↓のように添付ビヘイビアをセットするだけ。

<Window x:Class="AttachedBehaviorTest.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:AttachedBehaviorTest"
        Title="MainWindow"
        Width="525"
        Height="350">
    <Grid>
        <Rectangle Width="100"
                   Height="50"
                   Fill="#FFF4F4F5"
                   Stroke="Black"
                   local:FileDropAttachedBehavior.Command="{Binding FileDropCommand}"/>
    </Grid>
</Window>
MainWindowViewModel.cs

VMでは以下のように、string[]の引数を受け付けるRelayCommandを作ります。
この引数にドロップしたファイル/フォルダのパスの配列が入ってきます。

    class MainWindowViewModel : BindableBase
    {
        private RelayCommand<string[]> fileDropCommand;
        public RelayCommand<string[]> FileDropCommand
        {
            get { return fileDropCommand = fileDropCommand ?? new RelayCommand<string[]>(FileDrop); }
        }

        private void FileDrop(string[] parameter)
        {
            string str = string.Join("\n", parameter);
            MessageBox.Show(str, "ドロップされたファイル一覧");
        }
    }

実行して、Rectangleにファイルをドロップすると、ドロップしたファイルなどのパス一覧を表示します。
f:id:minami_SC:20140316173753p:plain:w300