入力の概要

Windows Presentation Foundation (WPF) サブシステムでは、マウス、キーボード、タッチ、スタイラスなど、さまざまなデバイスからの入力を取得するために、強力な API が提供されています。 このトピックでは、WPF で提供されるサービスと、入力システムのアーキテクチャについて説明します。

入力 API

公開されている主な入力 API は、基本要素クラスの UIElementContentElementFrameworkElementFrameworkContentElement にあります。 基本要素の詳細については、「基本要素の概要」を参照してください。 これらのクラスは、キー操作、マウス ボタン、マウス ホイール、マウス動作、フォーカス管理、マウス キャプチャなどに関連する入力イベントの機能を提供しています。 入力アーキテクチャでは、すべての入力イベントをサービスとして処理するのではなく、基本要素に入力 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;
}
' 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) And KeyStates.Down) > 0 Then
    btnNone.Background = Brushes.Red

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

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

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

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

スタイラス入力

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

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

イベント ルーティング

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

イベント ルーティングは、ルートに沿った特定のオブジェクトや要素が、異なる要素によって供給されたイベントに、(処理を介して) 重要な応答を提供できるように、イベントを複数の要素に転送するプロセスです。 ルーティング イベントには、直接、バブル、トンネルのいずれかのルーティング メカニズムが使用されます。 直接ルーティングでは、ソース要素が通知を受ける唯一の要素であり、イベントは他の要素にルーティングされません。 ただし、標準の 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);
' Create the UI elements.
Dim keyboardStackPanel As New StackPanel()
Dim keyboardButton1 As 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.
AddHandler keyboardButton1.KeyDown, AddressOf 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;
        }
    }
}
Private Sub OnButtonKeyDown(ByVal sender As Object, ByVal e As KeyEventArgs)
    Dim source As Button = TryCast(e.Source, Button)
    If source IsNot Nothing Then
        If e.Key = Key.Left Then
            source.Background = Brushes.LemonChiffon
        Else
            source.Background = Brushes.AliceBlue
        End If
    End If
End Sub

マウス入力イベントの例

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

この例の最初のセクションでは、StackPanel コントロールと Button コントロールが作成され、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);
' Create the UI elements.
Dim mouseMoveStackPanel As New StackPanel()
Dim mouseMoveButton As New Button()

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

' Attach Buttons to StackPanel.
mouseMoveStackPanel.Children.Add(mouseMoveButton)

' Attach event handler.
AddHandler mouseMoveButton.MouseEnter, AddressOf OnMouseExampleMouseEnter
AddHandler mouseMoveButton.MouseLeave, AddressOf 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 Sub OnMouseExampleMouseEnter(ByVal sender As Object, ByVal e As MouseEventArgs)
    ' Cast the source of the event to a Button.
    Dim source As Button = TryCast(e.Source, Button)

    ' If source is a Button.
    If source IsNot Nothing Then
        source.Background = Brushes.SlateGray
    End If
End Sub
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;
    }
}
Private Sub OnMosueExampleMouseLeave(ByVal sender As Object, ByVal e As MouseEventArgs)
    ' Cast the source of the event to a Button.
    Dim source As Button = TryCast(e.Source, Button)

    ' If source is a Button.
    If source IsNot Nothing Then
        source.Background = Brushes.AliceBlue
    End If
End Sub

テキスト入力

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

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

WPF で KeyUp/KeyDown イベントが送信されると、キーストロークが TextInput イベントの一部になる場合は (たとえば、Alt + S キーが押された場合)、KeyKey.System に設定されます。 これにより、KeyDown イベント ハンドラーのコードで、Key.System をチェックし、見つかった場合は、以降に発生する TextInput イベントのハンドラーに処理を任せることができます。 これらの場合、TextCompositionEventArgs 引数のさまざまなプロパティを使用して、元のキーストロークを判断できます。 同様に、IME がアクティブになっている場合、Key には値 Key.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);
' Create the UI elements.
Dim textInputStackPanel As New StackPanel()
Dim textInputeButton As New Button()
Dim textInputTextBox As New TextBox()
textInputeButton.Content = "Open"

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

' Attach event handlers.
AddHandler textInputStackPanel.KeyDown, AddressOf OnTextInputKeyDown
AddHandler textInputeButton.Click, AddressOf 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");
}
Private Sub OnTextInputKeyDown(ByVal sender As Object, ByVal e As KeyEventArgs)
    If e.Key = Key.O AndAlso Keyboard.Modifiers = ModifierKeys.Control Then
        handle()
        e.Handled = True
    End If
