コマンド実行の概要

コマンド実行は、デバイス入力よりも意味を持つレベルで入力を処理する Windows Presentation Foundation (WPF) の入力機構です。 コマンドの例としては、多くのアプリケーションで目にする [コピー][切り取り][貼り付け] などの操作があります。

ここでは、WPF にはどんなコマンドがあるか、コマンド実行モデルに含まれるクラス、およびアプリケーションでコマンドを使用および作成する方法について説明します。

このトピックは、次のセクションで構成されています。

  • コマンドとは

  • WPF の簡単なコマンドの例

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

  • コマンド ライブラリ

  • カスタム コマンドの作成

コマンドとは

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

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

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

WPF の簡単なコマンドの例

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

TextBox にキーボード フォーカスがある場合に、MenuItem がクリックされると TextBoxPaste コマンドを呼び出すように MenuItem を設定する方法を次の例に示します。

<StackPanel>
  <Menu>
    <MenuItem Command="ApplicationCommands.Paste" />
  </Menu>
  <TextBox />
</StackPanel>
            ' 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
// 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;

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

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

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

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

  • コマンドの対象は、コマンドが実行されているオブジェクトです。

  • コマンド バインディングは、コマンド ロジックをコマンドに割り当てるオブジェクトです。

前の例では、Paste コマンドがコマンド、MenuItem がコマンド ソース、TextBox がコマンドの対象であり、コマンド バインディングは TextBox コントロールによって提供されています。 ただし、CommandBinding が、コマンドの対象クラスであるコントロールによって必ず提供されるとは限らないことに注意してください。 アプリケーション開発者が CommandBinding を作成する必要がある場合や、CommandBinding をコマンドの対象の親に割り当てる場合も多くあります。

コマンド

WPF のコマンドを作成するには、ICommand インターフェイスを実装します。 ICommand は、Execute および CanExecute という 2 つのメソッドと、CanExecuteChanged イベントを公開します。 Execute はコマンドに関連するアクションを実行します。CanExecute は現在のコマンドの対象でコマンドを実行できるかどうかを決定します。 CanExecuteChanged は、コマンド実行操作を集中管理するコマンド マネージャーが、発生済みだがコマンド バインディングによってまだ実行されていないコマンドを無効にする可能性がある変更をコマンド ソース内で検出した場合に発生します。 WPF での ICommand の実装は RoutedCommand クラスです。この概要ではこのクラスを中心に説明しています。

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

RoutedCommandExecute メソッドは、コマンドの対象で PreviewExecuted イベントと Executed イベントを発生させます。 RoutedCommandCanExecute メソッドは、コマンドの対象で CanExecute イベントと PreviewCanExecute イベントを発生させます。 これらのイベントは、その特定のコマンドの CommandBinding を持つオブジェクトを検出するまで、要素ツリー内をトンネリングおよびバブリングします。

WPF は、複数のクラスに散在する一連の一般的なルーティング コマンドである MediaCommandsApplicationCommandsNavigationCommandsComponentCommands、および EditingCommands を提供します。 これらのクラスは、RoutedCommand オブジェクトのみで構成され、コマンドの実装ロジックは含まれません。 実装ロジックは、コマンドが実行されているオブジェクトに含まれます。

コマンド ソース

コマンド ソースは、コマンドを呼び出すオブジェクトです。 コマンド ソースには、MenuItemButtonKeyGesture などがあります。

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

ICommandSource は、CommandCommandTarget、および CommandParameter の 3 つのプロパティを公開します。

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

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

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

ICommandSource を実装する WPF のクラスには、ButtonBaseMenuItemHyperlink、および InputBinding があります。 ButtonBaseMenuItem、および Hyperlink は、クリックされるとコマンドを呼び出し、InputBinding は、それに関連付けられている InputGesture が実行されるとコマンドを呼び出します。

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

<StackPanel>
  <StackPanel.ContextMenu>
    <ContextMenu>
      <MenuItem Command="ApplicationCommands.Properties" />
    </ContextMenu>
  </StackPanel.ContextMenu>
</StackPanel>
            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
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;

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

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

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

KeyGestureRoutedCommand の間に KeyBinding を作成する方法を次の例に示します。

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

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

            Me.InputBindings.Add(OpenCmdKeybinding)
KeyGesture OpenKeyGesture = new KeyGesture(
    Key.B,
    ModifierKeys.Control);

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

this.InputBindings.Add(OpenCmdKeybinding);

InputGestureRoutedCommand に関連付ける別の方法は、InputGestureRoutedCommandInputGestureCollection に追加する方法です。

KeyGestureRoutedCommandInputGestureCollection に追加する方法を次の例に示します。

            Dim OpenCmdKeyGesture As New KeyGesture(Key.B, ModifierKeys.Control)

            ApplicationCommands.Open.InputGestures.Add(OpenCmdKeyGesture)
KeyGesture OpenCmdKeyGesture = new KeyGesture(
    Key.B,
    ModifierKeys.Control);

ApplicationCommands.Open.InputGestures.Add(OpenCmdKeyGesture);

CommandBinding

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

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

