入力の概要

更新 : 2007 年 11 月

Windows Presentation Foundation (WPF) サブシステムには、マウス、キーボード、およびスタイラスを含むさまざまなデバイスから入力を取得するための、強力な API が用意されています。

ここでは、WPF によって提供されるサービスと、入力システムのアーキテクチャについて説明します。

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

  • 入力 API
  • イベントのルーティング
  • 入力イベントの処理
  • テキスト入力
  • フォーカス
  • マウスの位置
  • マウス キャプチャ
  • コマンド
  • 入力システムと基本要素
  • 次の内容
  • 関連トピック

入力 API

公開されている主な入力 API は、基本要素クラス UIElementContentElementFrameworkElement、および FrameworkContentElement で提供されています。基本要素の詳細については、「基本要素の概要」を参照してください。これらのクラスは、キー操作、マウス ボタン、マウス ホイール、マウス動作、フォーカス管理、マウス キャプチャなどに関する入力イベントの機能を提供します。入力アーキテクチャでは、すべての入力イベントをサービスとして処理するのではなく、基本要素に入力 API を配置することにより、UI 内の特定のオブジェクトによって入力イベントを供給し、複数の要素が入力イベントを処理できるイベント ルーティング方法をサポートしています。多くの入力イベントは、それぞれに関連付けられた一対のイベントがあります。たとえば、キー押下イベントは、KeyDown イベントおよび PreviewKeyDown イベントと関連付けられています。これらのイベントの違いは、ターゲット要素にルーティングされる方法です。プレビュー イベントは、ルート要素からターゲット要素へ、要素ツリーを下位に向かいます (トンネル)。バブル イベントは、ターゲット要素からルート要素へ、上位に向かいます (バブル)。WPF のイベント ルーティングについては、この概要トピックの中、および「ルーティング イベントの概要」で詳細に説明します。

Keyboard クラスと Mouse クラス

基本要素クラスでの入力 API に加えて、Keyboard クラスと Mouse クラスでは、キーボード入力およびマウス入力を処理するための追加 API が提供されています。

Keyboard クラスでの入力 API の例としては、現在押されている ModifierKeys を返す Modifiers プロパティや、指定したキーが押されているかどうかを判断する IsKeyDown メソッドなどがあります。

次の例では、GetKeyStates メソッドを使用して、Key が押された状態であるかどうかを判断します。

// Uses the Keyboard.GetKeyStates to determine if a key is down.
// A bitwise AND operation is used in the comparison. 
// e is an instance of KeyEventArgs.
if ((Keyboard.GetKeyStates(Key.Return) & KeyStates.Down) > 0)
{
    btnNone.Background = Brushes.Red;
}

Mouse クラスの入力 API の例としては、マウスの中央ボタンの状態を取得する MiddleButton や、現在マウス ポインタの下にある要素を取得する DirectlyOver などがあります。

次の例では、マウスの LeftButtonPressed 状態であるかどうかを判断します。

if (Mouse.LeftButton == MouseButtonState.Pressed)
{
    UpdateSampleResults("Left Button Pressed");
}

Mouse クラスおよび Keyboard クラスについては、この概要でさらに詳しく説明します。

スタイラス入力

WPF では、Stylus が統合的にサポートされます。Stylus は Tablet PC によって一般的になったペン入力です。WPF アプリケーションでは、マウス API を使用してスタイラスをマウスとして処理できますが、WPF では、キーボードとマウスに類似したモデルを使用するスタイラス デバイス抽象クラスも公開されます。スタイラス関連のすべての API には、"Stylus" という単語が含まれます。

スタイラスはマウスとして動作できるため、マウス入力だけをサポートするアプリケーションでも、ある程度のスタイラス入力が自動的にサポートされます。スタイラスがこのように使用される場合、アプリケーションは、適切なスタイラス イベントを処理する機会が与えられた後、対応するマウス イベントを処理します。さらに、インク入力などのより高いレベルのサービスも、スタイラス デバイス抽象クラスを介して使用できます。入力としてのインクの詳細については、「インクの概要」を参照してください。

イベントのルーティング

FrameworkElement は、他の要素を子要素としてそのコンテンツ モデルに格納し、要素のツリーを形成できます。WPF では、イベントを処理することによって、親要素が、その子要素またはその他の子孫要素に命令された入力に関与できます。これは、小さいコントロールからコントロールを構築する場合に特に役立ちます。このプロセスは "コントロール コンポジション" または "合成処理" と呼ばれます。要素ツリーおよび要素ツリーとイベント ルートの連携の詳細については、「WPF のツリー」を参照してください。

