その1「ICommandインターフェース」:http://blogs.wankuma.com/kazuki/archive/2008/03/16/127903.aspx
その1で、ICommandインターフェースとコンソールアプリで実装したクラスを使うところまでやってみた。
正直、あんな風には使わずにWPFで提供されているICommandを実装したクラスや、もっといい方法を使ったりする。
今回で、一般的なWPFでのコマンドの使い方くらいまで紹介できたらいいなと考えてる。
今回は、GUIでコマンドを使ってみる。
ボタンを押すと世界中のプログラマが見たことある「こんにちは世界」を表示するという素敵なアプリケーションを作ってみようと思う。
実行結果すると丁度下のような画面になるものだ。
ということで、早速コマンドを実装してみようと思う。
実装は、解説がいらないくらいシンプル。
using System;
using System.Windows;
using System.Windows.Input;
namespace WpfCommand
{
public class HelloCommand : ICommand
{
public bool CanExecute(object parameter)
{
return true;
}
public event EventHandler CanExecuteChanged;
public void Execute(object parameter)
{
MessageBox.Show("こんにちは世界");
}
}
}
この調子でサクサクとXAMLも定義していく。StackPanelにボタンを乗せるだけでOK。一応ボタンクリックの時のイベントも定義してある。
<Window x:Class="WpfCommand.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="コマンド" Height="300" Width="300">
<StackPanel>
<Button Content="Click!" Click="Button_Click" />
</StackPanel>
</Window>
そして、ボタンクリックのイベントのあるWindow1.xaml.csは、こんな感じ。
using System.Windows;
using System.Windows.Input;
namespace WpfCommand
{
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
ICommand command = new HelloCommand();
if (command.CanExecute(null))
{
command.Execute(null);
}
}
}
}
コマンドが実行可能かどうか尋ねて実行してる。
これをベースに、改造していく。
とりあえず、今コマンドをボタンクリックの中でインスタンス化してる。
普通は、コマンドってショートカットから実行されたり、メニューから実行されたり、ツールバーのボタンから実行されたり、さまざまな所から実行される。
なので、各々のイベントでインスタンスを作るのは無駄っぽい。
ICommandインターフェースは、実行に必要な情報を引数から受け取る実装をすることが多そうなので、大体ステートレスになりそうだ。
ということで、Window1のstaticでreadonlyな定数として定義する。
using System.Windows;
using System.Windows.Input;
namespace WpfCommand
{
public partial class Window1 : Window
{
// 皆から使えるようにstatic readonlyで定義
static readonly ICommand HelloCommand = new HelloCommand();
public Window1()
{
InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
// 定数として定義したものを使う
if (HelloCommand.CanExecute(null))
{
HelloCommand.Execute(null);
}
}
}
}
これでメニューとかから実行する場合も、if (HelloCommand.CanExecute(null)) HelloCommand.Execute(null);を書くだけでOKだ。
でも、まだ毎回同じコードを書くことになってしまう。
そうならないように、WPFでコマンドと関連付けそうなコントロールには、Commandプロパティというものが定義されてる。
これを使うとWindow1.xamlとWindow1.xaml.csは下のようになる。
<Window x:Class="WpfCommand.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="コマンド" Height="300" Width="300">
<StackPanel>
<Button Name="button" Content="Click!" />
</StackPanel>
</Window>
using System.Windows;
using System.Windows.Input;
namespace WpfCommand
{
public partial class Window1 : Window
{
// 皆から使えるようにstatic readonlyで定義
static readonly ICommand HelloCommand = new HelloCommand();
public Window1()
{
InitializeComponent();
button.Command = HelloCommand;
}
}
}
これで、初期化のコードに、このボタンはこのコマンドを実行しますという事を書くだけでOKになる。
だいぶすっきりした。
こういう風に宣言的に書くコードはXAMLに移動させることが出来る。
<Window x:Class="WpfCommand.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:WpfCommand="clr-namespace:WpfCommand"
Title="コマンド" Height="300" Width="300">
<StackPanel>
<Button Name="button" Content="Click!" Command="{x:Static WpfCommand:Window1.HelloCommand}"/>
</StackPanel>
</Window>
変わったのは、4行目の名前空間の宣言と、7行目のボタンのCommandプロパティの設定になる。
x:Staticを使ってWindow1のHelloCommandを設定するようにしてある。
Window1.xaml.csのほうは下のようになった。
using System.Windows;
using System.Windows.Input;
namespace WpfCommand
{
public partial class Window1 : Window
{
// 外部から見えるようにpublicへ
public static readonly ICommand HelloCommand = new HelloCommand();
public Window1()
{
InitializeComponent();
}
}
}
これでWPFが提供している仕組みとかを使って素敵な感じになってきた。
ICommandインターフェースを実装してバリバリ書いていくぜ!!となりそうだけど、実際はRoutedCommandクラスとかを使うことになる。
それはまた別の話しで。