MFCの基礎 - マウス
概要
MFCアプリケーションにおけるマウス処理は、Windowsのメッセージドリブン設計に基づいて実装される。
マウス入力は、WM_LBUTTONDOWN、WM_MOUSEMOVE、WM_MOUSEWHEELなどのWindowsメッセージとして処理される。
MFCは、これらのメッセージを処理するための便利なメッセージハンドラ (OnLButtonDown、OnMouseMove、OnMouseWheel等) を提供している。
- マウスメッセージの種類と処理フロー
- マウス位置の取得と座標変換
- マウスボタン状態の取得
- MFCのマウスイベントハンドラ
- マウスカーソルの制御
- マウスキャプチャによるドラッグ操作
- マウストラッキングとホバー検出
- マウス入力のシミュレーション
マウスメッセージのパラメータには、マウスの座標情報 (lParam) とボタン状態 (wParam) が含まれる。
座標はクライアント座標系で提供されるため、スクリーン座標との変換が必要な場合もある。
MFCのマウス処理を理解することにより、以下に示す機能を実装できる。
- ドラッグアンドドロップ機能
- 描画アプリケーションのペン操作
- ズーム・パン操作
- コンテキストメニューの表示
- ツールチップやホバー効果の実装
マウスメッセージ
Windowsは、マウス入力を複数のメッセージとして処理する。
| メッセージ | 説明 |
|---|---|
| WM_LBUTTONDOWN WM_LBUTTONUP |
左ボタンが押下された時と離された時に送信される。 |
| WM_RBUTTONDOWN WM_RBUTTONUP |
右ボタンが押下された時と離された時に送信される。 |
| WM_MBUTTONDOWN WM_MBUTTONUP |
中ボタンが押下された時と離された時に送信される。 |
| WM_MOUSEMOVE | マウスカーソルが移動した時に送信される。 |
| WM_MOUSEWHEEL | 垂直マウスホイールが回転した時に送信される。 |
| WM_MOUSEHWHEEL | 水平マウスホイールが回転した時に送信される。 |
| WM_LBUTTONDBLCLK WM_RBUTTONDBLCLK |
ダブルクリックされた時に送信される。 |
| WM_NCLBUTTONDOWN 等 | 非クライアント領域でのマウス入力時に送信される。 |
WM_LBUTTONDOWN / WM_LBUTTONUP
- WM_LBUTTONDOWNメッセージ
- 左ボタンが押下された時に送信される。
- WM_LBUTTONUPメッセージ
- 左ボタンが離された時に送信される。
※注意
WM_LBUTTONDOWNメッセージ と WM_LBUTTONUPメッセージ は、クライアント領域内でのマウス操作を検出する場合に使用する。
| パラメータ | 説明 |
|---|---|
| wParam (nFlags) | ボタンと修飾キーの状態 (MK_LBUTTON、MK_CONTROL、MK_SHIFT等) |
| lParam (point) | マウスカーソルの位置 (クライアント座標) 下位ワード: X座標、上位ワード: Y座標 |
| フラグ | 説明 |
|---|---|
| MK_LBUTTON | 左ボタンが押下されている |
| MK_RBUTTON | 右ボタンが押下されている |
| MK_MBUTTON | 中ボタンが押下されている |
| MK_CONTROL | Ctrlキーが押下されている |
| MK_SHIFT | Shiftキーが押下されている |
WM_RBUTTONDOWN / WM_RBUTTONUP
- WM_RBUTTONDOWNメッセージ
- 右ボタンが押下された時に送信される。
- WM_RBUTTONUPメッセージ
- 右ボタンが離された時に送信される。
右クリックは、通常コンテキストメニューを表示するために使用される。
WM_RBUTTONUPメッセージのハンドラ内で、TrackPopupMenu 関数を呼び出してコンテキストメニューを表示する。
メッセージパラメータは、WM_LBUTTONDOWN / WM_LBUTTONUPと同様である。
WM_MBUTTONDOWN / WM_MBUTTONUP
- WM_MBUTTONDOWNメッセージ
- 中ボタンが押下された時に送信される。
- WM_MBUTTONUPメッセージ
- 中ボタンが離された時に送信される。
中ボタンは、スクロール操作や特殊な機能のトリガーとして使用されることが多い。
例えば、Webブラウザでは、中ボタンクリックで新しいタブでリンクを開く機能が実装されている。
メッセージパラメータは、WM_LBUTTONDOWN / WM_LBUTTONUPと同様である。
WM_MOUSEMOVE
WM_MOUSEMOVEメッセージは、マウスカーソルがクライアント領域内で移動した時に送信される。
このメッセージは、マウスカーソルの移動に伴って高頻度で送信されるため、処理内容は軽量に保つ必要がある。
重い処理を実装すると、アプリケーションの応答性が低下する。
WM_MOUSEMOVEメッセージは、以下の用途で使用される。
- ドラッグ操作の実装
- マウスカーソル追従型のツールチップ表示
- 描画アプリケーションのペン操作
- ホバー効果の実装
メッセージパラメータは、他のマウスメッセージと同様に、wParamにボタン状態、lParamに座標が含まれる。
WM_MOUSEWHEEL
WM_MOUSEWHEELメッセージは、垂直マウスホイールが回転した時に送信される。
| パラメータ | 説明 |
|---|---|
| wParam | 上位ワード : 回転量 (zDelta) 下位ワード : 修飾キーの状態 |
| lParam | マウスカーソルの位置 (スクリーン座標) |
zDelta (回転量) は、WHEEL_DELTA (120) の倍数で表現される。
- 正の値
- ユーザーから見て手前方向 (上方向) への回転
- 負の値
- ユーザーから見て奥方向 (下方向) への回転
スクロール操作を実装する場合、zDeltaの正負を判定して、スクロール方向を決定する。
※注意
WM_MOUSEWHEEL メッセージの lParam は、スクリーン座標で提供されるため、クライアント座標への変換が必要である。
ダブルクリックメッセージ
- WM_LBUTTONDBLCLKメッセージ
- 左ボタンがダブルクリックされた時に送信される。
- WM_RBUTTONDBLCLKメッセージ
- 右ボタンがダブルクリックされた時に送信される。
- WM_MBUTTONDBLCLKメッセージ
- 中ボタンがダブルクリックされた時に送信される。
ダブルクリックメッセージを受信するには、ウィンドウクラスに CS_DBLCLKS スタイルを設定する必要がある。
MFCのビュークラスやダイアログクラスでは、このスタイルがデフォルトで設定されている。
ダブルクリックの時間間隔は、GetDoubleClickTime 関数で取得できる。
デフォルトでは500ミリ秒であり、SetDoubleClickTime 関数で変更できる。
メッセージパラメータは、通常のボタンメッセージと同様である。
非クライアント領域メッセージ
非クライアント領域 (タイトルバー、フレーム、スクロールバー等) でのマウス操作は、別のメッセージとして処理される。
- WM_NCLBUTTONDOWN
- 非クライアント領域で左ボタンが押下された時に送信される。
- WM_NCRBUTTONDOWN
- 非クライアント領域で右ボタンが押下された時に送信される。
- WM_NCMOUSEMOVE
- 非クライアント領域でマウスが移動した時に送信される。
これらのメッセージは、通常システムがデフォルト処理を行うため、カスタム処理が必要な場合のみハンドラを定義する。
メッセージの処理フロー
マウスメッセージの処理フローを以下に示す。
- ユーザがマウスを操作する。
- Windowsは、適切なマウスメッセージを生成する。
- メッセージは、カーソル位置のウィンドウに送信される。
- ウィンドウプロシージャは、メッセージハンドラを呼び出す。
- ハンドラ内で、マウス操作に応じた処理を実行する。
この処理フローを理解することにより、適切なメッセージハンドラを選択することができる。
クリック操作を処理する場合は、WM_LBUTTONUPメッセージを使用する。
ドラッグ操作を処理する場合は、WM_LBUTTONDOWN、WM_MOUSEMOVE、WM_LBUTTONUPの組み合わせを使用する。
マウス位置の取得
マウスの位置を取得するには、複数の方法がある。
| 方法 | 説明 |
|---|---|
| メッセージパラメータ (lParam) | マウスメッセージ内で提供されるクライアント座標 |
| GetCursorPos関数 | 現在のマウスカーソル位置をスクリーン座標で取得 |
| GET_X_LPARAM / GET_Y_LPARAMマクロ | lParamから座標を抽出 |
GetCursorPos関数
GetCursorPos 関数は、現在のマウスカーソル位置をスクリーン座標で取得する関数である。
関数のプロトタイプを以下に示す。
BOOL GetCursorPos(LPPOINT lpPoint);
| 種別 | 項目 | 説明 |
|---|---|---|
| パラメータ | lpPoint | カーソル位置を受け取るPOINT構造体へのポインタ |
| 戻り値 | BOOL | 成功した場合はTRUE、失敗した場合はFALSE |
使用例を以下に示す。
POINT pt;
GetCursorPos(&pt);
CString str;
str.Format(_T("カーソル位置 (スクリーン座標): x=%d, y=%d"), pt.x, pt.y);
AfxMessageBox(str);
GetCursorPos関数は、スクリーン座標を返すため、クライアント座標への変換が必要な場合が多い。
ScreenToClient関数 / ClientToScreen関数
座標系の変換には、ScreenToClient 関数 と ClientToScreen 関数を使用する。
関数のプロトタイプを以下に示す。
BOOL ScreenToClient(HWND hWnd, LPPOINT lpPoint);
BOOL ClientToScreen(HWND hWnd, LPPOINT lpPoint);
| 項目 | 説明 |
|---|---|
| hWnd | 座標変換の基準となるウィンドウのハンドル |
| lpPoint | 変換する座標を持つPOINT構造体へのポインタ |
- ScreenToClient関数
- スクリーン座標をクライアント座標に変換する。
- ClientToScreen関数
- クライアント座標をスクリーン座標に変換する。
使用例を以下に示す。
void CMyView::ShowCursorPosition()
{
// スクリーン座標でカーソル位置を取得
POINT pt;
GetCursorPos(&pt);
// クライアント座標に変換
ScreenToClient(&pt);
CString str;
str.Format(_T("カーソル位置 (クライアント座標): x=%d, y=%d"), pt.x, pt.y);
AfxMessageBox(str);
}
MFCでは、CWndクラスのメンバ関数として ScreenToClient メソッド と ClientToScreen メソッド が提供されている。
POINT pt;
GetCursorPos(&pt);
ScreenToClient(&pt); // CWndのメンバ関数
GET_X_LPARAMマクロ / GET_Y_LPARAMマクロ
lParamから座標を抽出するには、GET_X_LPARAM マクロ と GET_Y_LPARAM マクロを使用する。
これらのマクロは、windowsx.h ヘッダファイルで定義されている。
使用例を以下に示す。
#include <windowsx.h>
LRESULT CMyView::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)
{
if (message == WM_LBUTTONDOWN) {
int xPos = GET_X_LPARAM(lParam);
int yPos = GET_Y_LPARAM(lParam);
CString str;
str.Format(_T("クリック位置: x=%d, y=%d"), xPos, yPos);
AfxMessageBox(str);
}
return CView::WindowProc(message, wParam, lParam);
}
MFCのイベントハンドラでは、CPoint型のパラメータとして座標が提供されるため、これらのマクロは不要である。
void CMyView::OnLButtonDown(UINT nFlags, CPoint point)
{
// pointは既にCPoint型で提供される
CString str;
str.Format(_T("クリック位置: x=%d, y=%d"), point.x, point.y);
AfxMessageBox(str);
CView::OnLButtonDown(nFlags, point);
}
マウスボタン状態の取得
マウスボタンの状態を取得するには、複数の方法がある。
| 方法 | 説明 |
|---|---|
| nFlags (wParam) | マウスメッセージのパラメータから取得 |
| GetAsyncKeyState関数 | リアルタイムのボタン状態を取得 |
nFlagsからのボタン状態取得
マウスメッセージのwParamには、ボタンと修飾キーの状態が含まれる。
ビットフラグを確認することにより、複数のボタンが同時に押下されているかを判定できる。
使用例を以下に示す。
void CMyView::OnMouseMove(UINT nFlags, CPoint point)
{
// 左ボタンが押下されている場合
if (nFlags & MK_LBUTTON) {
// ドラッグ中の描画処理
DrawLine(point);
}
// 左ボタンと右ボタンが同時に押下されている場合
if ((nFlags & MK_LBUTTON) && (nFlags & MK_RBUTTON)) {
// 特殊な操作
ZoomOperation(point);
}
// [Ctrl]キーを押下しながらドラッグしている場合
if ((nFlags & MK_LBUTTON) && (nFlags & MK_CONTROL)) {
// コピー操作
CopyDrag(point);
}
CView::OnMouseMove(nFlags, point);
}
nFlagsによるボタン状態の取得は、マウスメッセージハンドラ内で使用するのに適している。
GetAsyncKeyState関数
GetAsyncKeyState 関数を使用すると、リアルタイムのマウスボタン状態を取得できる。
関数のプロトタイプを以下に示す。
SHORT GetAsyncKeyState(int vKey);
下表に、マウスボタン用の仮想キーコードを示す。
| 定数 | 説明 |
|---|---|
| VK_LBUTTON | 左ボタン |
| VK_RBUTTON | 右ボタン |
| VK_MBUTTON | 中ボタン |
使用例を以下に示す。
void CMyView::OnTimer(UINT_PTR nIDEvent)
{
// タイマ内でマウスボタン状態を確認
if (GetAsyncKeyState(VK_LBUTTON) & 0x8000) {
// 左ボタンが押下されている場合
m_isDragging = TRUE;
}
else {
m_isDragging = FALSE;
}
// 中ボタンでスクロールモード
if (GetAsyncKeyState(VK_MBUTTON) & 0x8000) {
EnableScrollMode();
}
CView::OnTimer(nIDEvent);
}
GetAsyncKeyState関数は、マウスメッセージの外でボタン状態を確認する場合に便利である。
例えば、タイマイベント内や、定期的なポーリング処理で使用する。
MFCでのマウスイベントハンドラ
MFCは、マウスメッセージを処理するための便利なメッセージハンドラを提供している。
| メソッド | 説明 |
|---|---|
| OnLButtonDown OnLButtonUp |
WM_LBUTTONDOWN / WM_LBUTTONUPメッセージの処理 |
| OnRButtonDown OnRButtonUp |
WM_RBUTTONDOWN / WM_RBUTTONUPメッセージの処理 |
| OnMButtonDown OnMButtonUp |
WM_MBUTTONDOWN / WM_MBUTTONUPメッセージの処理 |
| OnMouseMove | WM_MOUSEMOVEメッセージの処理 |
| OnMouseWheel | WM_MOUSEWHEELメッセージの処理 |
| OnLButtonDblClk OnRButtonDblClk |
ダブルクリックメッセージの処理 |
| OnSetCursor | WM_SETCURSORメッセージの処理 |
| PreTranslateMessage | メッセージの前処理 |
OnLButtonDown / OnLButtonUp
OnLButtonDown イベントハンドラ と OnLButtonUp イベントハンドラ は、左ボタンの押下と解放を処理するハンドラである。
関数のプロトタイプを以下に示す。
afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
afx_msg void OnLButtonUp(UINT nFlags, CPoint point);
| 項目 | 説明 |
|---|---|
| nFlags | ボタンと修飾キーの状態 (MK_CONTROL、MK_SHIFT等) |
| point | マウスカーソルの位置 (クライアント座標) |
OnLButtonDownハンドラ と OnLButtonUpハンドラを使用することにより、クリック操作やドラッグ操作を実装できる。
使用例を以下に示す。
void CMyView::OnLButtonDown(UINT nFlags, CPoint point)
{
// ドラッグ開始
m_bDragging = TRUE;
m_ptStart = point;
// マウスキャプチャを開始
SetCapture();
CView::OnLButtonDown(nFlags, point);
}
void CMyView::OnLButtonUp(UINT nFlags, CPoint point)
{
if (m_bDragging) {
// ドラッグ終了
m_bDragging = FALSE;
// マウスキャプチャを解放
ReleaseCapture();
// ドラッグ完了処理
CompleteDrag(m_ptStart, point);
}
CView::OnLButtonUp(nFlags, point);
}
右ボタン (OnRButtonDown / OnRButtonUp) と 中ボタン (OnMButtonDown / OnMButtonUp) も同様のシグネチャを持つ。
OnMouseMove
OnMouseMove イベントハンドラは、マウス移動を処理するハンドラである。
関数のプロトタイプを以下に示す。
afx_msg void OnMouseMove(UINT nFlags, CPoint point);
| 項目 | 説明 |
|---|---|
| nFlags | ボタンと修飾キーの状態 |
| point | マウスカーソルの位置 (クライアント座標) |
OnMouseMoveハンドラは、ドラッグ操作や描画操作を実装する場合に使用する。
使用例を以下に示す。
void CMyView::OnMouseMove(UINT nFlags, CPoint point)
{
// ドラッグ中の場合
if (m_bDragging && (nFlags & MK_LBUTTON)) {
// 線を描画
CClientDC dc(this);
dc.MoveTo(m_ptPrev);
dc.LineTo(point);
m_ptPrev = point;
}
// カーソル位置を表示
CString str;
str.Format(_T("X: %d, Y: %d"), point.x, point.y);
GetParent()->SetWindowText(str);
CView::OnMouseMove(nFlags, point);
}
※注意
OnMouseMoveは高頻度で呼び出されるため、処理は軽量に保つ必要がある。
重い処理を実装すると、マウス追従が遅延し、ユーザ体験が低下する。
OnMouseWheel
OnMouseWheel イベントハンドラは、マウスホイール回転を処理するハンドラである。
関数のプロトタイプを以下に示す。
afx_msg BOOL OnMouseWheel(UINT nFlags, short zDelta, CPoint pt);
| 項目 | 説明 |
|---|---|
| nFlags | 修飾キーの状態 (MK_CONTROL、MK_SHIFT等) |
| zDelta | ホイールの回転量 正の値: 手前方向 (上方向)、負の値: 奥方向 (下方向) |
| pt | マウスカーソルの位置 (スクリーン座標) |
zDeltaは、WHEEL_DELTA (120) の倍数で提供される。
通常のスクロールでは、1ノッチ (1クリック) で120の値が渡される。
使用例を以下に示す。
BOOL CMyView::OnMouseWheel(UINT nFlags, short zDelta, CPoint pt)
{
// スクリーン座標をクライアント座標に変換
ScreenToClient(&pt);
// ホイールの回転量に応じてスクロール
int scrollLines = zDelta / WHEEL_DELTA;
if (zDelta > 0) {
// 上にスクロール
ScrollUp(scrollLines);
}
else {
// 下にスクロール
ScrollDown(abs(scrollLines));
}
// [Ctrl]キーを押下しながらホイール回転でズーム
if (nFlags & MK_CONTROL) {
if (zDelta > 0) {
ZoomIn();
}
else {
ZoomOut();
}
}
return CView::OnMouseWheel(nFlags, zDelta, pt);
}
OnSetCursor
OnSetCursor イベントハンドラは、マウスカーソルの形状を変更するハンドラである。
関数のプロトタイプを以下に示す。
afx_msg BOOL OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message);
| 項目 | 説明 |
|---|---|
| pWnd | カーソルを含むウィンドウへのポインタ |
| nHitTest | ヒットテストコード (HTCLIENT等) |
| message | マウスメッセージ |
OnSetCursorハンドラは、マウスカーソルの形状をカスタマイズする場合に使用する。
使用例を以下に示す。
BOOL CMyView::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message)
{
// クライアント領域内の場合
if (nHitTest == HTCLIENT) {
// 編集モードの場合は十字カーソル
if (m_bEditMode) {
SetCursor(LoadCursor(NULL, IDC_CROSS));
return TRUE;
}
// リサイズモードの場合はサイズ変更カーソル
if (m_bResizeMode) {
SetCursor(LoadCursor(NULL, IDC_SIZEALL));
return TRUE;
}
}
return CView::OnSetCursor(pWnd, nHitTest, message);
}
OnSetCursorハンドラがTRUEを返す場合、カーソルの変更を処理済みとして扱う。
FALSEを返す場合、デフォルトのカーソル処理が実行される。
PreTranslateMessageメソッド
PreTranslateMessage メソッドは、メッセージがディスパッチされる前に呼び出されるメソッドである。
このメソッドをオーバーライドすることにより、メッセージの前処理やフィルタリングを実装できる。
関数のプロトタイプを以下に示す。
virtual BOOL PreTranslateMessage(MSG* pMsg);
| 種別 | 項目 | 説明 |
|---|---|---|
| パラメータ | pMsg | メッセージ構造体へのポインタ |
| 戻り値 | TRUE | メッセージを処理済み (DispatchMessageに渡さない) |
| FALSE | メッセージを通常通り処理 (DispatchMessageに渡す) |
使用例を以下に示す。
BOOL CMyView::PreTranslateMessage(MSG* pMsg)
{
// 右クリックを無効化
if (pMsg->message == WM_RBUTTONDOWN) {
return TRUE; // メッセージを消費
}
// マウスホイールの処理をカスタマイズ
if (pMsg->message == WM_MOUSEWHEEL) {
// カスタム処理
CustomWheelHandler(pMsg);
return TRUE;
}
return CView::PreTranslateMessage(pMsg);
}
PreTranslateMessageメソッドは、マウス操作のデフォルト動作を変更する場合に有用である。
マウスカーソル
マウスカーソルの形状を制御することにより、ユーザに視覚的なフィードバックを提供できる。
| 方法 | 説明 |
|---|---|
| SetCursor関数 | カーソルを即座に変更 |
| LoadCursor関数 | システムカーソルまたはカスタムカーソルをロード |
| OnSetCursorハンドラ | WM_SETCURSORメッセージで動的にカーソルを変更 |
システム定義カーソル
Windowsは、複数の標準カーソルを提供している。
| 定数 | 説明 |
|---|---|
| IDC_ARROW | 標準の矢印カーソル |
| IDC_IBEAM | テキスト選択用のIビームカーソル |
| IDC_WAIT | 待機中を示す砂時計カーソル |
| IDC_CROSS | 十字カーソル |
| IDC_HAND | ハンドカーソル (リンクの上等) |
| IDC_SIZEALL | 4方向サイズ変更カーソル |
| IDC_SIZENESW | 右上/左下方向サイズ変更カーソル |
| IDC_SIZENS | 垂直方向サイズ変更カーソル |
| IDC_SIZENWSE | 左上/右下方向サイズ変更カーソル |
| IDC_SIZEWE | 水平方向サイズ変更カーソル |
| IDC_NO | 禁止カーソル |
| IDC_APPSTARTING | 作業中カーソル (矢印+砂時計) |
使用例を以下に示す。
void CMyView::ChangeCursor(int cursorType)
{
HCURSOR hCursor = NULL;
switch (cursorType) {
case 0:
hCursor = LoadCursor(NULL, IDC_ARROW);
break;
case 1:
hCursor = LoadCursor(NULL, IDC_CROSS);
break;
case 2:
hCursor = LoadCursor(NULL, IDC_HAND);
break;
case 3:
hCursor = LoadCursor(NULL, IDC_WAIT);
break;
}
if (hCursor != NULL) {
SetCursor(hCursor);
}
}
カスタムカーソル
カスタムカーソルをリソースファイルから読み込む方法を以下に示す。
// カスタムカーソルをロード (IDC_MYCURSORはリソースID)
HCURSOR hCustomCursor = LoadCursor(AfxGetInstanceHandle(),
MAKEINTRESOURCE(IDC_MYCURSOR));
if (hCustomCursor != NULL) {
SetCursor(hCustomCursor);
}
カーソルファイル (.cur) から直接ロードする方法を以下に示す。
HCURSOR hCursor = LoadCursorFromFile(_T("C:\\Cursors\\mycursor.cur"));
if (hCursor != NULL) {
SetCursor(hCursor);
}
WM_SETCURSORメッセージ
WM_SETCURSORメッセージは、マウスカーソルの形状を変更する必要がある時に送信される。
OnSetCursorハンドラをオーバーライドすることにより、動的にカーソルを変更できる。
使用例を以下に示す。
BOOL CMyView::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message)
{
if (nHitTest == HTCLIENT) {
CPoint pt;
GetCursorPos(&pt);
ScreenToClient(&pt);
// 特定の領域の上にカーソルがある場合
if (m_hotRect.PtInRect(pt)) {
SetCursor(LoadCursor(NULL, IDC_HAND));
return TRUE;
}
}
return CView::OnSetCursor(pWnd, nHitTest, message);
}
待機カーソルの表示
MFCは、待機カーソルを簡単に表示するためのメソッドを提供している。
- BeginWaitCursor
- 待機カーソル (砂時計) を表示する。
- EndWaitCursor
- 待機カーソルを元に戻す。
使用例を以下に示す。
void CMyView::LongOperation()
{
BeginWaitCursor(); // 待機カーソル表示
// 長時間かかる処理
for (int i = 0; i < 10000; i++) {
ProcessData(i);
}
EndWaitCursor(); // 元のカーソルに戻す
}
CWaitCursorクラスを使用すると、スコープベースで自動的にカーソルを管理できる。
void CMyView::LongOperation()
{
CWaitCursor wait; // コンストラクタで待機カーソル表示
// 長時間かかる処理
ProcessData();
// デストラクタで自動的に元のカーソルに戻る
}
マウスキャプチャ
マウスキャプチャは、特定のウィンドウが全てのマウス入力を受け取る機能である。
キャプチャを開始すると、マウスカーソルがウィンドウの外に出ても、そのウィンドウがマウスメッセージを受信し続ける。
この機能は、ドラッグ操作の実装に不可欠である。
| 関数 | 説明 |
|---|---|
| SetCapture | マウスキャプチャを開始 |
| ReleaseCapture | マウスキャプチャを解放 |
| GetCapture | 現在キャプチャしているウィンドウを取得 |
SetCapture / ReleaseCapture / GetCapture
関数のプロトタイプを以下に示す。
HWND SetCapture(HWND hWnd);
BOOL ReleaseCapture(void);
HWND GetCapture(void);
- SetCapture
- マウスキャプチャを開始する。
- すべてのマウス入力が指定されたウィンドウに送信されるようになる。
- ReleaseCapture
- マウスキャプチャを解放する。
- GetCapture
- 現在マウスキャプチャを行っているウィンドウのハンドルを返す。
MFCでは、CWndクラスのメンバ関数として提供されている。
CWnd* SetCapture();
static BOOL PASCAL ReleaseCapture();
static CWnd* PASCAL GetCapture();
ドラッグ操作の実装例
マウスキャプチャを使用したドラッグ操作の実装例を以下に示す。
class CMyView : public CView
{
protected:
BOOL m_bDragging; // ドラッグ中かどうか
CPoint m_ptStart; // ドラッグ開始位置
CPoint m_ptCurrent; // 現在の位置
public:
CMyView() : m_bDragging(FALSE) {}
afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
afx_msg void OnLButtonUp(UINT nFlags, CPoint point);
afx_msg void OnMouseMove(UINT nFlags, CPoint point);
DECLARE_MESSAGE_MAP()
};
void CMyView::OnLButtonDown(UINT nFlags, CPoint point)
{
// ドラッグ開始
m_bDragging = TRUE;
m_ptStart = point;
m_ptCurrent = point;
// マウスキャプチャを開始
SetCapture();
CView::OnLButtonDown(nFlags, point);
}
void CMyView::OnMouseMove(UINT nFlags, CPoint point)
{
if (m_bDragging) {
// ドラッグ中の処理
CClientDC dc(this);
// 前回の矩形を消去 (XORモード)
dc.SetROP2(R2_NOTXORPEN);
dc.Rectangle(m_ptStart.x, m_ptStart.y,
m_ptCurrent.x, m_ptCurrent.y);
// 新しい矩形を描画
m_ptCurrent = point;
dc.Rectangle(m_ptStart.x, m_ptStart.y,
m_ptCurrent.x, m_ptCurrent.y);
}
CView::OnMouseMove(nFlags, point);
}
void CMyView::OnLButtonUp(UINT nFlags, CPoint point)
{
if (m_bDragging) {
// ドラッグ終了
m_bDragging = FALSE;
// マウスキャプチャを解放
ReleaseCapture();
// 最終的な矩形を確定
CRect rect(m_ptStart, point);
rect.NormalizeRect();
// 矩形を描画
DrawFinalRectangle(rect);
}
CView::OnLButtonUp(nFlags, point);
}
※注意
マウスキャプチャを開始した場合、必ずReleaseCapture関数で解放する必要がある。
解放しないと、他のウィンドウがマウス入力を受け取れなくなる。
マウスキャプチャは、以下の用途で使用される。
- ドラッグアンドドロップ操作
- リサイズ操作
- 描画ツールでのペン操作
- スライダーやスクロールバーのドラッグ
マウストラッキング
マウストラッキングは、マウスカーソルがウィンドウに入った時や出た時、ホバーした時を検出する機能である。
TrackMouseEvent 関数を使用して、WM_MOUSELEAVE と WM_MOUSEHOVER メッセージを受信できる。
TrackMouseEvent関数
TrackMouseEvent関数は、マウストラッキングを開始する関数である。
関数のプロトタイプを以下に示す。
BOOL TrackMouseEvent(LPTRACKMOUSEEVENT lpEventTrack);
TRACKMOUSEEVENT構造体を以下に示す。
typedef struct tagTRACKMOUSEEVENT {
DWORD cbSize;
DWORD dwFlags;
HWND hwndTrack;
DWORD dwHoverTime;
} TRACKMOUSEEVENT, *LPTRACKMOUSEEVENT;
| メンバ | 説明 |
|---|---|
| cbSize | 構造体のサイズ |
| dwFlags | トラッキングフラグ (TME_LEAVE、TME_HOVER等) |
| hwndTrack | トラッキング対象のウィンドウハンドル |
| dwHoverTime | ホバーまでの時間 (ミリ秒) |
| フラグ | 説明 |
|---|---|
| TME_LEAVE | マウスがウィンドウを離れた時にWM_MOUSELEAVEを送信 |
| TME_HOVER | マウスがホバーした時にWM_MOUSEHOVERを送信 |
| TME_CANCEL | トラッキングをキャンセル |
WM_MOUSELEAVE / WM_MOUSEHOVER
- WM_MOUSELEAVEメッセージ
- マウスカーソルがウィンドウのクライアント領域から出た時に送信される。
- WM_MOUSEHOVERメッセージ
- マウスカーソルが指定時間静止した時に送信される。
使用例を以下に示す。
class CMyView : public CView
{
protected:
BOOL m_bTracking; // トラッキング中かどうか
public:
CMyView() : m_bTracking(FALSE) {}
afx_msg void OnMouseMove(UINT nFlags, CPoint point);
afx_msg LRESULT OnMouseLeave(WPARAM wParam, LPARAM lParam);
afx_msg LRESULT OnMouseHover(WPARAM wParam, LPARAM lParam);
DECLARE_MESSAGE_MAP()
};
BEGIN_MESSAGE_MAP(CMyView, CView)
ON_WM_MOUSEMOVE()
ON_MESSAGE(WM_MOUSELEAVE, OnMouseLeave)
ON_MESSAGE(WM_MOUSEHOVER, OnMouseHover)
END_MESSAGE_MAP()
void CMyView::OnMouseMove(UINT nFlags, CPoint point)
{
if (!m_bTracking) {
// トラッキングを開始
TRACKMOUSEEVENT tme;
tme.cbSize = sizeof(TRACKMOUSEEVENT);
tme.dwFlags = TME_LEAVE | TME_HOVER;
tme.hwndTrack = m_hWnd;
tme.dwHoverTime = 1000; // 1秒
if (TrackMouseEvent(&tme)) {
m_bTracking = TRUE;
}
}
CView::OnMouseMove(nFlags, point);
}
LRESULT CMyView::OnMouseLeave(WPARAM wParam, LPARAM lParam)
{
// マウスがウィンドウを離れた
m_bTracking = FALSE;
// ホバー効果を解除
ClearHoverEffect();
return 0;
}
LRESULT CMyView::OnMouseHover(WPARAM wParam, LPARAM lParam)
{
// マウスがホバーした
// ツールチップやホバー効果を表示
ShowTooltip();
// 再度ホバーメッセージを受け取るには、TrackMouseEventを再度呼び出す
m_bTracking = FALSE;
return 0;
}
マウストラッキングは、以下の用途で使用される。
- ホバー効果の実装 (ボタンの色変更等)
- ツールチップの表示
- マウスがウィンドウを離れた時のクリーンアップ処理
- カスタムコントロールの視覚フィードバック
マウス入力のシミュレーション
プログラムから仮想的にマウス入力を生成する技術を、マウス入力のシミュレーションという。
Windowsでは、SendInput 関数 と mouse_event 関数の2つの方法がある。
- SendInput関数
- Windows 2000以降で推奨される方法である。
- mouse_event関数
- レガシーな方法であり、互換性のために残されている。
SendInput関数
SendInput関数は、キーボードやマウスの入力イベントを合成する関数である。
関数のプロトタイプを以下に示す。
UINT SendInput(
UINT nInputs,
LPINPUT pInputs,
int cbSize
);
| 項目 | 説明 |
|---|---|
| nInputs | 入力イベントの数 |
| pInputs | INPUT構造体の配列 |
| cbSize | INPUT構造体のサイズ |
INPUT構造体とMOUSEINPUT構造体を以下に示す。
typedef struct tagINPUT {
DWORD type; // INPUT_MOUSE、INPUT_KEYBOARD等
union {
MOUSEINPUT mi;
KEYBDINPUT ki;
HARDWAREINPUT hi;
};
} INPUT, *PINPUT;
typedef struct tagMOUSEINPUT {
LONG dx;
LONG dy;
DWORD mouseData;
DWORD dwFlags;
DWORD time;
ULONG_PTR dwExtraInfo;
} MOUSEINPUT, *PMOUSEINPUT;
| メンバ | 説明 |
|---|---|
| dx | X方向の移動量 (または絶対座標) |
| dy | Y方向の移動量 (または絶対座標) |
| mouseData | ホイール回転量やボタンデータ |
| dwFlags | イベントフラグ (MOUSEEVENTF_LEFTDOWN等) |
| time | タイムスタンプ (0で自動設定) |
| dwExtraInfo | 追加情報 |
| フラグ | 説明 |
|---|---|
| MOUSEEVENTF_MOVE | マウス移動 |
| MOUSEEVENTF_LEFTDOWN | 左ボタン押下 |
| MOUSEEVENTF_LEFTUP | 左ボタン解放 |
| MOUSEEVENTF_RIGHTDOWN | 右ボタン押下 |
| MOUSEEVENTF_RIGHTUP | 右ボタン解放 |
| MOUSEEVENTF_MIDDLEDOWN | 中ボタン押下 |
| MOUSEEVENTF_MIDDLEUP | 中ボタン解放 |
| MOUSEEVENTF_WHEEL | 垂直ホイール回転 |
| MOUSEEVENTF_HWHEEL | 水平ホイール回転 |
| MOUSEEVENTF_ABSOLUTE | dx, dyを絶対座標として扱う |
使用例を以下に示す。
void SendMouseClick(int x, int y)
{
INPUT inputs[3] = {};
// マウス移動
inputs[0].type = INPUT_MOUSE;
inputs[0].mi.dx = x * (65535 / GetSystemMetrics(SM_CXSCREEN));
inputs[0].mi.dy = y * (65535 / GetSystemMetrics(SM_CYSCREEN));
inputs[0].mi.dwFlags = MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE;
// 左ボタン押下
inputs[1].type = INPUT_MOUSE;
inputs[1].mi.dwFlags = MOUSEEVENTF_LEFTDOWN;
// 左ボタン解放
inputs[2].type = INPUT_MOUSE;
inputs[2].mi.dwFlags = MOUSEEVENTF_LEFTUP;
// 入力イベントの送信
UINT uSent = SendInput(3, inputs, sizeof(INPUT));
if (uSent != 3) {
AfxMessageBox(_T("SendInputに失敗しました"));
}
}
マウスホイール回転をシミュレートする例を以下に示す。
void SendMouseWheelRotation(int delta)
{
INPUT input = {};
input.type = INPUT_MOUSE;
input.mi.mouseData = delta; // 通常はWHEEL_DELTA (120) の倍数
input.mi.dwFlags = MOUSEEVENTF_WHEEL;
SendInput(1, &input, sizeof(INPUT));
}
// 使用例
void CMyView::SimulateWheelUp()
{
SendMouseWheelRotation(WHEEL_DELTA); // 上方向に1ノッチ
}
mouse_event関数
mouse_event関数は、レガシーなマウス入力シミュレーション関数である。
関数のプロトタイプを以下に示す。
VOID mouse_event(
DWORD dwFlags,
DWORD dx,
DWORD dy,
DWORD dwData,
ULONG_PTR dwExtraInfo
);
| 項目 | 説明 |
|---|---|
| dwFlags | イベントフラグ (MOUSEEVENTF_LEFTDOWN等) |
| dx | X方向の移動量 (または絶対座標) |
| dy | Y方向の移動量 (または絶対座標) |
| dwData | ホイール回転量やボタンデータ |
| dwExtraInfo | 追加情報 (通常は0) |
使用例を以下に示す。
void SendMouseClickLegacy(int x, int y)
{
// マウス移動
mouse_event(MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE,
x * (65535 / GetSystemMetrics(SM_CXSCREEN)),
y * (65535 / GetSystemMetrics(SM_CYSCREEN)),
0, 0);
// 左ボタン押下
mouse_event(MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0);
// 左ボタン解放
mouse_event(MOUSEEVENTF_LEFTUP, 0, 0, 0, 0);
}
※注意
mouse_event関数は、Windows 2000以降では非推奨とされている。
新規開発では、SendInput関数の使用を推奨する。
マウス入力のシミュレーションは、自動化ツール、テストツール、マクロツール、リモート操作ツールなどの実装に利用される。
ただし、セキュリティ上の理由から、使用には注意が必要である。
関連リンク
- Microsoft Docs - Mouse Input
- Microsoft Docs - SendInput function
- Microsoft Docs - TrackMouseEvent function
- Microsoft Docs - SetCapture function