Language

総括的な概要

コマンドは、Windows Presentation Foundation (WPF) の入力メカニズムであり、デバイス入力よりもセマンティック レベルで入力処理を提供します。 コマンドの例としては、コピー、切り取り、貼り付け 操作があります。

この概要では、WPF のコマンド、コマンド モデルの一部であるクラス、およびアプリケーションでコマンドを使用して作成する方法を定義します。

このトピックには、次のセクションが含まれています。

コマンドとは

コマンドにはいくつかの目的があります。 最初の目的は、コマンドを実行するロジックからコマンドを呼び出すセマンティクスとオブジェクトを分離することです。 これにより、複数の異なるソースが同じコマンド ロジックを呼び出すことができます。また、異なるターゲットに合わせてコマンド ロジックをカスタマイズできます。 たとえば、コピー、切り取り、および 貼り付けの編集操作は、コマンドを使用して実装されている場合、さまざまなユーザーアクションを使用して呼び出すことができます。 アプリケーションでは、ユーザーがボタンをクリックするか、メニュー内の項目を選択するか、Ctrl + X などのキーの組み合わせを使用して、選択したオブジェクトまたはテキストを切り取る場合があります。 コマンドを使用すると、各種類のユーザー アクションを同じロジックにバインドできます。

コマンドのもう 1 つの目的は、アクションが使用可能かどうかを示す場合です。 オブジェクトまたはテキストを切り取る例を続けるには、何かが選択されている場合にのみアクションが有効です。 ユーザーが何も選択せずにオブジェクトまたはテキストを切り取ろうとすると、何も起こりません。 これをユーザーに示すために、多くのアプリケーションでは、アクションを実行できるかどうかをユーザーが認識できるように、ボタンとメニュー項目を無効にします。 コマンドは、CanExecute メソッドを実装することによってアクションが可能かどうかを示すことができます。 ボタンは、CanExecuteChanged イベントをサブスクライブし、CanExecute が false を返す場合は無効にしたり、CanExecute が trueを返した場合に有効にしたりすることができます。

コマンドのセマンティクスは、アプリケーションとクラス間で一貫性を持つことができますが、アクションのロジックは、処理される特定のオブジェクトに固有です。 キーの組み合わせ Ctrl + X キーを押すと、テキスト クラス、イメージ クラス、Web ブラウザーで Cut コマンドが呼び出されますが、切り取り 操作を実行するための実際のロジックは、カットを実行するアプリケーションによって定義されます。 RoutedCommand を使用すると、クライアントはロジックを実装できます。 テキスト オブジェクトは選択したテキストをクリップボードに切り取り、イメージ オブジェクトは選択した画像を切り取ることができます。 アプリケーションは、Executed イベントを処理するときに、コマンドのターゲットにアクセスでき、ターゲットの種類に応じて適切なアクションを実行できます。

WPF での単純なコマンドの例

WPF でコマンドを使用する最も簡単な方法は、コマンド ライブラリ クラスの 1 つから定義済みの RoutedCommand を使用することです。コマンドを処理するためのネイティブ サポートを持つコントロールを使用する。コマンドを呼び出すためのネイティブ サポートを持つコントロールを使用します。 Paste コマンドは、ApplicationCommands クラスの定義済みコマンドの 1 つです。 TextBox コントロールには、Paste コマンドを処理するためのロジックが組み込まれています。 また、MenuItem クラスには、コマンドを呼び出すためのネイティブ サポートがあります。

次の例は、MenuItem にキーボード フォーカスがあると仮定して、Paste をクリックしたときに TextBox コマンドを TextBoxで呼び出すように設定する方法を示しています。

<StackPanel>
  <Menu>
    <MenuItem Command="ApplicationCommands.Paste" />
  </Menu>
  <TextBox />
</StackPanel>
// Creating the UI objects
StackPanel mainStackPanel = new StackPanel();
TextBox pasteTextBox = new TextBox();
Menu stackPanelMenu = new Menu();
MenuItem pasteMenuItem = new MenuItem();

// Adding objects to the panel and the menu
stackPanelMenu.Items.Add(pasteMenuItem);
mainStackPanel.Children.Add(stackPanelMenu);
mainStackPanel.Children.Add(pasteTextBox);

// Setting the command to the Paste command
pasteMenuItem.Command = ApplicationCommands.Paste;

// Setting the command target to the TextBox
pasteMenuItem.CommandTarget = pasteTextBox;
' Creating the UI objects
Dim mainStackPanel As New StackPanel()
Dim pasteTextBox As New TextBox()
Dim stackPanelMenu As New Menu()
Dim pasteMenuItem As New MenuItem()

