WisdomSoft - for your serial experiences.

5.1 マウス入力

UIElement オブジェクト上でマウスボタンが押されるなど、マウスカーソルに関するイベントを受けて処理する方法を解説します。

5.1.1 クリックに反応する

ここからは、ウィンドウ上のコンテンツに対して、ユーザーが何らかの入力を行ったときに発生するイベントを処理する方法について説明します。Microsoft .NET では、言語レベルでイベント処理の機能が提供されているため、WPF もこれに従います。本書では、C# 言語の構文としてのデリゲートやイベントについての解説は行いません。これらは、Windows Forms アプリケーション時代から使われていた技術です。

WPF でも、基本的なイベントの概念や処理方法に変更はありません。開発者は、イベントを処理するためのイベントハンドラを用意し、監視したいコントロールが公開しているイベントに、イベントハンドラのデリゲートを渡すだけです。イベント登録手続きが適切に行われていれば、アプリケーション実行時に、ユーザーがコントロールに何らかの入力を行うと、イベントハンドラが自動的にコールバックされます。この仕組みは、これまでの .NET アプリケーションと同じです。

WPF では、コントロールも図形も、同一の UIElement クラスから派生しています。よって、こうした描画要素に共通する、マウスやキーボードなどの基本的なイベントは全て UIElement クラスで提供されています。イベントの処理方法は WPF 独自の新しいものではありません。本書では基本的なイベント処理の例を紹介するに留めます。詳細は、ドキュメントを参照してください。

最も基本的な操作である、マウスが押されたことを感知するには MouseDown イベントを利用します。

UIElement クラス MouseDown イベント
public event MouseButtonEventHandler MouseDown

MouseDown イベントには System.Windows.Input.MouseButtonEventHandler デリゲートを追加、または削除できます。このイベントに登録されているデリゲートがイベント発生時(マウスが押されたとき)に呼び出されます。

System.Windows.Input.MouseButtonEventHandler デリゲート
public delegate void MouseButtonEventHandler (
    Object sender,
    MouseButtonEventArgs e
)

MouseButtonEventHandler デリゲートは、sender パラメータにイベント発生元のオブジェクトを、e にイベントに関連する情報を提供するイベントオブジェクトを受け取ります。このデリゲートのパラメータも、古くから .NET で使われている設計に基づいています。

MouseButtonEventHandler デリゲートが受け取るイベントオブジェクトは、マウスのボタン入力に関連する情報を提供する System.Windows.Input.MouseButtonEventArgs クラスです。このクラスは、マウスの各種ボタンが押されているかどうかなどの情報をプロパティから提供します。

System.Windows.Input.MouseButtonEventArgs クラス
System.Object 
   System.EventArgs 
     System.Windows.RoutedEventArgs 
       System.Windows.Input.InputEventArgs 
         System.Windows.Input.MouseEventArgs 
          System.Windows.Input.MouseButtonEventArgs
public class MouseButtonEventArgs : MouseEventArgs

イベントハンドラが、イベント発生時の情報を提供する e パラメータを利用するかどうかは開発者の問題です。イベントが発生したと言う事実だけに興味がある場合は e を使う必要はありませんし、押されたボタンなどに応じて処理結果を分岐させたい場合は MouseButtonEventArgs から情報を取得します。

コード1
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Shapes;
using System.Windows.Input;

class Test : Window {
	[STAThread]
	public static void Main() {
		Window wnd = new Test();
		Application app = new Application();
		app.Run(wnd);
	}

	private Ellipse ellipse;
	public Test() {
		ellipse = new Ellipse();
		ellipse.Fill = Brushes.Blue;
		ellipse.Width = 200;
		ellipse.Height = 200;
		ellipse.MouseDown += ellipseMouseDown;

		Content = ellipse;
	}

	private void ellipseMouseDown(object sender, MouseButtonEventArgs e) {
		ellipse.Fill = Brushes.Red;
	}
}
実行結果
コード1 実行結果 コード1 実行結果