End Sub

Private Sub OnTextInputButtonClick(ByVal sender As Object, ByVal e As RoutedEventArgs)
    handle()
    e.Handled = True
End Sub

Public Sub handle()
    MessageBox.Show("Pretend this opens a file")
End Sub

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

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

タッチおよび操作

Windows 7 オペレーティング システムの新しいハードウェアと API では、アプリケーションが、複数のタッチからの入力を同時に受け取ることができる機能を提供しています。 WPF を使用すると、タッチが行われたときにイベントを発生させることで、マウスやキーボードなどの他の入力に応答する場合と同様に、アプリケーションでタッチを検出して応答できます。

WPF では、タッチが行われると、タッチ イベントと操作イベントという 2 種類のイベントを公開します。 タッチ イベントは、タッチスクリーン上の各指とその動きについて生データを提供します。 操作イベントは、入力を特定のアクションとして解釈します。 このセクションでは、この 2 種類のイベントについて説明します。

必須コンポーネント

タッチに応答するアプリケーションを開発するには、次のコンポーネントが必要です。

  • Visual Studio 2010。

  • Windows 7。

  • Windows タッチをサポートするデバイス (タッチスクリーンなど)。

用語

タッチについて説明するときに使用される用語を次に示します。

  • タッチは、Windows 7 で認識されるユーザー入力の種類です。 通常、タッチを検知するスクリーンに指を当てると、タッチが開始されます。 デバイスによって指の位置と動きがマウス入力として変換されるだけの場合、ノート PC で一般的なタッチパッドなどのデバイスでは、タッチがサポートされないことに注意してください。

  • マルチタッチは、複数のポイントが同時に行われるときのタッチです。 Windows 7 および WPF では、マルチタッチをサポートします。 WPF に関するドキュメントでタッチについて説明されている場合、その概念はマルチタッチを対象とします。

  • 操作は、タッチが、オブジェクトに適用される物理的なアクションとして解釈されるときに発生します。 WPF では、操作イベントによって、平行移動、拡大縮小、または回転の各操作として入力が解釈されます。

  • touch device は、タッチスクリーン上での 1 本の指など、タッチ入力を生成するデバイスを表します。

タッチに応答するコントロール

次のコントロールは、スクロールされて見えないコンテンツがある場合に、コントロール上を指でドラッグするとスクロールできます。

ScrollViewer で定義されている ScrollViewer.PanningMode 添付プロパティを使用すると、水平方向、垂直方向、またはその両方向のタッチ パンを有効にするか、どちらも有効にしないかどうかを指定することができます。 ScrollViewer.PanningDeceleration プロパティでは、ユーザーがタッチスクリーンから指を離したときに、スクロール速度を遅くする速さを指定します。 ScrollViewer.PanningRatio 添付プロパティでは、操作オフセットを平行移動するためにオフセットをスクロールする比率を指定します。

タッチ イベント

基底クラス UIElementUIElement3DContentElement では、アプリケーションがタッチに応答するようにサブスクライブできるイベントが定義されています。 タッチ イベントは、アプリケーションでタッチがオブジェクトの操作以外の動作として解釈される場合に役立ちます。 たとえば、ユーザーが 1 本以上の指を使って描画できるアプリケーションは、タッチ イベントをサブスクライブします。

この 3 つのクラスはすべて、定義クラスに関係なく、動作がよく似た次のイベントを定義します。

キーボード イベントやマウス イベントと同様に、タッチ イベントはルーティング イベントです。 Preview で始まるイベントはトンネル イベントで、Touch で始まるイベントはバブル イベントです。 ルーティング イベントの詳細については、「ルーティング イベントの概要」を参照してください。 これらのイベントを処理するときは、GetTouchPoint または GetIntermediateTouchPoints メソッドを呼び出すことで、入力の位置を任意の要素に対する相対位置として取得できます。

タッチ イベント間の対話について理解するには、ユーザーがある要素の上に指を 1 本置き、その要素内で指を動かした後、要素から指を離すシナリオを考えてみます。 次の図は、バブル イベントの実行について示しています (わかりやすくするため、トンネル イベントは省略しています)。

The sequence of touch events. タッチ イベント

