SourceChord

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

添付ビヘイビアを使って、任意のコマンド呼び出しを行う

以前、↓で書いたRelayCommandですが、これはCommandプロパティを持つコントロールでしか使用できません。
XAMLからViewModelのメソッドにバインドする~RelayCommand~ - SourceChord

RelayCommandをバインドできるのは、↓みたいにCommandプロパティを持っているButtonなどのようなコントロールに限られます。

        <Button Width="75"
                Margin="10,10,0,0"
                HorizontalAlignment="Left"
                VerticalAlignment="Top"
                Content="Button"
                Command="{Binding HelloCommand}"/>

でも、他のコントロールや、ボタン押下以外のイベントとかでRelayCommandを実行したい、ということもあるかと思います。
そんな時、添付ビヘイビアをつくることで、任意のCommandを実行する機能を付加することができます。

てことで、マウス左クリックをした時に任意のCommandを実行する添付ビヘイビアを作ってみました。
Commandプロパティに、VMのRelayCommandのプロパティをバインドすることで、この添付ビヘイビアを設定したコントロールを左クリックしたときにコマンドを実行することができます。

ExecuteCommandBehavior.cs
    public class ExecuteCommandBehavior
    {
        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(ExecuteCommandBehavior), new PropertyMetadata(null, OnCommandChanged));


        public static object GetCommandParameter(DependencyObject obj)
        {
            return (object)obj.GetValue(CommandParameterProperty);
        }
        public static void SetCommandParameter(DependencyObject obj, object value)
        {
            obj.SetValue(CommandParameterProperty, value);
        }
        // Using a DependencyProperty as the backing store for CommandParameter.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty CommandParameterProperty =
            DependencyProperty.RegisterAttached("CommandParameter", typeof(object), typeof(ExecuteCommandBehavior), new PropertyMetadata(null));


        private static void OnCommandChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            // Command添付プロパティに変更が加わったら、マウス左クリック時のイベントを登録する。
            var element = d as UIElement;
            if (element == null)
                return;

            if (e.NewValue is ICommand)
            {
                element.MouseLeftButtonDown += element_MouseLeftButtonDown;
            }
            else
            {
                element.MouseLeftButtonDown -= element_MouseLeftButtonDown;
            }

        }


        static void element_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            // マウス左クリック時には、プロパティに設定されたコマンドを実行する。
            var element = sender as UIElement;
            if (element == null)
                return;

            var cmd = GetCommand(element);
            var param = GetCommandParameter(element);
            if(cmd.CanExecute(param))
                cmd.Execute(param);
        }
    }
MainWindow.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:ExecuteCommandBehavior.Command="{Binding ShowMessageCommand}"
                   local:ExecuteCommandBehavior.CommandParameter="Hello World!!"/>
    </Grid>
</Window>

ウィンドウには、↓のようなVMをデータコンテキストにセットしています。

MainWindowViewModel.cs
    class MainWindowViewModel : BindableBase
    {
        private RelayCommand<string> showMessageCommand;
        public RelayCommand<string> ShowMessageCommand
        {
            get { return showMessageCommand = showMessageCommand ?? new RelayCommand<string>(ShowMessage); }
        }

        private void ShowMessage(string parameter)
        {
            MessageBox.Show(parameter);
        }
    }

こういう風に添付ビヘイビアを作れば、コードビハインドを使わずに好きなタイミングで任意のコマンドを実行できますね。
ただし、やっぱかなり回りくどい書き方だなぁ。。とは思う。コードビハインドで書けば数行で済む内容だし。


添付ビヘイビア、、
もっと使いまわしの効くような用途の時に作った方がいいのかな。