前回、サブスレッドを使ってマウスの座標を取得し続ける方法を学びました。しかし、やりたいことを整理すると、
- マウスカーソルがウインドウの上にあるときだけ座標を取得できれば良い
- 座標を取得するのは、どこから輪切りにするかを決めるときだけ
ということになって、ひたすら律儀に座標を取得し続ける必要はなさそうですし、座標の取得をしている最中に、他の込み入った処理をしなければならないこともなさそうです。
それで色々と調べ回ったところ、マウスに関するイベントを利用してあげれば、それほど面倒な処理をしなくてもやりたいことが出来そう、ということが分かりました。
イベントとは、マイクロコンピューターの世界で言う割り込み処理です。私は、ハードウエアをいじる仕事をしていた経験から、割り込み処理と言った方がピンときます。
マウスに関係するイベントは全部で6種類あります。ここで言うマウスに関係するイベントとは、クリックやダブルクリックではなく、マウスがコントロールの上に来たときや、マウスがコントロールの上を動いたときに発生するイベントのことです。こちらに詳細説明がありますが、機械翻訳なのでわかりにくいです。
この中で、MouseHoverイベントと、MouseMoveイベントに着目しました。MouseHoverイベントはマウスカーソルがコントロールの上にあるときに発生するイベント、MouseMoveはマウスカーソルがコントロールの上を移動すると発生するイベントです。
Mainメソッドが書かれたファイルは前回と同じなので割愛します。
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; namespace sample27 { public partial class Form1 : Form { public static int mouse_x = 0, mouse_y = 0; //マウスポインタの座標置き場 public Form1() { InitializeComponent(); this.MaximumSize = this.Size; //ウィンドウサイズを変更できない用にする処理 this.MinimumSize = this.Size; //ロードされたときのウィンドウサイズを取得して、Min/Maxサイズ両方に設定 } // マウスが動いたときに発生するイベント private void Form1_MouseMove(object sender, MouseEventArgs e) { getMousePosition(); } // マウスがウインドウの中に入ったときに発生するイベント private void Form1_MouseHover(object sender, EventArgs e) { getMousePosition(); } // Formが非選択になったときに発生 private void Form1_Leave(object sender, EventArgs e) { if(!this.Focused) { label1.Text = "Lost Focus"; } } // Formが無効化されたときに発生 private void Form1_Deactivate(object sender, EventArgs e) { label1.Text = "Deactivated"; mouse_x = mouse_y = (int)0xffff; this.Text = "x=" + mouse_x + ":y=" + mouse_y; } private void pictureBox1_MouseHover(object sender, EventArgs e) { getMousePosition(); } private void pictureBox1_MouseMove(object sender, MouseEventArgs e) { getMousePosition(); } // マウスの座標を取得、ウィンドウ内の座標(クライアント座標)に変換して出力 private void getMousePosition( ) { if (this.Focused) { System.Drawing.Point cp = this.PointToClient(Cursor.Position); mouse_x = cp.X; mouse_y = cp.Y; label1.Text = "Focused"; this.Text = "x=" + mouse_x + ":y=" + mouse_y; } else { mouse_x = mouse_y = (int)0xffff; label1.Text = "Not Focused"; this.Text = "x=" + mouse_x + ":y=" + mouse_y; } } } }これがうまく動いた初版プログラムです。
上記リストでは、2種類のMouseHoverイベント、MouseMoveイベント(Form1とpictureBox1)を捕まえて、getMousePosition()メソッドでマウスカーソルのシステム座標をウインドウ内の座標に変換して求めています。
Formの上にPictureBoxが乗っているようなウインドウの場合は、PictureBoxが置いてあるエリアではFormに関するマウスイベントは発生しません。したがって、このようにFormとPictureBoxの二つのマウスイベントのハンドラーを用意しなければなりません。右の図において、Form1の中の破線の四角がPictureBoxです。
なお、マウスイベントの処理をする以外に、フォームがアクティブになったときとフォームが選択されなくなったときに発生するイベントを使って、フォームが選択されているかどうかをフォームに表示させる機能も付けてあります。
プログラムを起動してウインドウを選択し、マウスをウインドウに乗せると、マウスカーソルの座標がウインドウの上に表示されます。
ウインドウを非選択にすると、下記のようにNot Focusedと表示され、座標値が65535(0xffff)、(0xffff)になります。
今回の目標仕様では、このマウスカーソルの座標取得方法で十分と判断し、この方法を採用することにしました。
プログラムを動かしてみたところを動画にしてみました。
なお、その後の検討で、MouseHoverイベントは、コントロール上にマウスがある一定時間止まっていないと発生しないイベントということがわかり、使わなくてもイベントを取りこぼすことはないだろうと判断しました。
したがって、最終的には上記プログラムからMouseHoverイベントの処理に関するコードを抜き取ることにしました。
今回はここまでにします。