(XAML#06)「CommandBinding と CanExecute」
WPF を Windows フォームの延長線として使っていると、ボタン(Button)を押した時の動作を定義するために Click イベントハンドラーを使うということは少なくありません。実際、ウィンドウ上に Button コントロールを配置してダブルクリックすると、空の Click イベントハンドラーが定義され、そこに動作を書くことは自然なことにみえます。しかし、このような「動作」を定義するために WPF には「コマンド(Command)」という考え方があります。
ごく単純な形でコマンドを実装してみます。
[XAML]
<Window x:Class="WpfApp1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="325" Width="525">
<Window.Resources>
<RoutedCommand x:Key="MyCommand" />
</Window.Resources>
<Window.CommandBindings>
<CommandBinding Command="{StaticResource MyCommand}" Executed="CommandBinding_Executed" />
</Window.CommandBindings>
<StackPanel>
<Button Content="Click Me!" Command="{StaticResource MyCommand}" />
</StackPanel>
</Window>
[コード]
private void CommandBinding_Executed(object sender, ExecutedRoutedEventArgs e)
{
MessageBox.Show("Command Executed!");
}
まず、XAML のリソースとして MyCommand というオブジェクトを定義しています。Window の CommandBindings プロパティは、この MyCommand オブジェクトが呼び出されたときに呼び出す Executed イベントハンドラを割り当てています。そして、このコマンドを Button の Command プロパティにも割り当てています。これによって、「ボタンを押すと CommandBinding_Executed イベントハンドラーが実行される」という処理が実現されています。Click イベントを処理するよりもまわりくどいと感じられるかもしれませんが、こうすることで「コントロール」と「動作(コマンド)」を分離しておけます。
このようにコマンドを使うと、コマンドの有効/無効状態を切り替えるのが楽になります。上記に CheckBox コントロールを追加して、CommandBinding の定義に CanExecute を追加します。これに対応するイベントハンドラーも定義しておきます。
[XAML]
<Window.CommandBindings>
<CommandBinding Command="{StaticResource MyCommand}"
Executed="CommandBinding_Executed"
CanExecute="CommandBinding_CanExecute" />
</Window.CommandBindings>
<StackPanel>
<Button Content="Click Me!" Command="{StaticResource MyCommand}" />
<CheckBox x:Name="cbox1" Content="Check Me!" />
</StackPanel>
[コード]
private void CommandBinding_CanExecute(object sender, CanExecuteRoutedEventArgs e)
{
// この CanExecute はチェックボックス(cbox1)が構築される前に呼び出されることもあるので
// cbox1 が null でないかどうかを確認する必要がある
e.CanExecute = (cbox1 != null) && (cbox1.IsChecked == true);
}
こうすると、チェックボックスがチェックされている状態のときだけボタンが有効化され(Enabled=true)、チェックされていなければ無効化されます。たとえば、リストボックスに項目があるときだけ使える機能や、テキストボックスに文字が入力されているときだけに使える機能といったものは、CanExecute で e.CanExecute に true か false を代入するだけで、その機能の有効/無効を切り替えられます。これにより、「何かのイベントハンドラーを呼び出したときにコントロールの Enabled プロパティを操作する」よりも、ずっと直観的でわかりやすく実装できます。
このように自分で定義するコマンドだけでなく、よく使われるものはあらかじめ ApplicationCommands というクラスに定義されています。たとえば、ApplicationCommands.Copy、ApplicationCommands.Cut、ApplicationCommands.Paste は Menu 上に配置されている MenuItem や ToolBar に配置されている Button などに関連付けられると、自動的に「コピー」「切り取り」「貼り付け」の機能が実行されます。以下はこれらを利用した、簡単なテキストエディタの例です。
<Window x:Class="WpfApp1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="325" Width="525">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<ToolBar Grid.Row="0">
<Button Content="Copy" Command="ApplicationCommands.Copy" />
<Button Content="Cut" Command="ApplicationCommands.Cut" />
<Button Content="Paste" Command="ApplicationCommands.Paste" />
</ToolBar>
<TextBox Grid.Row="1" />
</Grid>
</Window>
ApplicationCommands で定義されているコマンドについては、MSDN をご覧ください。