Command は、CommandBinding が関連付けられているコマンドです。 PreviewExecuted イベントと Executed イベントに割り当てられたイベント ハンドラーは、コマンド ロジックを実装します。 PreviewCanExecute イベントと CanExecute イベントに割り当てられたイベント ハンドラーは、コマンドが現在のコマンドの対象で実行可能かどうかを判断します。

アプリケーションのルート WindowCommandBinding を作成する方法を次の例に示します。 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
            Dim OpenCmdBinding As New CommandBinding(ApplicationCommands.Open, AddressOf OpenCmdExecuted, AddressOf OpenCmdCanExecute)

            Me.CommandBindings.Add(OpenCmdBinding)
// Creating CommandBinding and attaching an Executed and CanExecute handler
CommandBinding OpenCmdBinding = new CommandBinding(
    ApplicationCommands.Open,
    OpenCmdExecuted,
    OpenCmdCanExecute);

this.CommandBindings.Add(OpenCmdBinding);

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

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 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 OpenCmdCanExecute(ByVal sender As Object, ByVal e As CanExecuteRoutedEventArgs)
    e.CanExecute = True
End Sub
void OpenCmdCanExecute(object sender, CanExecuteRoutedEventArgs e)
{
    e.CanExecute = true;
}

CommandBinding は、アプリケーションまたはコントロールのルート Window などの特定のオブジェクトに割り当てられます。 CommandBinding が割り当てられたオブジェクトは、バインディングのスコープを定義します。 たとえば、コマンドの対象の先祖に割り当てられた CommandBinding には、Executed イベントによって到達できますが、コマンドの対象の子孫に割り当てられた CommandBinding には到達できません。 これは、イベントを発生させるオブジェクトから RoutedEvent がトンネリングおよびバブリングするというしくみが直接影響するからです。

場合によっては、CommandBinding は、TextBox クラスに対する CutCopyPaste の各コマンドのように、コマンドの対象自体に割り当てられます。 ただし、CommandBinding は、特に同じ CommandBinding を複数のコマンドの対象で使用できる場合には、メイン Window やアプリケーション オブジェクトなど、コマンドの対象の先祖に割り当てる方が便利な場合がよくあります。 これらは、コマンド実行インフラストラクチャの作成時に検討される設計上の決定事項です。

コマンドの対象

コマンドの対象は、コマンドが実行される要素です。 RoutedCommand のコマンドの対象は、Executed および CanExecute のルーティングが開始される要素です。 前に述べたように、WPF では、ICommandSourceCommandTarget プロパティは、ICommandRoutedCommand の場合にのみ適用されます。 ICommandSourceCommandTarget が設定されていても、対応するコマンドが RoutedCommand でない場合は、コマンドの対象は無視されます。

コマンド ソースは、コマンドの対象を明示的に設定できます。 コマンドの対象が定義されていない場合は、キーボード フォーカスを持つ要素がコマンドの対象として使用されます。 キーボード フォーカスを持つ要素をコマンドの対象として使用する利点の 1 つは、アプリケーション開発者が同じコマンド ソースを使用して、コマンドの対象を追跡せずにコマンドを複数の対象で呼び出すことができることです。 たとえば、MenuItemTextBox コントロールと PasswordBox コントロールを持つアプリケーションで [貼り付け] コマンドを呼び出した場合、その対象は、キーボード フォーカスのあるコントロールに応じて TextBox または PasswordBox にすることができます。

マークアップと分離コードでコマンドの対象を明示的に設定する方法を次の例に示します。

<StackPanel>
  <Menu>
    <MenuItem Command="ApplicationCommands.Paste"
              CommandTarget="{Binding ElementName=mainTextBox}" />
  </Menu>
  <TextBox Name="mainTextBox"/>
</StackPanel>
            ' 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
// 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;

CommandManager

CommandManager は、多数のコマンド関連機能を提供します。 PreviewExecutedExecutedPreviewCanExecute、および CanExecute の各イベント ハンドラーを特定の要素に対して追加および削除するための一連の静的メソッドを提供します。 CommandBinding オブジェクトと InputBinding オブジェクトを特定のクラスに登録する手段を提供します。 また、CommandManager は、RequerySuggested イベントを介して、CanExecuteChanged イベントを発生させるタイミングをコマンドに通知する方法も提供します。

InvalidateRequerySuggested メソッドは、CommandManagerRequerySuggested イベントを強制的に発生させます。 これは、コマンドを無効または有効にする必要がある場合に役立ちますが、CommandManager が認識しない場合には使用しません。

コマンド ライブラリ

WPF は、一連の定義済みコマンドを提供します。 コマンド ライブラリは、ApplicationCommandsNavigationCommandsMediaCommandsEditingCommands、および ComponentCommands の各クラスから構成されます。 これらのクラスは、CutBrowseBackBrowseForwardPlayStopPause などのコマンドを提供します。

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

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

カスタム コマンドの作成

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

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

参照

処理手順

方法 : ICommandSource を実装する

方法 : MenuItem にコマンドを追加する

参照

RoutedCommand

CommandBinding

InputBinding

CommandManager

概念

入力の概要

ルーティング イベントの概要

その他の技術情報

カスタム RoutedCommand の作成のサンプル