Composite Application Guidance for WPFには、ICommandの実装クラスとしてDelegateCommand<T>とCompositeCommandというクラスが用意されている。この2つのCommandについてちょっと見てみようと思う。
DelegateCommand<T>
このクラスは、すこぶるシンプル。
コンストラクタは以下の二種類ある。
- DelegateCommand<T>(Action<T> executeMethod)
DelegateCommand<T>.Execute(T parameter)が呼び出されたときに呼ばれるメソッドを指定するコンストラクタ - DelegateCommand<T>(Action<T> executeMethod, Func<T, bool> canExecuteMethod)
DelegateCommand<T>.Execute(T parameter)が呼び出されたときに呼ばれるメソッドと、DelegateCommand<T>.CanExecute(T parameter)が呼び出されたときに呼ばれるメソッドを指定するコンストラクタ
基本このコンストラクタを使うだけでよさそうだ。ExecuteやCanExecuteを直接呼ぶことは少ないだろうからね。ちょいとお試し。
using System;
using Microsoft.Practices.Composite.Wpf.Commands;
namespace CompositeCommandTest
{
class Program
{
// Executeが呼ばれたときに呼ばれる
private static void ExecuteCallback(string parameter)
{
Console.WriteLine("Execute: " + parameter);
}
// CanExecuteが呼ばれたときに呼ばれる
private static bool CanExecuteCallback(string parameter)
{
Console.WriteLine("CanExecute: " + parameter);
return true;
}
static void Main(string[] args)
{
// コマンド作ってCanExecuteとExecuteを呼んでみる
var command = new DelegateCommand<string>(ExecuteCallback, CanExecuteCallback);
command.CanExecute("ほげ");
command.Execute("もげ");
}
}
}
実行結果は以下の通り。
CanExecute: ほげ
Execute: もげ
まぁ動きとしては問題ない。
CompositeCommand
これもシンプル構造のCommandになってる。どういうCommandかというと、複数のCommandを1まとめにするというもの。
CompositeCommand.RegisterCommand(ICommand command)メソッドでCommandを登録して、CompositeCommand.UnregisterCommand(ICommand command)メソッドで登録を取り消す。
Commandを登録しておくと、ExecuteやCanExecuteを呼び出したときにすべてのCommandが実行されるようになる。CanExecuteはすべてのCommandのCanExecuteがtrueの場合にtrueを返すようだ。というわけで早速実験!!
using System;
using Microsoft.Practices.Composite.Wpf.Commands;
namespace CompositeCommandTest
{
class Program
{
static void Main(string[] args)
{
// CompositeCommandを用意して
var compositeCommand = new CompositeCommand();
// ExecuteがConsole.WriteLineで、CanExecuteが常にtrueのコマンドを作って登録
var command1 = new DelegateCommand<object>(
Console.WriteLine, (arg) => true);
compositeCommand.RegisterCommand(command1);
// CanExecuteとExecuteの実行
Console.WriteLine("CanExecuteの実行結果: " + compositeCommand.CanExecute(null));
compositeCommand.Execute("はろーわーるど1");
// Commandを2つ登録する前と後の実行結果を区切るための目印
Console.WriteLine("----------------------------------");
// ExecuteがConsole.WriteLineで、CanExecuteが常にfalseのコマンドを作って登録
var command2 = new DelegateCommand<object>(
Console.WriteLine, (arg) => false);
compositeCommand.RegisterCommand(command2);
// CanExecuteとExecuteの実行
Console.WriteLine("CanExecuteの実行結果: " + compositeCommand.CanExecute(null));
compositeCommand.Execute("はろーわーるど2");
}
}
}
実行結果は以下のようになる。
CanExecuteの実行結果: True
はろーわーるど1
----------------------------------
CanExecuteの実行結果: False
はろーわーるど2
はろーわーるど2
動きとしては問題ない。
何のためにあるの?
WPFには、デフォルトでICommandの実装としてRoutedCommandやRoutedUICommandっていうクラスが用意されてて、CommandBindingとかと組み合わせると、快適に実装できるようになっている。
にも関わらず、Composite Application Guidance for WPFで新たに2つの実装を用意してるのは何でだろう?ということになってくる。これは、Composite Application Guidance for WPFで推奨するアプリの実装方法のPresentationModelパターンでの実装を視野に入れているからっぽい。
PresentationModelパターンは、View(WPFでいうと恐らくxaml + コードビハインドクラス)の他に、PresentationModelというView用のプロパティや処理を受け持つクラスを作るということになっている。
ということは、ボタンのクリック処理なんかもPresentationModel側に書くのがセオリーってことになってくるが、RoutedCommandは、Visual Tree上のものにしか、ExecuteやCanExecuteを関連付けることを、あまり意識して作られてなさげ。
なので、シンプルにdelegateを使ったDelegateCommand<T>とかが出来たんだと思われる。
CompositeCommandは、単純に複数のCommandを1つにまとめたかっただけかもしれん。英語難しい…。