' Adding objects to the panel and the menu
stackPanelMenu.Items.Add(pasteMenuItem)
mainStackPanel.Children.Add(stackPanelMenu)
mainStackPanel.Children.Add(pasteTextBox)

' Setting the command to the Paste command
pasteMenuItem.Command = ApplicationCommands.Paste

WPF コマンドの 4 つの主要な概念

WPF のルーティング コマンド モデルは、コマンド、コマンド ソース、コマンド ターゲット、およびコマンド バインドの 4 つの主要な概念に分けることができます。

  • コマンド は、実行するアクションです。

  • コマンド ソース は、コマンドを呼び出すオブジェクトです。

  • コマンド ターゲット は、コマンドが実行されているオブジェクトです。

  • コマンド バインド は、コマンド ロジックをコマンドにマップするオブジェクトです。

前の例では、Paste コマンドはコマンド、MenuItem はコマンド ソース、TextBox はコマンド ターゲット、コマンド バインドは TextBox コントロールによって提供されています。 コマンド ターゲット クラスであるコントロールによって CommandBinding が提供されるとは限りません。 多くの場合、CommandBinding はアプリケーション開発者が作成する必要があります。または、CommandBinding がコマンド ターゲットの先祖にアタッチされている可能性があります。

コマンド

WPF のコマンドは、ICommand インターフェイスを実装することによって作成されます。 ICommand では、Executeと CanExecuteの 2 つのメソッドと、イベント (CanExecuteChanged) が公開されます。 Execute は、コマンドに関連付けられているアクションを実行します。 CanExecute は、現在のコマンド ターゲットでコマンドを実行できるかどうかを決定します。 CanExecuteChanged は、コマンド・マネージャーがコマンド・ソースの変更を検出し、コマンド・バインディングによってまだ実行されていないコマンドを無効にする可能性がある場合に発生します。 ICommand の WPF 実装は RoutedCommand クラスであり、この概要の焦点です。

WPF の入力の主なソースは、マウス、キーボード、インク、ルーティング コマンドです。 デバイス指向の入力が多いほど、RoutedEvent を使用して、入力イベントが発生したことをアプリケーション ページ内のオブジェクトに通知します。 RoutedCommand も特に違いはありません。 Execute の CanExecute メソッドと RoutedCommand メソッドにはコマンドのアプリケーション ロジックは含まれませんが、これらのメソッドが発生させたルーティング イベントは、CommandBinding を含むオブジェクトに遭遇するまで、要素ツリー内をトンネリングおよびバブリングします。 CommandBinding には、これらのイベントのハンドラーが含まれており、コマンドを実行するハンドラーです。 WPF でのイベント ルーティングの詳細については、「ルーティング イベントの概要 」を参照してください。

Execute の RoutedCommand メソッドは、コマンド ターゲットで PreviewExecuted イベントと Executed イベントを発生させます。 CanExecute の RoutedCommand メソッドは、コマンド ターゲットで CanExecute イベントと PreviewCanExecute イベントを発生させます。 これらのイベントは、その特定のコマンドの CommandBinding を持つオブジェクトに遭遇するまで、要素ツリーをトンネリングしてバブルします。

WPF には、MediaCommands、ApplicationCommands、NavigationCommands、ComponentCommands、EditingCommandsなど、いくつかのクラスに分散した一連の一般的なルーティング コマンドが用意されています。 これらのクラスは、コマンドの実装ロジックではなく、RoutedCommand オブジェクトのみで構成されます。 実装ロジックは、コマンドが実行されているオブジェクトの役割です。

コマンド ソース

コマンド ソースは、コマンドを呼び出すオブジェクトです。 コマンド ソースの例としては、MenuItem、Button、KeyGestureがあります。

WPF のコマンド ソースは、通常、ICommandSource インターフェイスを実装します。

ICommandSource では、Command、CommandTarget、CommandParameterの 3 つのプロパティが公開されます。

  • Command は、コマンド ソースが呼び出されたときに実行するコマンドです。

  • CommandTarget は、コマンドを実行するオブジェクトです。 WPF では、CommandTarget の ICommandSource プロパティは、ICommand が RoutedCommandである場合にのみ適用されることに注目してください。 CommandTarget が ICommandSource に設定されていて、対応するコマンドが RoutedCommandでない場合、コマンド ターゲットは無視されます。 CommandTarget が設定されていない場合、キーボード フォーカスを持つ要素がコマンド ターゲットになります。

  • CommandParameter は、コマンドを実装するハンドラーに情報を渡すために使用されるユーザー定義のデータ型です。