イベント ルーティングは、イベントを複数の要素に転送するプロセスであり、ルートに沿った特定のオブジェクトまたは要素が、異なる要素によって供給されたイベントに対して、(処理を介して) 重要な応答を提供できます。ルーティング イベントには、直接、バブル、トンネルのいずれか 1 つのルーティング機構が使用されます。直接ルーティングでは、ソース要素が通知を受ける唯一の要素であり、他の要素にイベントはルーティングされません。ただし、標準の CLR イベントとは対照的に、直接ルーティング イベントは、ルーティング イベントにのみ存在するいくつかの追加機能を引き続き提供します。バブル ルーティングでは、最初にイベントの発生元である要素に通知し、次にその親要素へと順に通知することで、要素ツリーの上位へ処理が実行されます。トンネル ルーティングでは、要素ツリーのルートから始まり下位へと処理が実行され、ソース要素で処理が終了します。ルーティング イベントの詳細については、「ルーティング イベントの概要」を参照してください。

一般に、WPF の入力イベントはトンネル イベントとバブル イベントが対になって構成されます。トンネリング イベントは、"Preview" プレフィックスによってバブルリング イベントと区別されます。たとえば、PreviewMouseMove はマウス移動イベントのトンネリング イベントであり、MouseMove は、このイベントのバブリング イベントです。このイベントのペアは、要素レベルで実装される規則であり、WPF イベント システムの継承機能ではありません。詳細については、「ルーティング イベントの概要」の「WPF の入力イベント」を参照してください。

入力イベントの処理

要素に対する入力を受け取るには、イベント ハンドラをその特定のイベントに関連付ける必要があります。XAML では、これは簡単です。イベントの名前を、このイベントをリッスンする要素の属性として参照します。次に、属性の値を、デリゲートに基づいて、定義するイベント ハンドラの名前に設定します。イベント ハンドラは、C# などのコードで記述する必要があり、分離コード ファイルに含めることができます。

キーボード イベントは、キーボード フォーカスが要素上にある状態で、オペレーティング システムがキー操作を報告すると発生します。マウス イベントとスタイラス イベントにはそれぞれ、要素を基準とするポインタ位置の変更を報告するイベントと、デバイス ボタンの状態の変更を報告するイベントの 2 種類があります。

キーボード入力イベントの例

左方向キーが押されるのを待ち受ける場合の例を次に示します。Button を保持する StackPanel を作成します。左方向キーが押されるのをリッスンするイベント ハンドラを、Button インスタンスに結合します。

この例の最初の部分では、StackPanelButton を作成し、KeyDown のイベント ハンドラを結合しています。

<StackPanel>
  <Button Background="AliceBlue"
          KeyDown="OnButtonKeyDown"
          Content="Button1"/>
</StackPanel>
// Create the UI elements.
StackPanel keyboardStackPanel = new StackPanel();
Button keyboardButton1 = new Button();

// Set properties on Buttons.
keyboardButton1.Background = Brushes.AliceBlue;
keyboardButton1.Content = "Button 1";

// Attach Buttons to StackPanel.
keyboardStackPanel.Children.Add(keyboardButton1);

// Attach event handler.
keyboardButton1.KeyDown += new KeyEventHandler(OnButtonKeyDown);

2 番目の部分はコード内に記述され、イベント ハンドラを定義しています。左方向キーが押され、Button にキーボード フォーカスがあると、ハンドラが実行され、ButtonBackground の色が変更されます。左方向キー以外のキーが押された場合は、ButtonBackground の色が開始時の色に変更されます。

private void OnButtonKeyDown(object sender, KeyEventArgs e)
{
    Button source = e.Source as Button;
    if (source != null)
    {
        if (e.Key == Key.Left)
        {
            source.Background = Brushes.LemonChiffon;
        }
        else
        {
            source.Background = Brushes.AliceBlue;
        }
    }
}

マウス入力イベントの例

次の例では、マウス ポインタが Button に入ると、ButtonBackground の色を変更します。マウスが Button から離れると、Background の色が元に戻ります。

この例の最初の部分では、StackPanelButton コントロールを作成し、MouseEnter イベントと MouseLeave イベントのイベント ハンドラを Button に結合しています。

<StackPanel>
  <Button Background="AliceBlue"
          MouseEnter="OnMouseExampleMouseEnter"
          MouseLeave="OnMosueExampleMouseLeave">Button

  </Button>
</StackPanel>
// Create the UI elements.
StackPanel mouseMoveStackPanel = new StackPanel();
Button mouseMoveButton = new Button();