コード1は、アプリケーション起動時に、ウィンドウのコンテンツとして青いブラシで内部を塗りつぶす Ellipse オブジェクトを追加しています。表示された青い楕円形をマウスでクリックすると Ellipse オブジェクトの MouseDown イベントに追加されているデリゲートから ellipseMouseDown() メソッドがコールバックされ、Ellipse オブジェクトの Fill プロパティに赤いブラシを設定します。これら一連の作業は、ユーザーにとって楕円形をクリックすると色が変わるという処理になります。

5.1.2 イベント情報

押されたボタンに応じて処理を分岐させるには、イベントハンドラで受け取る MouseButtonEventArgs オブジェクトを利用します。このクラスは、 イベント発生時のボタンの状態を返すプロパティを提供しています。

どのマウスボタンの入力によってイベントが発生したかという情報は ChangedButton プロパティから取得することができます。

MouseButtonEventArgs クラス ChangedButton プロパティ
public MouseButton ChangedButton { get; }

ChangedButton が返すのは、イベントの引金となったマウスボタンを表す値です。この値は、System.Windows.Input.MouseButton 列挙体のいずれかのメンバです。

System.Windows.Input.MouseButton 列挙体
public enum MouseButton

MouseButton 列挙隊のメンバは、左ボタンを表す Left、中央ボタンを表す Middle、右ボタンを表す Right、そして拡張ボタンを表す XButton1 と XButton2 があります。

コード2
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;

class Test : Window {
	[STAThread]
	public static void Main() {
		Window wnd = new Test();
		Application app = new Application();
		app.Run(wnd);
	}

	private TextBlock textBlock;
	public Test() {
		textBlock = new TextBlock();
		textBlock.Text = "マウスボタンを押してください";
		textBlock.FontSize = 30;

		MouseDown += mouseDown;
		Content = textBlock;
	}

	private void mouseDown(object sender, MouseButtonEventArgs e) {
		textBlock.Text = "MouseButton=" + e.ChangedButton;
	}
}
実行結果
コード2 実行結果

コード2は、ウィンドウ上でマウスボタンを押すと、押されたマウスボタンに対応する MouseButton 列挙体の値を表示するプログラムです。

イベントを発生させたボタンとは無関係にイベント発生時のマウスボタンの状態を取得するために、個別のボタンの状態を保存するプロパティが用意されています。左ボタンを表す LeftButton プロパティ、右ボタンを表す RightButton プロパティ、中央ボタンを表す MiddleButton プロパティから、それぞれのボタンの状態を取得することができます。

MouseEventArgs クラス LeftButton プロパティ
public MouseButtonState LeftButton { get; }
MouseEventArgs クラス RightButton プロパティ
public MouseButtonState RightButton { get; }
MouseEventArgs クラス MiddleButton プロパティ
public MouseButtonState MiddleButton { get; }

これらのプロパティが返す値は System.Windows.Input.MouseButtonState 列挙体のメンバのいずれかです。

System.Windows.Input.MouseButtonState 列挙体
public enum MouseButtonState

この列挙体には、ボタンが押されている状態を表す Pressed メンバと、離されている状態を表す Released メンバの 2 つが宣言されています。

イベントハンドラが受け取った MouseButtonEventArgs オブジェクトから、調べたいボタンの MouseButtonState 列挙体の値を取得し、Pressed メンバに一致すればボタンが押されていることを確認できます。

コード3
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Shapes;
using System.Windows.Input;

class Test : Window {
	[STAThread]
	public static void Main() {
		Window wnd = new Test();
		Application app = new Application();
		app.Run(wnd);
	}

	private Ellipse ellipse;
	public Test() {
		ellipse = new Ellipse();
		ellipse.Stroke = Brushes.Black;
		ellipse.Fill = Brushes.White;
		ellipse.Width = 200;
		ellipse.Height = 200;
		ellipse.MouseDown += ellipseMouseDown;

		Content = ellipse;
	}

