kazuakix の日記

Windows Phone とか好きです

コマンド入門してみる その 2

昨日は数字を増やすという動作を ICommand を使ってビューから切り離しました。

でも、例えば数字を減らすという動作が増えた場合...

f:id:kazuakix:20141109175944j:plain,w240

同じように ICommand を継承したクラスを追加していると、際限なくクラスが増えていってしまいます。

public class MainPageViewModel : INotifyPropertyChanged
{
    public ICommand AddValCommand       { get; private set; }
    public ICommand SubstractValCommand { get; private set; }

    public void AddVal()
    {
        this.Val ++;
    }
    public void SubstractVal()
    {
        this.Val--;
    }
    
    public MainPageViewModel()
    {
        this.AddValCommand       = new AddButtonClick();
        this.SubstractValCommand = new SubtractButtonClick();
    }
}

// 以下、コマンドが増えるたびに作る?

public class AddButtonClick : ICommand
{
    public bool CanExecute(object parameter)
    {
        return true;
    }
    public event EventHandler CanExecuteChanged;

    public void Execute(object parameter)
    {
        ((MainPageViewModel)parameter).AddVal();
    }
}

public class SubtractButtonClick : ICommand
{
    public bool CanExecute(object parameter)
    {
        return true;
    }
    public event EventHandler CanExecuteChanged;

    public void Execute(object parameter)
    {
        ((MainPageViewModel)parameter).SubstractVal();
    }
}

 
それじゃ困りますよねって事で、例えば MVVM ライブラリの 1 つである Prism なんかでは DelegateCommand なるものが用意されているそうです。

コマンドの数だけクラスを作るのではなく、汎用的なクラスを作って Execute で実行する処理だけを差し替えようって事で、すごく大ざっぱに書くとこんな感じでしょうか?

public class DelegateCommand : ICommand
{
    private Action _execute;

    public DelegateCommand(Action execute)
    {
        this._execute = execute;
    }

    public void Execute(object parameter)
    {
        this._execute();
    }

    public bool CanExecute(object parameter)
    {
        return true;
    }
    public event EventHandler CanExecuteChanged;
}

 
これを使う事でコマンド用のクラスが増え続ける事はなくなります。

public class MainPageViewModel : INotifyPropertyChanged
{
    public DelegateCommand AddValCommand       { get; private set; }
    public DelegateCommand SubstractValCommand { get; private set; }

    public void AddVal()
    {
        this.Val ++;
    }
    public void SubstractVal()
    {
        this.Val--;
    }
    
    public MainPageViewModel()
    {
        this.AddValCommand       = new DelegateCommand(this.AddVal);
        this.SubstractValCommand = new DelegateCommand(this.SubstractVal);
    }
}

 
でも、実際には引数有りのパターンや非同期のパターンなんかも欲しいでしょうからそこそこ複雑になりそうです。
実際 Prism の DelegateCommand の中身を見るとそれなりに複雑ですね。素直にライブラリのお世話になろうという気になりますね。
 

余談ですが、コマンドについて調べていると DelegateCommand と RelayCommand という実装をよく見かけました。

何が違うのかと思っていたのですが、聞くところによると DelegateCommand と名付ける派閥と RelayCommand と名付ける派閥がいるという事でした*1。エディタは Emacs か Vi かみたいなアレでしょうか? 知らなくてもいい闇の抗争を覗いてしまったような気になりました。

*1:これを教えてくれた方は DelegateCommand 派閥という事になります