次のリストに、前の図のイベントのシーケンスを示します。

  1. ユーザーが要素の上に指を置くと、TouchEnter イベントが 1 回発生します。

  2. TouchDown イベントが 1 回発生します。

  3. ユーザーがその要素内で指を移動すると、TouchMove イベントが複数回発生します。

  4. ユーザーがその要素から指を離すと、TouchUp イベントが 1 回発生します。

  5. TouchLeave イベントが 1 回発生します。

3 本以上の指を使用した場合は、それぞれの指に対してイベントが発生します。

操作イベント

アプリケーションでユーザーがオブジェクトを操作できるようにする場合は、UIElement クラスで操作イベントが定義されています。 単にタッチの位置を報告するタッチ イベントとは異なり、操作イベントは、入力がどのように解釈されるかを報告します。 操作には、平行移動、拡大縮小、および回転の 3 種類があります。 次のリストでは、3 種類の操作を呼び出す方法を示します。

  • 平行移動の操作を呼び出すには、オブジェクトの上に指を置き、タッチスクリーン上で指を動かします。 通常、この操作を行うと、オブジェクトが移動します。

  • 拡大縮小の操作を呼び出すには、オブジェクトの上に 2 本の指を置き、2 本の指を近づけたり離したりします。 通常、この操作を行うと、オブジェクトのサイズが変更されます。

  • 回転の操作を呼び出すには、オブジェクトの上に 2 本の指を置き、一方の指を中心にもう一方の指を回転させます。 通常、この操作を行うと、オブジェクトが回転します。

2 種類以上の操作を同時に発生させることもできます。

オブジェクトを操作に応答させると、オブジェクトに慣性があるように見せることができます。 これにより、オブジェクトに現実の世界をシミュレートさせることができます。 たとえば、テーブル上で書籍を押す場合、強く押すと、書籍は離した後も移動し続けます。 WPF を使用すると、ユーザーがオブジェクトから指を離した後に、操作イベントを発生させることで、この動作をシミュレートできます。

ユーザーがオブジェクトの移動、サイズ変更、回転を実行できるアプリケーションを作成する方法は、「チュートリアル: 初めてのタッチ アプリケーションの作成」を参照してください。

UIElement では、次の操作イベントが定義されています。

既定では、UIElement はこのような操作イベントを受け取りません。 UIElement で操作イベントを受け取るには、UIElement.IsManipulationEnabledtrue に設定します。

操作イベントの実行パス

ユーザーがオブジェクトを "スローする" シナリオを検討します。 ユーザーは、オブジェクトの上に指を置き、タッチスクリーン上で指を短い距離移動させ、オブジェクトの移動中に指を離します。 この結果、オブジェクトはユーザーの指の下で移動し、ユーザーが指を離した後も移動を続けます。

次の図は、操作イベントの実行パスと各イベントに関する重要な情報を示しています。

The sequence of manipulation events. 操作イベント

次のリストに、前の図のイベントのシーケンスを示します。

  1. ユーザーがオブジェクトの上に指を置くと、ManipulationStarting イベントが発生します。 特に、このイベントを使用すると ManipulationContainer プロパティを設定できます。 後続のイベントでの操作の位置は、ManipulationContainer が基準になります。 ManipulationStarting 以外のイベントでは、このプロパティは読み取り専用のため、ManipulationStarting イベントでのみ、このプロパティを設定できます。

  2. 次に、ManipulationStarted イベントが発生します。 このイベントは、操作の起点を報告します。

  3. ユーザーの指がタッチスクリーン上で移動すると、ManipulationDelta イベントが複数回発生します。 ManipulationDeltaEventArgs クラスの DeltaManipulation プロパティでは、操作が移動、拡大縮小、または平行移動のいずれとして解釈されるかが報告されます。 ここで、オブジェクトを操作する作業のほとんどを実行します。

  4. ユーザーの指がオブジェクトから離れると、ManipulationInertiaStarting イベントが発生します。 このイベントでは、慣性による処理中の操作の減速を指定できます。 これは、選択した場合、オブジェクトが異なる物理領域や属性をエミュレートできるようにするためです。 たとえば、アプリケーションが現実の世界のアイテムを表す 2 つのオブジェクトを保持していて、一方のオブジェクトがもう一方のオブジェクトよりも重いとします。 重いオブジェクトを軽いオブジェクトよりもすばやく減速させるようにすることができます。

  5. 慣性による処理が発生すると、ManipulationDelta イベントが複数回発生します。 このイベントは、ユーザーの指がタッチスクリーン上で移動したとき、および WPF が慣性による処理をシミュレートしたときに発生することに注意してください。 つまり、ManipulationDelta は、ManipulationInertiaStarting イベントの前と後に発生します。 ManipulationDeltaEventArgs.IsInertial プロパティでは、慣性による処理中に ManipulationDelta イベントが発生するかどうかが報告されるため、そのプロパティを確認し、その値に応じて異なるアクションを実行できます。

  6. 操作と慣性による処理が終了すると、ManipulationCompleted イベントが発生します。 つまり、すべての ManipulationDelta イベントが発生した後、ManipulationCompleted イベントが発生して、操作が完了したことが通知されます。

