RelayCommand

WPF のための MODEL-VIEW-VIEWMODEL (MVVM) デザイン パターン - MSDN マガジン February 2009

MVVMパターンで使用するコマンドの実装を考えた場合、いろいろなやり方を考えることができると思うが、コマンドの機能毎にICommandの実装を用意する煩雑さとそもそもICommandインタフェースがジェネリクスに対応していないため、本記事のようにデベロッパはActionデリゲートをコマンドの委譲先に利用した汎用のコマンド実装を使うことが多い。

RelayCommand.cs (Silverlight4版)
    public class RelayCommand : ICommand
    {
        readonly Action _execute;
        readonly Predicate _canExecute;

        public RelayCommand(Action execute)
            : this(execute, null)
        { }

        public RelayCommand(Action execute, Predicate canExecute)
        {
            if (execute == null)
                throw new ArgumentNullException("execute");

            _execute = execute;
            _canExecute = canExecute;
        }

        [DebuggerStepThrough]
        public bool CanExecute(Object parameter)
        {
            return _canExecute == null || _canExecute((T)parameter);
        }

        public event EventHandler CanExecuteChanged;
        public void RaiseCanExecuteChanged()
        {
            var handler = CanExecuteChanged;
            if (handler != null)
            {
                handler(this, EventArgs.Empty);
            }
        }

        public void Execute(Object parameter)
        {
            _execute((T)parameter);
        }
    }

前回書いたNavigateCommandをRelayCommandを拡張して書き直すと、以下のようになる。

NavigateCommand
    public class NavigateCommand : RelayCommand
    {
        public NavigateCommand() 
        : base(
            a => a.NavigationService.Navigate(new Uri("/Page2.xaml", UriKind.Relative))
            )
        {}
    }

無理矢理だがXAML構文ではオブジェクト生成時に引数を渡すことも、プロパティでデリゲート構文を差込むこともできないので、このようにデフォルトコンストラクタにラムダでActionデリゲートを挿入している。本来コマンドを単独で使うことはなく、この後言及するであろうViewModelにカプセル化して使うのが普通だ。

XAMLの記述は変わらないので省略する。