ICommandSource を実装する WPF クラスは、ButtonBase、MenuItem、Hyperlink、および InputBindingです。 ButtonBase、MenuItem、および Hyperlink クリックするとコマンドが呼び出され、InputBinding はコマンドに関連付けられている InputGesture が実行されたときにコマンドを呼び出します。

次の例では、MenuItem の ContextMenu を Properties コマンドのコマンド ソースとして使用する方法を示します。

<StackPanel>
  <StackPanel.ContextMenu>
    <ContextMenu>
      <MenuItem Command="ApplicationCommands.Properties" />
    </ContextMenu>
  </StackPanel.ContextMenu>
</StackPanel>
StackPanel cmdSourcePanel = new StackPanel();
ContextMenu cmdSourceContextMenu = new ContextMenu();
MenuItem cmdSourceMenuItem = new MenuItem();

// Add ContextMenu to the StackPanel.
cmdSourcePanel.ContextMenu = cmdSourceContextMenu;
cmdSourcePanel.ContextMenu.Items.Add(cmdSourceMenuItem);

// Associate Command with MenuItem.
cmdSourceMenuItem.Command = ApplicationCommands.Properties;
Dim cmdSourcePanel As New StackPanel()
Dim cmdSourceContextMenu As New ContextMenu()
Dim cmdSourceMenuItem As New MenuItem()

' Add ContextMenu to the StackPanel.
cmdSourcePanel.ContextMenu = cmdSourceContextMenu
cmdSourcePanel.ContextMenu.Items.Add(cmdSourceMenuItem)

' Associate Command with MenuItem.
cmdSourceMenuItem.Command = ApplicationCommands.Properties

通常、コマンド ソースは CanExecuteChanged イベントをリッスンします。 このイベントは、現在のコマンド ターゲットで実行するコマンドの機能が変更された可能性があることをコマンド ソースに通知します。 コマンド ソースは、RoutedCommand メソッドを使用して、CanExecute の現在の状態を照会できます。 コマンドソースは、コマンドを実行できない場合、それ自体を無効にすることができます。 その例として、コマンドを実行できない場合に MenuItem が淡色表示される場合があります。

InputGesture はコマンド ソースとして使用できます。 WPF の 2 種類の入力ジェスチャは、KeyGesture と MouseGestureです。 KeyGesture は、Ctrl + C などのキーボード ショートカットと考えることができます。 KeyGesture は、Key と一連の ModifierKeysで構成されます。 MouseGesture は、MouseAction とオプションの ModifierKeysセットで構成されます。

InputGesture をコマンド ソースとして機能させるには、コマンドに関連付ける必要があります。 これを実現するには、いくつかの方法があります。 1 つの方法は、InputBindingを使用する方法です。

次の例は、KeyBinding と KeyGestureの間に RoutedCommand を作成する方法を示しています。

<Window.InputBindings>
  <KeyBinding Key="B"
              Modifiers="Control" 
              Command="ApplicationCommands.Open" />
</Window.InputBindings>
KeyGesture OpenKeyGesture = new KeyGesture(
    Key.B,
    ModifierKeys.Control);

KeyBinding OpenCmdKeybinding = new KeyBinding(
    ApplicationCommands.Open,
    OpenKeyGesture);

this.InputBindings.Add(OpenCmdKeybinding);
Dim OpenKeyGesture As New KeyGesture(Key.B, ModifierKeys.Control)

Dim OpenCmdKeybinding As New KeyBinding(ApplicationCommands.Open, OpenKeyGesture)

Me.InputBindings.Add(OpenCmdKeybinding)

InputGesture を RoutedCommand に関連付けるもう 1 つの方法は、InputGestureの InputGestureCollection に RoutedCommand を追加することです。

次の例は、KeyGestureの InputGestureCollection に RoutedCommand を追加する方法を示しています。

KeyGesture OpenCmdKeyGesture = new KeyGesture(
    Key.B,
    ModifierKeys.Control);

ApplicationCommands.Open.InputGestures.Add(OpenCmdKeyGesture);
Dim OpenCmdKeyGesture As New KeyGesture(Key.B, ModifierKeys.Control)

ApplicationCommands.Open.InputGestures.Add(OpenCmdKeyGesture)

コマンドバインディング

CommandBinding は、コマンドを実装するイベント ハンドラーにコマンドを関連付けます。

