第10回 WPFの「入力イベントとアニメーション」を学ぼう:連載:WPF入門(1/2 ページ)
WPFでキーボードやマウス、スタイラス、マルチタッチなどによる入力を処理する方法と、アニメーションを実現する、ストーリーボードなどの各種機能を解説。
powered by Insider.NET
今回は入力イベントとアニメーションについて説明していく。
前回までの説明は、Visual Studioの無償版であるVisual C# ExpressやVisual Basic Expressでも試すことができた。しかし、今回は、有償製品のExpression Blendの利用が前提となる説明が多くなっている。その点ご容赦いただきたい。Expression Blendを利用するのは、Visual Studioにはアニメーションを視覚的に編集する機能がないためだ。
一応、視覚的な編集ツールが不要で、Expression Blend付属のライブラリだけなら、無償で入手可能なBlend SDKをインストールすれば利用可能である。
* 本稿での「ユーザー」という表記はすべて、WPFアプリケーションを利用する「エンド・ユーザー」を指す。
■入力
連載第6回で説明したように、モデル(=見た目と関係なく成り立つロジックやデータ)に対するイベント通知はコマンド(=意味論的なイベント処理機構)という層を通して行うことが多く、この用途で入力イベントを直接処理する機会は多くないだろう。入力イベントは以下のような場面で利用することになる。
- コマンド・ソースとなるユーザー・コントロールを自作する
- アニメーション開始などのトリガーとして使い、インタラクティブなビューを作成する
●入力に関連するクラス
WPFでは、キー入力関連ならKeyboardクラス、マウス入力関連ならMouseクラス(いずれもSystem.Windows.Input名前空間)というように、入力デバイスの種類ごとにルーティング・イベントを定義するクラスが存在する。例えば、キー・ダウン・イベントの場合、KeyboardクラスのKeyDownEventルーティング・イベントが発生する。
また、UIElementクラス(System.Windows名前空間)には、UI要素がこれらのルーティング・イベントを受け取ったときに発生するCLRイベントが定義されている。従って、XAMLコード中では、以下のような2通りのイベント・ハンドラ登録の方法がある。
<!-- Keyboard クラスのルーティング・イベント(添付イベント) -->
<TextBox Keyboard.KeyDown="TextBox_KeyDown" />
<!-- UIElement クラスのイベント -->
<TextBox KeyDown="TextBox_KeyDown" />
「添付イベント(attached event)」とは、添付プロパティのルーティング・イベント版のようなもので、ほかのクラスで定義されているルーティング・イベントを受け取って処理するための機構である。
●バブル・イベントとトンネル・イベント
入力イベントにはバブル・イベントとトンネル・イベント(第6回参照)があり、トンネル・イベントの名前には語頭に「Preview」が付いている(付いていないものはバブル・イベント)。例えば、キー・ダウンなら、バブル・イベントが「KeyDown」、トンネル・イベントが「PreviewKeyDown」となる。
コントロールなどでは、バブル・イベントを「処理済み」(=それ以上イベントが親要素に伝搬(でんぱ)しない)にしてしまうものがある(例えば、ボタンはマウス・クリックを、テキストボックスはキー・ダウンを処理済みにする)。子要素の種類によらず、親要素側でイベントを拾いたい場合にはトンネル・イベント(=Previewで始まるイベント)を利用する。
次節からは個々のイベントについて、主要なものを紹介していこう。1つの入力イベントに対して関連するプロパティやメソッド、イベントが複数あるが、命名規則は一貫しているので使い分けは難しくない。次節以降では、例えば、「KeyboardクラスのKeyDownイベント」といった場合、以下のようなものが存在するものと読み替えてほしい。
◆ Keyboardクラス中:
- KeyDownEvent静的プロパティ
- AddKeyDownHandler静的メソッド
- RemoveKeyDownHandler静的メソッド
◆ UIElementクラス中:
- OnKeyDownメソッド
- KeyDownイベント
また、「Preview」を付けることでトンネル・イベントとなるので、バブル・イベントのみを紹介する。
●キーボード
Keyboardクラス(System.Windows.Input名前空間)に以下のようなイベントが定義されている。
○キー
- KeyDown: キーを押したときに発生する。
- KeyUp: キーを離したときに発生する。
どのキーが押されたかは、イベント・ハンドラの第2引数(KeyEventArgsクラス)のKeyプロパティで取得できる。また、KeyEventArgsクラスにはKeyboardDeviceプロパティ(KeyboardDeviceクラス)があり、これを介して[Shift]キーなどの修飾キーに関する情報も取得できる。
○フォーカス
- GotKeyboardFocus: UI要素がキーボード・フォーカスを得たときに発生する。
- LostKeyboardFocus: UI要素がキーボード・フォーカスを失ったときに発生する。
また、イベント駆動ではなく、能動的にキーボードの状態を調べるために、KeyboardクラスにはIsKeyDownやGetKeyStatesなどの静的メソッドも定義されている。
○キーボード・フォーカスと論理フォーカス
WPFのフォーカスには、以下のように、2種類の概念が存在する。
- キーボード・フォーカス: 実際にキーボードからの入力を受け取れる状態。デスクトップにあるすべてのウィンドウの中で1つのUI要素だけがキーボード・フォーカスを持つ。
- 論理フォーカス: フォーカス範囲(=通常は1つのウィンドウが1つのフォーカス範囲を持つ)ごとに1つのUI要素が論理フォーカスを持つ。一度アクティブでなくなったウィンドウが再度アクティブになった際、論理フォーカスを持つUI要素がキーボード・フォーカスを得る。
KeyboardクラスのGotKeyboardFocusイベントおよびLostKeyboardFocusイベントはキーボード・フォーカスの取得/消失で発生するイベントである。一方で、論理フォーカスに関しては、FocusManagerクラス(System.Windows.Input名前空間)に以下のようなイベントが定義されている。
- GotFocus: UI要素が論理フォーカスを得たときに発生する。
- LostFocus: UI要素が論理フォーカスを失ったときに発生する。
Movie 1にキーボード・フォーカスと論理フォーカスの差を確認する例を示す。
この例では、フォーカスを得た瞬間にアニメーションを開始するようにしてある。テキストボックスを移動した際にはキーボード・フォーカスと論理フォーカスの両方が変化するのに対して、ウィンドウを切り替えた際にはキーボード・フォーカスだけが変化する。
●テキスト入力
キーボードからのキー入力とは別に、キーボード/手書き認識/音声認識のいずれの入力かを問わないテキスト入力のためのイベントとして、TextCompositionManagerクラス(System.Windows.Input名前空間)に以下のイベントが定義されている。
- TextInputStart: テキスト入力を開始したときに発生する。
- TextInputUpdate: テキスト入力の状態が変化したときに発生する。
- TextInput: テキスト入力が完了したときに発生する。
TextInputStartイベントやTextInputUpdateイベントを利用すれば、IMEによる日本語変換の途中の状態を取得することもできる。
Movie 2に、音声認識、手書き認識、および、IME経由によるテキスト入力の例を示す。
TextInputイベントが起こるたびに、入力されたテキストを右側のリストボックスに追加している(左側はただのテキストボックス)。
●マウス
Mouseクラス(System.Windows.Input名前空間)に以下のようなイベントが定義されている。
○マウス・ボタン/ホイール
- MouseDown: マウス・ボタンを押したときに発生する。
- MouseUp: マウス・ボタンを離したときに発生する。
- MouseWheel: ホイール操作を行ったときに発生する。
これらMouseクラスで定義されているイベントのほかに、UIElementクラスでは以下のようなイベントが定義されている。
- MouseLeftButtonDown/MouseLeftButtonUp: マウスの左ボタンのダウン/アップで発生する。
- MouseRightButtonDown/MouseRightButtonUp: マウスの右ボタンのダウン/アップで発生する。
マウスの左ボタンを押した際にはMouseDownイベントとMouseLeftButtonDownイベントの両方が発生するので利用の際には注意が必要である。
ちなみにダブル・クリックに関するイベントは、UIElementクラスにはなく、ControlクラスでMouseDoubleClickイベントが定義されている。
○マウス移動
- MouseEnter: マウス・ポインタがUI要素の上に乗った瞬間に発生する。
- MouseMove: マウス・ポインタがUI要素上を動くか、キャプチャ中のマウスが動いた場合に発生する。
- MouseLeave: マウス・ポインタがUI要素上から離れた瞬間に発生する。
- GotMouseCapture: マウスがキャプチャ開始された瞬間に発生する。
- LostMouseCapture: マウスのキャプチャが解除された瞬間に発生する。
マウスのキャプチャ(=マウス・ポインタの位置によらず、コントロールがすべてのマウス入力コマンドを受け取るようにようにマウスを捕捉すること)は、UIElementクラスのCaptureMouseメソッドで、キャプチャ解除はReleaseMouseCaptureメソッドで行う。
また、マウス・ポインタの位置は、MouseクラスのGetPosition静的メソッドか、MouseDeviceクラス(=イベント・ハンドラの第2引数からオブジェクトを取得可能)のGetPositionメソッドを使って取得できる。GetPositionメソッドは、引数にUI要素を与えることで、そのUI要素の左上座標を起点とした相対座標を取得する。
【コラム】マウス・カーソルの形状を変化させるには?
UI要素上にマウス・ポインタを乗せたときに、カーソルの形状を変化させたい場合、FrameworkElementクラス(System.Windows名前空間)のCursorプロパティを利用する。Cursorプロパティに指定するカーソルは、CursorクラスのコンストラクタでWindowsのカーソル・ファイル(拡張子は「.cur」や「.ani」)を読み込んだものか、Cursorsクラス(System.Windows.Input名前空間)の静的プロパティとして定義されている標準カーソルのいずれかを利用する。
また、FrameworkElementクラスのForceCursorプロパティの値を「true」にすることで、(子要素でのCursorプロパティの設定を無視して)自身のCursorプロパティの値を強制することができる。
●ドラッグ&ドロップ
DragDropクラス(System.Windows.Input名前空間)に以下のようなイベントが定義されている。
- DragEnter/DragOver/DragLeave: ドラッグ中のマウス・ポインタがUI要素上に乗った瞬間/動いたとき/離れた瞬間に発生する。
- Drop: ドロップされると発生する。
- GiveFeedBack: ドロップ・ターゲットからドロップ・ソースへのフィードバック用のイベント。
- QueryContinue: ドラッグ&ドロップ操作中にキーボード操作などがあったときに、ドラッグ&ドロップ操作を続けるかどうかを判断するためのイベント。
●スタイラス
Stylusクラス(System.Windows.Input名前空間)にスタイラス関連のイベントが定義されている。
スタイラスはマウスとしても認識される(=スタイラス・イベントと同時にマウス・イベントが発生する)ため、スタイラス固有の機能を利用しない場合にはマウス・イベントの処理だけでもアプリケーションを作成できる。
MouseDownイベントに対してStylusDownイベントがあり、CaptureMouseメソッドに対してCaptureStylusメソッドがあるなど、マウス・イベントから類推できるスタイラス・イベントも多い。一方、スタイラス固有のイベントとしては以下のようなものがある。
- StylusInrange/StylusOutOfRange: スタイラスがタブレット範囲に入った/離れた瞬間に発生する。
- StylusInAirMove: スタイラスをタブレットから浮かせながら移動させたときに発生する。
- StylusSystemGesture: スタイラス・ジェスチャ*1をユーザーが実行したときに発生する。
*1 スタイラス・ジェスチャとは、例えばタップやドラッグなどのこと。利用可能なジェスチャについては、SystemGesture列挙体(System.Windows.Input名前空間)のメンバを参照してほしい。
●マルチタッチ
Windows 7のマルチタッチ対応に合わせて、WPF 4でタッチ・イベントが追加された。WPF 4では方針が変更されたようで、マウス・イベントでいうところのMouseクラスに相当するTouchクラスやManipulationクラス(いずれもSystem.Windows.Input名前空間)にはルーティング・イベントが(publicには)定義されていない。UIElementクラスにTouchDownなどのイベントが定義されているので、これらを利用する。
マルチタッチ・デバイスに関連したイベントには以下の2つの種類がある。
- タッチ・イベント: マルチタッチ・デバイスに指が触れたときなどの、直接的なイベント。
- 操作(manipuration)イベント: タッチ・ジェスチャ*2による回転・拡大・平行移動などの、一段階抽象度の高い情報が得られるイベント。
*2 タッチ・ジェスチャとは、例えばパンやズームなどのこと。詳しくは、windows.microsoft.comの「Windows 7 タッチ ジェスチャ」を参照してほしい。すなわち、操作イベントとは、フレームワーク上でタッチ・イベントを要約して、平行移動や拡大/縮小などの、プログラマーに分かりやすい情報に変換してからイベントを発生させるものである。
前者のタッチ・イベントは、スタイラス同様、マウス・イベントも発生させるため、マウス・イベントの処理だけでもある程度のアプリケーションを作成できる。イベント名も、MouseDownイベントに対してTouchDownイベントがあるなど、マウス・イベントから類推可能である。
一方、操作イベントには以下のようなものがある。
- ManipulationStarted: ユーザーがタッチ操作を開始したときに発生する。
- ManipulationStarting: タッチ操作可能なUI要素(=IsManipulationEnabledプロパティの値が「true」)に対してユーザーがタッチ操作を開始したときに発生する。
- ManipulationInertiaStarting: ユーザーが慣性操作(=勢いよく指を動かしたときに、しばらく慣性的にManipulationDeltaイベントが起きる)を開始したときに発生する。
- ManipulationDelta:ユーザーが指を動かしてタッチ操作に変化が生じたときに発生する。
- ManipulationCompleted: ユーザーがタッチ操作を終えたときに発生する。
- ManipulationBoundaryFeedback: 操作対象のUI要素がタッチ操作可能な範囲の境界に到達したときに発生する。
Movie 3に操作イベントの利用例を示す。
この例では、Blend SDK付属のTranslateZoomRotateBehaviorを利用して、操作イベントに応じてUI要素の回転・拡大・平行移動を行っている。詳細は本稿の後半で説明するが、動画を見てのとおり、UI要素にビヘイビアを付与する(Expression Blend上ではドラッグ&ドロップ操作で付与可能)だけで、操作イベントに応じて回転・拡大・平行移動ができる。
Copyright© Digital Advantage Corp. All Rights Reserved.