UIElement では、ManipulationBoundaryFeedback イベントも定義されています。 このイベントは、ReportBoundaryFeedback メソッドが ManipulationDelta イベント内で呼び出されると発生します。 ManipulationBoundaryFeedback イベントを使用すると、アプリケーションまたはコンポーネントで、オブジェクトが境界に接したときに視覚的フィードバックを提供できます。 たとえば、Window クラスで ManipulationBoundaryFeedback イベントを処理し、ウィンドウの端に接したときにそのウィンドウを少し移動します。

ManipulationBoundaryFeedback イベントを除き、操作イベントのイベント引数に対して Cancel メソッドを呼び出すことにより、操作を取り消すことができます。 Cancel を呼び出すと、操作イベントは発生しなくなり、タッチに対してマウス イベントが発生します。 次の表は、操作が取り消される時間と発生するマウス イベントとの間の関係を示しています。

Cancel が呼び出されるイベント 既に行われている入力に対して発生するマウス イベント
ManipulationStarting および ManipulationStarted マウス ボタンを押すイベント。
ManipulationDelta マウス ボタンを押すイベントおよびマウス移動イベント。
ManipulationInertiaStarting および ManipulationCompleted マウス ボタンを押すイベント、マウス移動イベント、およびマウス ボタンを放すイベント。

操作が慣性による処理中に、Cancel が呼び出された場合、メソッドによって false が返され、入力ではマウス イベントが発生しないことにご注意ください。

タッチ イベントと操作イベントの関係

UIElement では、常にタッチ イベントを受け取ることができます。 IsManipulationEnabled プロパティを true に設定すると、UIElement でタッチ イベントと操作イベントの両方を受け取ることができます。 TouchDown イベントが処理されない場合 (つまり、Handled プロパティを false に設定した場合)、操作ロジックで要素へのタッチがキャプチャされて、操作イベントが生成されます。 TouchDown イベントで Handled プロパティが true に設定されている場合、操作ロジックでは操作イベントは生成されません。 次の図は、タッチ イベントと操作イベントの関係を示しています。

Relationship between touch and manipulation events タッチ イベントと操作イベント

次のリストに、前の図に示したタッチ イベントと操作イベントの関係を示します。

フォーカス

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

キーボード フォーカス

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

キーボード フォーカスは、要素に Tab キーで移動したり、TextBox などの特定の要素でマウスをクリックしたりすることで、取得されます。 キーボード フォーカスは、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);
}
Private Sub OnLoaded(ByVal sender As Object, ByVal e As RoutedEventArgs)
    ' Sets keyboard focus on the first Button in the sample.
    Keyboard.Focus(firstButton)
End Sub

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

論理フォーカス

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

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

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

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);
Dim focuseScope2 As New StackPanel()
FocusManager.SetIsFocusScope(focuseScope2, True)

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

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

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

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

マウスの位置

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

マウス キャプチャ

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

コマンド

コマンドでは、デバイス入力よりもセマンティックなレベルの入力処理が可能です。 コマンドは、CutCopyPasteOpen などの簡単なディレクティブです。 コマンドは、コマンド ロジックを一元管理するために役立ちます。 Menu から、ToolBar 上で、またはキーボード ショートカットを使用して、同じコマンドにアクセスできます。 また、コマンドでは、コマンドが使用できないときに、コントロールを無効にするための機構も提供されます。

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

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

WPF では、ApplicationCommandsMediaCommandsComponentCommandsNavigationCommandsEditingCommands で構成される一般的なコマンドのライブラリが提供されています。または、独自のライブラリを定義することもできます。

TextBox にキーボード フォーカスがある場合に、クリックされると 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;

// 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 のコマンドの詳細については、「コマンド実行の概要」を参照してください。

入力システムと基本要素

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

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

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

次の内容

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

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

関連項目