// Set properties on Button.
mouseMoveButton.Background = Brushes.AliceBlue;
mouseMoveButton.Content = "Button";

// Attach Buttons to StackPanel.
mouseMoveStackPanel.Children.Add(mouseMoveButton);

// Attach event handler.
mouseMoveButton.MouseEnter += new MouseEventHandler(OnMouseExampleMouseEnter);
mouseMoveButton.MouseLeave += new MouseEventHandler(OnMosueExampleMouseLeave);

この例の 2 番目の部分はコード内に記述され、イベント ハンドラを定義しています。Button へのマウス入力があると、ButtonBackground の色が SlateGray に変更されます。Button からマウスが離れると、 ButtonBackground の色が AliceBlue に戻ります。

private void OnMouseExampleMouseEnter(object sender, MouseEventArgs e)
{
    // Cast the source of the event to a Button.
    Button source = e.Source as Button;

    // If source is a Button.
    if (source != null)
    {
        source.Background = Brushes.SlateGray;
    }
}
private void OnMosueExampleMouseLeave(object sender, MouseEventArgs e)
{
    // Cast the source of the event to a Button.
    Button source = e.Source as Button;

    // If source is a Button.
    if (source != null)
    {
        source.Background = Brushes.AliceBlue;
    }
}

テキスト入力

TextInput イベントは、デバイスに依存しない方法でテキスト入力をリッスンできるようにします。テキスト入力の主要な手段はキーボードですが、音声認識、手書き、およびその他の入力デバイスもテキスト入力を生成できます。

キーボード入力の場合、WPF は、最初に適切な KeyDown / KeyUp イベントを送信します。そのイベントが処理されず、キーがテキストの場合 (方向キーやファンクション キーなどのコントロール キーでない場合)、TextInput イベントが発生します。複数のキーストロークでテキスト入力の 1 文字が生成される場合や、単一のキーストロークで複数文字の文字列が生成される場合があるため、KeyDown/KeyUp、および TextInput の各イベント間の対応付けは、常に 1 対 1 の単純な対応付けであるとは限りません。これは、入力方式エディタ (IME) を使用して多くの異なる文字をそれぞれの対応するアルファベットで生成する、中国語、日本語、韓国語のような言語の場合に特に当てはまります。

WPF が KeyUp/KeyDown イベントを送信したときに、キーストロークが TextInput イベントの一部になる場合は (たとえば、Alt + S キーが押された場合)、KeyKey.System に設定されます。これにより、KeyDown イベント ハンドラ内のコードが Key.System をチェックし、見つかった場合は、以降に発生する TextInput イベントのハンドラの処理を任せます。これらのケースでは、引数 TextCompositionEventArgs のさまざまなプロパティを使用して、元のキーストロークを決定できます。同様に、IME が有効な場合、KeyKey.ImeProcessed の値を持ち、ImeProcessedKey により元のキーストロークが示されます。

次の例では、Click イベントのハンドラと、KeyDown イベントのハンドラを定義します。

コードまたはマークアップの最初の部分では、ユーザー インターフェイスを作成します。

<StackPanel KeyDown="OnTextInputKeyDown">
  <Button Click="OnTextInputButtonClick"
          Content="Open" />
  <TextBox> . . . </TextBox>
</StackPanel>
// Create the UI elements.
StackPanel textInputStackPanel = new StackPanel();
Button textInputeButton = new Button();
TextBox textInputTextBox = new TextBox();
textInputeButton.Content = "Open";

// Attach elements to StackPanel.
textInputStackPanel.Children.Add(textInputeButton);
textInputStackPanel.Children.Add(textInputTextBox);

// Attach event handlers.
textInputStackPanel.KeyDown += new KeyEventHandler(OnTextInputKeyDown);
textInputeButton.Click += new RoutedEventHandler(OnTextInputButtonClick);

コードの 2 つ目のセグメントには、イベント ハンドラが含まれます。

private void OnTextInputKeyDown(object sender, KeyEventArgs e)
{
    if (e.Key == Key.O && Keyboard.Modifiers == ModifierKeys.Control)
    {
        handle();
        e.Handled = true;
    }
}

private void OnTextInputButtonClick(object sender, RoutedEventArgs e)
{
    handle();
    e.Handled = true;
} 

public void handle()
{
    MessageBox.Show("Pretend this opens a file");
}

入力イベントはイベント ルートを上方向へ通知するので、どの要素にキーボード フォーカスがあっても StackPanel が入力を受け取ります。TextBox コントロールが最初に通知され、TextBox が入力を処理しない場合のみ、OnTextInputKeyDown ハンドラが呼び出されます。KeyDown イベントの代わりに PreviewKeyDown イベントを使用すると、OnTextInputKeyDown ハンドラが最初に呼び出されます。