CommandBinding クラスには、Command プロパティと、PreviewExecuted、Executed、PreviewCanExecute、および CanExecute イベントが含まれます。

Command は、CommandBinding が関連付けられているコマンドです。 PreviewExecuted および Executed イベントにアタッチされるイベント ハンドラーは、コマンド ロジックを実装します。 PreviewCanExecute イベントと CanExecute イベントにアタッチされたイベント ハンドラーによって、コマンドが現在のコマンド ターゲットで実行できるかどうかを決定します。

次の例は、アプリケーションのルート CommandBinding に Window を作成する方法を示しています。 CommandBinding は、Open コマンドを Executed ハンドラーと CanExecute ハンドラーに関連付けます。

<Window.CommandBindings>
  <CommandBinding Command="ApplicationCommands.Open"
                  Executed="OpenCmdExecuted"
                  CanExecute="OpenCmdCanExecute"/>
</Window.CommandBindings>
// Creating CommandBinding and attaching an Executed and CanExecute handler
CommandBinding OpenCmdBinding = new CommandBinding(
    ApplicationCommands.Open,
    OpenCmdExecuted,
    OpenCmdCanExecute);

this.CommandBindings.Add(OpenCmdBinding);
' Creating CommandBinding and attaching an Executed and CanExecute handler
Dim OpenCmdBinding As New CommandBinding(ApplicationCommands.Open, AddressOf OpenCmdExecuted, AddressOf OpenCmdCanExecute)

Me.CommandBindings.Add(OpenCmdBinding)

次に、ExecutedRoutedEventHandler と CanExecuteRoutedEventHandler が作成されます。 ExecutedRoutedEventHandler は、コマンドが実行されたことを示す文字列を表示する MessageBox を開きます。 CanExecuteRoutedEventHandler は、CanExecute プロパティを trueに設定します。

void OpenCmdExecuted(object target, ExecutedRoutedEventArgs e)
{
    String command, targetobj;
    command = ((RoutedCommand)e.Command).Name;
    targetobj = ((FrameworkElement)target).Name;
    MessageBox.Show("The " + command +  " command has been invoked on target object " + targetobj);
}
Private Sub OpenCmdExecuted(ByVal sender As Object, ByVal e As ExecutedRoutedEventArgs)
    Dim command, targetobj As String
    command = CType(e.Command, RoutedCommand).Name
    targetobj = CType(sender, FrameworkElement).Name
    MessageBox.Show("The " + command + " command has been invoked on target object " + targetobj)
End Sub
void OpenCmdCanExecute(object sender, CanExecuteRoutedEventArgs e)
{
    e.CanExecute = true;
}
Private Sub OpenCmdCanExecute(ByVal sender As Object, ByVal e As CanExecuteRoutedEventArgs)
    e.CanExecute = True
End Sub

CommandBinding は、アプリケーションやコントロールのルート Window など、特定のオブジェクトにアタッチされます。 CommandBinding がアタッチされているオブジェクトによって、バインディングのスコープが定義されます。 たとえば、コマンド ターゲットの先祖にアタッチされた CommandBinding は、Executed イベントで到達できますが、コマンド ターゲットの子孫にアタッチされた CommandBinding に到達することはできません。 これは、イベントを発生させたオブジェクトからの RoutedEvent のトンネリングとバブリングの方法の直接的な結果です。

CommandBinding クラス、TextBox、Cut、Copy コマンドなど、Paste がコマンド ターゲット自体にアタッチされる場合があります。 ただし、多くの場合、メイン CommandBinding や Application オブジェクトなど、コマンド ターゲットの先祖に Window をアタッチする方が便利です(特に、同じ CommandBinding を複数のコマンド ターゲットに使用できる場合)。 これらは、コマンド実行インフラストラクチャの作成時に考慮する必要がある設計上の決定です。

コマンド ターゲット

コマンド ターゲットは、コマンドが実行される要素です。 RoutedCommandに関して、コマンド ターゲットは、Executed と CanExecute のルーティングを開始する要素です。 前述のように、WPF では、CommandTarget の ICommandSource プロパティは、ICommand が RoutedCommandである場合にのみ適用されます。 CommandTarget が ICommandSource に設定されていて、対応するコマンドが RoutedCommandでない場合、コマンド ターゲットは無視されます。

