.. :tocdepth:2 ============================ コマンド ============================ System.Windows.Input.ICommand ==================================================== まずWPFにおけるICommand( `System.Windos.Input.ICommand `_ )について簡単におさらいしておきましょう。 ICommandはメニューやボタンから実行するコマンドを表すインターフェースです。 .. code-block:: csharp public interface ICommand { event EventHandler CanExecuteChanged; bool CanExecute(object parameter); void Execute(object parameter); } コマンドを実行するExecuteおよびコマンドが実行可能かどうかを判定するCanExecuteメソッドを持ちます。 また、CanExecuteChangedイベントを持ち、コマンドが実行可能かどうか変更があった場合に通知を発生させなければなりません。 MoNo.RAILによるコマンドの生成 =============================== MoNo.RAILではICommand実装インタンスを生成する関数がいくつか用意されています。 Wpf.Command.createWpfCommand ------------------------------- まず最も基本的なものはMoNo.Wpf.Command.createWpfCommandです。シグネチャを確認してみましょう。 .. code-block:: fsharp Wpf.Command.createWpfCommand : canExecute:IReactive option -> execute:('a -> unit) -> System.Windows.Input.ICommand 引数としてIReactive option型のcanExecuteと関数executeを渡し、それぞれICommandのCanExecuteとExecuteに対応付けられたICommand実装クラスが生成されます。 コマンドの実行結果としては単に関数executeが実行されます。 通常、ICommandの実装にはCanExecuteメソッドとCanExecuteChangedイベントを適切に実装する必要があります。 MoNo.RAILに用意されている一連のコマンド生成関数ではReactive型を渡すことで実行可能かどうかを決定します。 また、実行可能かどうかの変更通知もReactiveの仕組みを利用して通知するためValプロパティの値を変更するだけです。 なおcanExecuteはoptionです。Noneの場合は常に実行可能なコマンドが生成されます。 Wpf.Cont.toWpfCommand --------------------------------- 次に引数に「実行される関数」ではなく前項で解説した「継続モナド」を渡してコマンド化する関数を見てみましょう。 .. code-block:: fsharp Wpf.Cont.toWpfCommand : runner:IContRunner -> canExecute:#IReactive option -> cont:Cont -> System.Windows.Input.ICommand 第1引数にIContRunnerというものを渡しています。これは本項では解説しませんが継続モナドを活かすためのインターフェースです。 サンプルプログラムではViewModelがその実装クラスであるContRunnerを継承し、自身を渡すようにしています。 第3引数で関数の代わりに継続モナドを渡しています。 これにより継続モナドを呼び出すICommand実装インスタンスが生成できます。 Wpf.Command.toWpfCommand ---------------------------------- 最後は引数にMoNo.Commandのインスタンスを渡してコマンド化する関数です。 .. code-block:: fsharp Wpf.Command.toWpfCommand : runner:IContRunner -> command:Command<'a,unit> -> System.Windows.Input.ICommand この関数では第2引数にCommand<'a,unit>を渡します。 これはMoNo.RAILで定義されているレコード型で、 .. code-block:: fsharp type Command<'a, 'b> = { CanExecute : IReactive Execute : 'a -> Cont<'b> } 上記の定義の通り、IReactive型のCanExecuteと'aを引数に取り'bの継続モナドを返すExecute関数を持つレコードです。 それぞれICommandのCanExecuteとExecuteに対応するので説明の必要はないでしょう。 サンプルプログラム CommandSample ===================================== さて、ここまでを踏まえてサンプルプログラムを見てみましょう。 サンプルプログラムは :download:`MoNo.RAIL <../../MoNo.RAIL-release.zip>` の下記フォルダに含まれています:: MoNo.RAIL.Samples/WpfMvvmSamples/CommandSample 外観はこのような感じです。 .. image:: command_appcapture.png サンプルプログラム CommandSampleの仕様は以下の通りです。 * Incrementボタンを押すと値が1増えます。 * Decrementボタンを押すと値が1減ります。ただし値が1以上の時でないと実行できません。 * Doubleボタンを押すと値が倍になります。 * Div By 2ボタンを押すと値が半分になります。ただし値が10以上の時でないと実行できません。 * 10 timesボタンを押すと3秒間待った後、値が10倍になります。待機時間中にAbortボタンが有効になり、これを押すと処理が中断されます。 * 20 timesボタンを押すとプログレスバーが表示され3秒間待った後、値が20倍になります。こちらも待機時間中にAbortボタンが有効になり、これを押すと処理が中断されます。 まずModel.fsを見てみましょう。 .. code-block:: fsharp type Model () = let value = Reactive 0 member __.Value = value ... シンプルなモデルクラスです。本質的にはReactive型の値を一つ持っているだけです。 次にMainWindow.xamlを見てみましょう。 .. code-block:: xml ...