BackacheEngineerの技術的な備忘録

技術系でいろいろ書けたらなーと

Xamlで使えるようになりたいCommand

最初見たときは「イベントでええやん」と思った Command だけど、MVVM を勉強したころから「これ使えるようになった方がいいかも」と気づいた。正直、全然理解しきれてないけど書いてく。

目次

Command の存在意義

自分なりの理解になってしまうが、Command がなぜ必要なのかまとめる。(自分なりというのはあまり好きじゃないけど) もしもなんか暇でここを読んでいる博識な方がいたら指摘お願いしたいくらい。

存在意義は単刀直入に言って、「View からコードを分離するため」に Command がある。 Command と イベントとの決定的な違いは、実装している場所である。 イベントは必ず内部コード(xaml.cs)側にコードを実装する。 しかし、Command は View Model 側にコードを実装する。

Command は View Model 側に実装されるため、「View(xaml.cs)には一切コードを追加しない」がボタンを押すとちゃんといろんな処理がされる、といったことも可能になる。

Command の使い方

そんなに使ったことがないのだけど、Command はバインディングして使う。

こんな感じで。とっても普通。

<Grid DataContext="{StaticResource viewModel}">
    <Button Context="キャンセル" Command="{Binding CancelCommand}"/>
</Grid>

また、下記のように、1つだけだが引数の指定が可能。1つしかダメな理由は後述の「 Command の構成」を参照。 ボタンを押すと 1 が指定のコントロールに追加される Command 例。

<Button Content="1" Command="{Binding AddCharacterCommand}" CommandParameter="1"/>

バインディングして使うので、もし同じ動きをしてほしいボタン等あれば Command をバインディングさせるだけで実現できる。 値を変えたかったら引数を指定してやればいい。 やってることのイメージは、1つのイベントを複数のコントロールに割り当ててる感じ。 そう考えると、Command とイベントの違いは本当に「実装している場所」くらいのイメージでいい(と思う)。

Command の構成

Command は ICommand インターフェースを継承して使う。 その時必要になるのは以下の3つ。

  1. public event EventHandler CanExecuteChanged
  2. public bool CanExecute(object parameter)
  3. public void Execute(object parameter)
項目 説明
CanExecuteChanged CanExecute 状態が変更されたときに発生するイベント(のはず)。これを発生させると、CanExecute メソッドの結果によってこの Command が割り当てられたコントロールが操作できなくなる( IsEnable が false )。
CanExecute 現在の状態で Command が実行可能かどうかを決定するメソッド。
Execute コマンドが起動される際に呼び出すメソッド。

上記のうち、 CanExecute と Execute をコンストラクタとかで入れてあげれば Command 完成。

サンプルは以下のとおり。 これを View Model 側でインスタンスを作ってやって、View Model 側にある関数を CanExecute や Execute に渡せばいい。

    public class DelegateCommand : ICommand 
    {
        Action<object> mExecute;
        Func<object, bool> mCanExecute;

        public event EventHandler CanExecuteChanged;

        public bool CanExecute(object parameter) => mCanExecute(parameter);
        public void Execute(object parameter) => mExecute(parameter);

        public DelegateCommand(Action<object> execute, Func<object, bool> canExecute)
        {
            mExecute = execute;
            mCanExecute = canExecute;
        }
        /// <summary>
        /// 既定のcanExecute代入するコンストラクタ
        /// </summary>
        public DelegateCommand(Action<object> execute)
        {
            mExecute = execute;
            mCanExecute = new Func<object, bool>((param) => { return true; });
        }
    }