コマンド ソースは、コマンド ターゲットを明示的に設定できます。 コマンド ターゲットが定義されていない場合は、キーボード フォーカスを持つ要素がコマンド ターゲットとして使用されます。 キーボード フォーカスを持つ要素をコマンド ターゲットとして使用する利点の 1 つは、アプリケーション開発者が同じコマンド ソースを使用して、コマンド ターゲットを追跡しなくても複数のターゲットでコマンドを呼び出すという点です。 たとえば、MenuItem が コントロールと TextBox コントロールを持つアプリケーションで PasswordBox コマンドを呼び出した場合、ターゲットはキーボード フォーカスを持つコントロールに応じて TextBox または PasswordBox にすることができます。

次の例は、マークアップと分離コードでコマンド ターゲットを明示的に設定する方法を示しています。

<StackPanel>
  <Menu>
    <MenuItem Command="ApplicationCommands.Paste"
              CommandTarget="{Binding ElementName=mainTextBox}" />
  </Menu>
  <TextBox Name="mainTextBox"/>
</StackPanel>
// Creating the UI objects
StackPanel mainStackPanel = new StackPanel();
TextBox pasteTextBox = new TextBox();
Menu stackPanelMenu = new Menu();
MenuItem pasteMenuItem = new MenuItem();

// Adding objects to the panel and the menu
stackPanelMenu.Items.Add(pasteMenuItem);
mainStackPanel.Children.Add(stackPanelMenu);
mainStackPanel.Children.Add(pasteTextBox);

// Setting the command to the Paste command
pasteMenuItem.Command = ApplicationCommands.Paste;

// Setting the command target to the TextBox
pasteMenuItem.CommandTarget = pasteTextBox;
' Creating the UI objects
Dim mainStackPanel As New StackPanel()
Dim pasteTextBox As New TextBox()
Dim stackPanelMenu As New Menu()
Dim pasteMenuItem As New MenuItem()

' Adding objects to the panel and the menu
stackPanelMenu.Items.Add(pasteMenuItem)
mainStackPanel.Children.Add(stackPanelMenu)
mainStackPanel.Children.Add(pasteTextBox)

' Setting the command to the Paste command
pasteMenuItem.Command = ApplicationCommands.Paste

コマンドマネージャー

CommandManager は、コマンド関連の関数を多数提供します。 特定の要素との間で PreviewExecuted、Executed、PreviewCanExecute、および CanExecute イベント ハンドラーを追加および削除するための静的メソッドのセットを提供します。 CommandBinding および InputBinding オブジェクトを特定のクラスに登録する手段を提供します。 CommandManager は、RequerySuggested イベントを介して、CanExecuteChanged イベントを発生させる必要があるときにコマンドに通知する手段も提供します。

InvalidateRequerySuggested メソッドは、CommandManager イベントを発生させる RequerySuggested を強制します。 これは、コマンドを無効または有効にする必要があるが、CommandManager が認識している条件ではない条件に役立ちます。

コマンド ライブラリ

WPF には、一連の定義済みコマンドが用意されています。 コマンド ライブラリは、ApplicationCommands、NavigationCommands、MediaCommands、EditingCommands、および ComponentCommandsの各クラスで構成されます。 これらのクラスは、Cut、BrowseBack、BrowseForward、Play、Stop、Pauseなどのコマンドを提供します。

これらのコマンドの多くは、一連の既定の入力バインドを含みます。 たとえば、アプリケーションでコピー コマンドを処理するように指定すると、キーボード バインド "Ctrl + C" が自動的に取得されます。また、タブレット PC のペン ジェスチャや音声情報など、他の入力デバイスのバインドも取得します。

XAML を使用してさまざまなコマンド ライブラリのコマンドを参照する場合は、通常、静的コマンド プロパティを公開するライブラリ クラスのクラス名を省略できます。 一般に、コマンド名は文字列として明確であり、所有する型はコマンドの論理的なグループ化を提供するために存在しますが、あいまいさを解消するために必要ではありません。 たとえば、より詳細な Command="Cut"ではなく、Command="ApplicationCommands.Cut" を指定できます。 これは、コマンド用に WPF XAML プロセッサに組み込まれている便利なメカニズムです (より正確には、読み込み時に WPF XAML プロセッサが参照する ICommandの型コンバーターの動作です)。

カスタム コマンドの作成

コマンド ライブラリ クラス内のコマンドがニーズを満たしていない場合は、独自のコマンドを作成できます。 カスタム コマンドを作成するには、2 つの方法があります。 1 つ目は、最初から開始し、ICommand インターフェイスを実装することです。 もう一方の方法、より一般的なアプローチは、RoutedCommand または RoutedUICommandを作成することです。

カスタム RoutedCommandを作成する例については、「カスタム RoutedCommand サンプルを作成する」を参照してください。

こちらも参照ください