この例では、Ctrl + O キー操作とボタンのクリック イベントの、2 回の処理ロジックが記述されています。これは、入力イベントを直接処理するのではなく、コマンドを使用することで簡単化できます。コマンドについては、この概要と「コマンド実行の概要」で詳しく説明します。

フォーカス

WPF では、フォーカスに関してキーボード フォーカスと論理フォーカスという 2 つの主要な概念があります。

キーボード フォーカス

キーボード フォーカスは、キーボード入力を受け取っている要素を指します。キーボード フォーカスを持つ要素は、デスクトップ全体で 1 つしかありません。WPF では、キーボード フォーカスを持つ要素の IsKeyboardFocused は true に設定されます。Keyboard の静的メソッド FocusedElement は、現在キーボード フォーカスを持っている要素を返します。

キーボード フォーカスは、TextBox などの要素に Tab キーで移動したり、特定の要素でマウスをクリックしたりすることで取得できます。キーボード フォーカスは、Keyboard クラスで Focus メソッドを使用してプログラムにより取得することもできます。Focus は、指定された要素へのキーボード フォーカスの設定を試みます。Focus によって返される要素は、現在キーボード フォーカスを持っている要素です。

要素でキーボード フォーカスを取得するためには、Focusable プロパティと IsVisible プロパティを true に設定する必要があります。Panel などの一部のクラスでは、Focusable の既定値は false です。したがって、要素がキーボード フォーカスを取得できるようにする場合は、このプロパティを true に設定する必要があります。

Focus を使用して、キーボード フォーカスを Button に設定する例を次に示します。アプリケーションで初期フォーカスを設定する場所は、Loaded イベント ハンドラ内にすることをお勧めします。

private void OnLoaded(object sender, RoutedEventArgs e)
{
    // Sets keyboard focus on the first Button in the sample.
    Keyboard.Focus(firstButton);
}

キーボード フォーカスの詳細については、「フォーカスの概要」を参照してください。

論理フォーカス

論理フォーカスとは、フォーカス範囲内の FocusManager.FocusedElement を意味します。アプリケーションでは、複数の要素が論理フォーカスを持つことがありますが、特定のフォーカス範囲で論理フォーカスを持つ要素は 1 つだけに限られます。

フォーカス範囲とは、その範囲内の FocusedElement を追跡するコンテナ要素です。フォーカスがフォーカス範囲を離れると、フォーカスがある要素はキーボード フォーカスを失いますが、論理フォーカスは引き続き保持します。フォーカスがフォーカス範囲に戻ると、フォーカスがある要素はキーボード フォーカスを得ます。これにより、キーボード フォーカスが複数のフォーカス範囲間で変更されても、フォーカスが戻ると、そのフォーカス範囲内のフォーカスのある要素が再びフォーカスのある要素になることが保証されます。

Extensible Application Markup Language (XAML) で FocusManager の添付プロパティ IsFocusScope を true に設定することで、またはコードで SetIsFocusScope メソッドを使用してこの添付プロパティを設定することで、要素をフォーカス範囲内に変更できます。

IsFocusScope 添付プロパティを設定して、StackPanel をフォーカス範囲にする例を次に示します。

<StackPanel Name="focusScope1" 
            FocusManager.IsFocusScope="True"
            Height="200" Width="200">
  <Button Name="button1" Height="50" Width="50"/>
  <Button Name="button2" Height="50" Width="50"/>
</StackPanel>
StackPanel focuseScope2 = new StackPanel();
FocusManager.SetIsFocusScope(focuseScope2, true);

既定でフォーカス範囲になる、WPF のクラスは、WindowMenuToolBar、および ContextMenu です。

キーボード フォーカスを持つ要素は、その要素が属するフォーカス範囲の論理フォーカスも持ちます。したがって、Keyboard クラスまたは基本要素クラスの Focus メソッドで要素にフォーカスを設定することは、その要素へのキーボード フォーカスと論理フォーカスの設定を試みることになります。

フォーカス範囲内でフォーカスのある要素を判断するには、GetFocusedElement を使用します。フォーカス範囲内でフォーカスのある要素を変更するには、SetFocusedElement を使用します。

論理フォーカスの詳細については、「フォーカスの概要」を参照してください。

マウスの位置