	private void ellipseMouseDown(object sender, MouseButtonEventArgs e) {
		if (e.LeftButton == MouseButtonState.Pressed)
			ellipse.Fill = Brushes.Red;
		else if (e.RightButton == MouseButtonState.Pressed)
			ellipse.Fill = Brushes.Blue;
	}
}
実行結果
コード3 実行結果

コード3は、アプリケーション起動時の初期状態で、内部を白色のブラシで塗りつぶす Ellipse オブジェクトを表示します。この楕円形をマウスの左ボタン、または右ボタンでクリックすると、ブラシを変更して色が変化するように仕組んでいます。

ellipseMouseDown() メソッドでは、e パラメータから左ボタンや右ボタンが押されているかどうかを調べています。もし、イベント発生時に左ボタンが押されている場合は楕円形を赤色に塗りつぶし、右ボタンが押されていれば青色に塗りつぶすように、プログラムを if 文で分岐させています。

マウスイベントでは、マウスボタンの状態の他に、イベントが発生したときのカーソルの座標も重要な情報となります。カーソルの座標は GetPosition() メソッドから取得することができます。

MouseEventArgs クラス GetPosition() メソッド
public Point GetPosition (IInputElement relativeTo)

このメソッドの relativeTo パラメータには、カーソルの座標を受ける IInputElement インターフェイスを実装しているオブジェクトを指定しなければなりません。FrameworkElement クラスが IInputElement インターフェイスを実装しているので GetPosition() メソッドには、イベントが発生したコントロールを渡すのが一般的でしょう。

このメソッドが返す Point オブジェクトにマウスカーソルの座標が格納されています。この座標は、relativeTo で指定したコントロールの相対座標となります。

コード4
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Shapes;
using System.Windows.Input;

class Test : Window {
	[STAThread]
	public static void Main() {
		Window wnd = new Test();
		Application app = new Application();
		app.Run(wnd);
	}

	private Ellipse ellipse;
	public Test() {
		ellipse = new Ellipse();
		ellipse.Fill = Brushes.Black;
		ellipse.Width = 100;
		ellipse.Height = 100;

		Canvas canvas = new Canvas();
		canvas.Children.Add(ellipse);

		Content = canvas;
		MouseDown += thisMouseDown;
	}

	private void thisMouseDown(object sender, MouseButtonEventArgs e) {
		Point pt = e.GetPosition(this);
		Canvas.SetLeft(ellipse, pt.X - (ellipse.Width / 2));
		Canvas.SetTop(ellipse, pt.Y - (ellipse.Height / 2));
	}
}
実行結果
コード4 実行結果

コード4は、マウスボタンを押すと、Canvas 上の楕円形が移動するというプログラムです。このプログラムでは MouseDown イベントが発生すると、イベントハンドラで Ellipse オブジェクトの座標を、イベント発生時のカーソルの座標を参考に再設定しています。

詳細は省略しますが、MouseDown イベントのような形で、この他にも多くのマウスイベントが用意されています。

表1 マウスイベント
イベント 発生条件
MouseDown マウスボタンが要素上で押された。
MouseUp マウスボタンが要素上で離された。
MouseLeftButtonDown マウスの左ボタンが要素上で押された。
MouseLeftButtonUp マウスの左ボタンが要素上で離された。
MouseRightButtonDown マウスの右ボタンが要素上で押された。
MouseRightButtonUp マウスの右ボタンが要素上で離された。
MouseWheel マウスホイールが要素上で回転した。
MouseEnter マウスカーソルが要素の領域上に侵入した。
MouseMove マウスカーソルが要素の領域上で移動した。
MouseLeave マウスカーソルが要素の領域の外に出た。

表1は、UIElement クラスで提供されている主なマウスイベントです。これらのイベントは発生条件やデリゲート型が異なりますが、基本的な利用方法は同じです。