WPFには、ICommandインターフェースというユーザーの操作を抽象化する仕組みがあります。ICommandインターフェースは、以下のように定義されています。
publicinterface ICommand { // コマンドを実行するかどうかに影響するような変更があった場合に発生します。event EventHandler CanExecuteChanged; // 現在の状態でこのコマンドを実行できるかどうかを判断するメソッドを定義します。//// パラメーター:// parameter:// コマンドで使用されたデータ。 コマンドにデータを渡す必要がない場合は、このオブジェクトを null に設定できます。//// 戻り値:// このコマンドを実行できる場合は true。それ以外の場合は false。bool CanExecute(object parameter); // コマンドの起動時に呼び出されるメソッドを定義します。//// パラメーター:// parameter:// コマンドで使用されたデータ。 コマンドにデータを渡す必要がない場合は、このオブジェクトを null に設定できます。void Execute(object parameter); }
コマンドが実行可能かどうかという状態に変化があったことを通知するCanExecuteChangedイベントと、実際にコマンドが実行可能かどうかを返すCanExecuteメソッドがあります。そして、コマンドの処理を実行するためのExecuteメソッドがあります。
ICommandは、ICommandSourceという以下のようなインターフェースを実装したクラスに対して設定することが出来ます。ICommandSourceは以下のように定義されています。
// コマンドを呼び出す方法を認識しているオブジェクトを定義します。publicinterface ICommandSource { // コマンド ソースが呼び出されると実行されるコマンドを取得します。//// 戻り値:// コマンド ソースが呼び出されると実行されるコマンド。 ICommand Command { get; } // コマンドの実行時にコマンドに渡すことのできるユーザー定義データの値を表します。//// 戻り値:// コマンド固有のデータ。object CommandParameter { get; } // コマンドが実行されているオブジェクト。//// 戻り値:// コマンドが実行されているオブジェクト。 IInputElement CommandTarget { get; } }
実行するコマンドを取得するためのCommandプロパティと、コマンドに渡すためのCommandParameterが定義されています。CommandTargetは後述するRoutedCommandのみに適用される特殊なプロパティなのでここでは省略します。ICommandSourceインターフェースは、ButtonBase(ボタン系コントロールの基本クラス)やMenuItemなど、ユーザーがアクションを実行するコントロールに主に実装されています。
WPFでのICommandインターフェースの実装クラスのRoutedCommandクラスは、CommandBindingという仕組みを通じてユーザーのアクションと処理を結びつける機能を持っています。RoutedCommandクラスは、以下のように、クラスの静的メンバーとして定義して使用します。
publicpartialclass MainWindow : Window { publicstatic RoutedCommand AlertCommand = new RoutedCommand(); public MainWindow() { InitializeComponent(); } }
このRoutedCommandと、実際の処理を結びつけるには、UIElementクラスに定義されているCommandBindingsプロパティにCommandBindingを設定して行います。一般的にWindowクラスのCommandBindingsプロパティを使って以下のように定義します。
<Window.CommandBindings><CommandBinding Command="{x:Static local:MainWindow.AlertCommand}"Executed="CommandBinding_Executed"CanExecute="CommandBinding_CanExecute"/></Window.CommandBindings>
CommandBindingクラスのCommandプロパティに、先程定義したCommandのインスタンスを設定します。そして、ExecuteイベントにCommandの実行時の処理のイベントハンドラと、CanExecuteイベントにCommandの実行可否の処理のイベントハンドラを設定します。ここでは、仮にCheckBoxコントロールが画面にあり、CheckBoxコントロールにチェックがされているときだけ実行可能なコマンドを定義します。画面にCheckBoxコントロールと、コマンドを実行するためのButtonを置きます。ButtonのCommandプロパティには、CommandBindingに設定したものと同じ、MainWindowクラスのAlertCommandプロパティを設定します。
<Window x:Class="CommandSample01.MainWindow"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:local="clr-namespace:CommandSample01"Title="MainWindow"Height="350"Width="525"><Window.CommandBindings><CommandBinding Command="{x:Static local:MainWindow.AlertCommand}"Executed="CommandBinding_Executed"CanExecute="CommandBinding_CanExecute"/></Window.CommandBindings><StackPanel><CheckBox x:Name="checkBox"Content="CanExecute"/><Button Content="AlertCommand"Command="{x:Static local:MainWindow.AlertCommand}" /></StackPanel></Window>
そして、コードビハインドでイベントハンドラの処理を記述します。
publicpartialclass MainWindow : Window { publicstatic RoutedCommand AlertCommand = new RoutedCommand(); public MainWindow() { InitializeComponent(); } privatevoid CommandBinding_Executed(object sender, ExecutedRoutedEventArgs e) { MessageBox.Show("Hello world"); } privatevoid CommandBinding_CanExecute(object sender, CanExecuteRoutedEventArgs e) { e.CanExecute = this.checkBox.IsChecked.Value; } }
全体のつながりを説明すると、CommandBindingとButtonのCommandに同じコマンドのインスタンスを設定することで、この2つを繋げます。そして実際の処理は、CommandBindingのイベントハンドラで行います。実行結果を以下に示します。
実行すると、以下のようにボタンが押せない状態で起動します。
CheckBoxコントロールにチェックを入れると、ボタンが押せるようになります。これはCommandBindingに設定したCanExecuteイベントのイベントハンドラで行っている処理でCheckBoxコントロールのチェック状態を見てイベント引数のCanExecuteプロパティに実行可否の値を設定しているためです。
そして、Buttonコントロールをクリックすると、CommandBindingのExecuteイベントのイベントハンドラが実行されてMessageBoxが表示されます。
このように、RoutedCommandクラスと、CommandBindingクラスを使うことで操作を表すコマンドと実際の処理を分離して記述することが出来ます。
コマンドは、InputBindingを使うことで簡単にキーボードショートカットやマウスジェスチャーに対応させることが出来ます。WindowなどのInputBindingsプロパティに、KeyBindingを設定することでキーボードショートカットとコマンドの関連付けを行うことが出来ます。KeyBindingのModifiersプロパティに修飾キーを設定して、Keyプロパティにキーを設定して、Commandプロパティに該当するキーボードが押されたときに実行する処理を表すコマンドを設定します。例として、先程のAlertCommandをCtrl+Alt+Aを押したときに表示するようにするコードを示します。
<Window.InputBindings><KeyBinding Modifiers="Alt+Control"Key="A"Command="{x:Static local:MainWindow.AlertCommand}" /></Window.InputBindings>
実行して、CheckBoxコントロールにチェックを入れた状態でCtrl+Alt+Aを押すとMessageBoxが表示されます。
これまでの例では、自分でRoutedCommandのインスタンスを用意したものを使用しましたが、WPFでは組み込みで、アプリケーションによくあるコマンドがあらかじめ定義されています。コピーやペーストなどの一般的な操作をRoutedCommandで定義する場合は、下記のApplicationCommandsクラスに定義されているものを使用するとよいでしょう。
- ApplicationCommandsクラス: http://msdn.microsoft.com/ja-jp/library/system.windows.input.applicationcommands(v=vs.110).aspx
このように、WPFでは、組み込みのICommandインターフェースの実装が提供されています。しかし、ICommandインターフェースを実装していれば、InputBindingなどの機能は使うことが出来ます。最近のWPFをはじめとするXAMLを使った開発ではICommandインターフェースを実装してExecuteやCanExecuteの処理をデリゲートで受け取るDelegateCommand(RelayCommandという名前の場合もある)という実装が使われるのが一般的です。これらのコマンドの独自実装については後述します。
過去記事
- WPF4.5入門 その1 「はじめに」
- WPF4.5入門 その2 「WPFとは」
- WPF4.5入門 その3 「Hello world」
- WPF4.5入門 その4 「Mainメソッドはどこにいった?」
- WPF4.5入門 その5 「全てC#でHello world」
- WPF4.5入門 その6 「WPFを構成するものを考えてみる」
- WPF4.5入門 その7 「XAMLのオブジェクト要素と名前空間」
- WPF4.5入門 その8 「オブジェクト要素のプロパティ」
- WPF4.5入門 その9 「コレクション構文」
- WPF4.5入門 その10 「コンテンツ構文」
- WPF4.5入門 その11 「マークアップ拡張」
- WPF4.5入門 その12 「その他のXAMLの機能」
- WPF4.5入門 その13 「簡単なレイアウトを行うコントロール」
- WPF4.5入門 その14 「レイアウトコントロールのCanvasとStackPanel」
- WPF4.5入門 その15 「レイアウトコントロールのDockPanelとWrapPanel」
- WPF4.5入門 その16 「ViewBoxコントロール」
- WPF4.5入門 その17 「ScrollViewerコントロール」
- WPF4.5入門 その18 「Gridコントロール part 1」
- WPF4.5入門 その19 「Gridコントロール part 2」
- WPF4.5入門 その20 「レイアウトに影響を与えるプロパティ」
- WPF4.5入門 その21 「WPFのコンセプトと重要な機能つまみ食い」
- WPF4.5入門 その22 「Buttonコントロール」
- WPF4.5入門 その23 「DataGridコントロール その1」
- WPF4.5入門 その24 「DataGridコントロール その2」
- WPF4.5入門 その25 「TreeViewコントロール その1」
- WPF4.5入門 その26 「TreeViewコントロール その2」
- WPF4.5入門 その28 「Calendarコントロール」 - かずきのBlog@hatena
- WPF4.5入門 その29 「ContextMenuコントロール」 - かずきのBlog@hatena
- WPF4.5入門 その30「Menuコントロール」 - かずきのBlog@hatena
- WPF4.5入門 その31 「ToolBarコントロール」 - かずきのBlog@hatena
- WPF4.5入門 その32 「CheckBoxコントロール」 - かずきのBlog@hatena
- WPF4.5入門 その33 「ComboBoxコントロール」 - かずきのBlog@hatena
- WPF4.5入門 その34 「ListBoxコントロール」 - かずきのBlog@hatena
- WPF4.5入門 その35 「RadioButtonコントロール」 - かずきのBlog@hatena
- WPF4.5入門 その36 「Sliderコントロール」 - かずきのBlog@hatena
- WPF4.5入門 その37 「TabControl」 - かずきのBlog@hatena
- WPF4.5入門 その38 「ファイルダイアログ」 - かずきのBlog@hatena
- WPF4.5入門 その39 「情報を表示するコントロール」 - かずきのBlog@hatena
- WPF4.5入門 その40 「Popup、ToolTip、TextBox、Image、MediaElementコントロール」 - かずきのBlog@hatena
- WPF4.5入門 その41 「DispatcherObject」 - かずきのBlog@hatena
- WPF4.5入門 その42 「WPFのプロパティシステム」 - かずきのBlog@hatena
- WPF4.5入門 その43 「読み取り専用の依存関係プロパティ」 - かずきのBlog@hatena
- 拡張されたプロパティメタデータ - かずきのBlog@hatena
- WPF4.5入門 その45 「添付プロパティ」 - かずきのBlog@hatena
- WPF4.5入門 その46 「WPFのイベントシステム」 - かずきのBlog@hatena
- WPF4.5入門 その47 「コンテンツモデル」 - かずきのBlog@hatena
- WPF4.5入門 その48 「WPFのアニメーション その1」 - かずきのBlog@hatena
- WPF4.5入門 その49 「WPFのアニメーション その2」 - かずきのBlog@hatena
- WPF4.5入門 その50 「Style」 - かずきのBlog@hatena
- WPF4.5入門 その51 「リソース」 - かずきのBlog@hatena
- WPF4.5入門 その52 「コントロールテンプレート」 - かずきのBlog@hatena
- WPF4.5入門 その53 「ユーザーコントロール」 - かずきのBlog@hatena
- WPF4.5入門 その54 「カスタムコントロール」 - かずきのBlog@hatena
- WPF4.5入門 その55 「Binding その1」 - かずきのBlog@hatena
- WPF4.5入門 その56「コレクションのバインディング」 - かずきのBlog@hatena