WPF の 入力 API は、座標空間に関する役立つ情報を提供します。たとえば、座標 (0,0) は左上の座標ですが、これはツリーのどの要素の左上でしょうか。入力対象の要素でしょうか。イベント ハンドラを結び付けた要素でしょうか。または、それ以外でしょうか。混乱を防ぐため、WPF の入力 API でマウスを介して取得した座標を扱う場合は、座標系を指定する必要があります。GetPosition メソッドは、指定した要素を基準としたマウス ポインタの座標を返します。

マウス キャプチャ

マウス デバイスは、マウス キャプチャと呼ばれるモーダル特性を特別に備えています。マウス キャプチャは、ドラッグ アンド ドロップ操作の開始時に、入力の遷移状態を保持する目的で使用されます。これにより、マウス ポインタの画面上での公称位置に関係する他の操作が、必ずしも発生しません。ドラッグの間、ユーザーはドラッグ アンド ドロップを中止しない限りクリックできません。マウス キャプチャはドラッグ元によって保持されますが、マウスを置いたときの手掛かりの大部分は適切でなくなります。入力システムは、マウス キャプチャの状態を判断できる API、および、特定の要素に対してマウス キャプチャを実行したり、マウス キャプチャの状態をクリアできる API を公開します。ドラッグ アンド ドロップ操作の詳細については、「ドラッグ アンド ドロップの概要」を参照してください。

コマンド

コマンドでは、デバイス入力と比べて、より意味的なレベルで入力を処理できます。コマンドは、Cut、Copy、Paste、Open などの簡単なディレクティブです。コマンドは、コマンド ロジックを集中化する場合に役立ちます。MenuToolBar、またはキーボード ショートカットから、同じコマンドにアクセスできます。またコマンドは、コマンドが使用できない場合にコントロールを無効にするための機構を提供します。

RoutedCommand は、WPF での ICommand の実装です。RoutedCommand を実行すると、コマンドの対象で PreviewExecuted イベントと Executed イベントが発生し、他の入力と同様に要素ツリー内をトンネリングおよびバブリングします。コマンドの対象が設定されていない場合は、キーボード フォーカスを持つ要素がコマンドの対象になります。コマンドを実行するロジックは、CommandBinding に結び付けられます。Executed イベントがその特定のコマンドの CommandBinding に到達すると、CommandBindingExecutedRoutedEventHandler が呼び出されます。このハンドラが、コマンドのアクションを実行します。

コマンド実行の詳細については、「コマンド実行の概要」を参照してください。

WPF には、ApplicationCommandsMediaCommandsComponentCommandsNavigationCommands、および EditingCommands で構成される一般的なコマンドのライブラリが用意されています。または、独自のライブラリを定義することもできます。

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

<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;

WPF のコマンドの詳細については、「コマンド実行の概要」を参照してください。

入力システムと基本要素

MouseKeyboard、および Stylus クラスによって定義される添付イベントなどの入力イベントは、入力システムによって発生し、実行時に行われるビジュアル ツリーのヒット テストに基づいて、オブジェクト モデル内の特定の位置に挿入されます。

MouseKeyboard、および Stylus によって添付イベントとして定義される各イベントは、基本要素クラスの UIElement および ContentElement でも、新しいルーティング イベントとして再度公開されます。基本要素のルーティング イベントは、元の添付イベントを処理し、イベント データを再利用するクラスによって生成されます。

入力イベントが、その基本要素の入力イベント実装を通じて特定のソース要素に関連付けられると、論理ツリー オブジェクトとビジュアル ツリー オブジェクトの組み合わせに基づく残りのイベント ルートを通じてルーティングされ、アプリケーション コードで処理されます。一般に、XAML とコードの両方で直感的なイベント ハンドラ構文を使用できるため、UIElement および ContentElement のルーティング イベントを使用して、このようなデバイス関連の入力イベントを処理すると便利です。プロセスを開始した添付イベントを処理することもできますが、いくつかの問題があります。添付イベントには、基本要素クラス処理によって処理されることがマークされる可能性があり、添付イベントに対してハンドラを添付するために、本当のイベント構文ではなく、アクセサ メソッドを使用する必要があります。

次の内容

WPF で入力を処理するさまざまな方法について説明しました。また、さまざまな種類の入力イベントと、WPF で使用されるルーティングされたイベントの機能について、理解を深める必要があります。

WPF のフレームワーク要素とイベントのルーティングの詳細を説明するリソースが他にもあります。詳細については、「コマンド実行の概要」、「フォーカスの概要」、「基本要素の概要」、「WPF のツリー」、および「ルーティング イベントの概要」を参照してください。

参照

概念

フォーカスの概要

コマンド実行の概要

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

基本要素の概要

その他の技術情報

プロパティ