トップ回答者
四角で囲まれたエリア内でのみマウスの位置を取得したい。

質問
-
画面のようなダイアログを表示し、マウスで左クリックしてみると、四角で囲まれたエリアのサイズでダイアログの右上の端を(0,0)としてマウスを取り込んでしまう。四角で囲まれたエリア内でのみマウスの位置を取得したい。
void CJyusinDataKakuninGld::OnLButtonDown(UINT nFlags, CPoint point)
{
// TODO: ここにメッセージ ハンドラー コードを追加するか、既定の処理を呼び出します。
CWnd* h = GetDlgItem(IDC_LINE_DISP);
CDC* pDC = h->GetDC();
CRect rect;
h->GetClientRect(&rect);
if (rect.PtInRect(point))
{
CPen p(PS_SOLID, 1, RGB(0, 255, 0));
CPen* oldp = pDC->SelectObject(&p);
CString ss;
ss.Format(_T("%04d,%04d"), point.x, point.y);
CRect rec(0, 0, 600, 600);
pDC->DrawText(ss, rec, DT_RIGHT | DT_VCENTER);
pDC->SelectObject(oldp);
h->ReleaseDC(pDC);
//SetCapture();
}
else
{
//ReleaseCapture();
}CDialogEx::OnLButtonDown(nFlags, point);
}--------------------------------------
Windows10
VS2017 Pro
C++ MFC
回答
-
一旦スクリーン座標に変換して、そこからコントロールの座標に変換するのはどうでしょうか?
ClientToScreen(&point); h->ScreenToClient(&point);
参考サイト:
CWnd::ClientToScreen https://msdn.microsoft.com/ja-jp/library/92hfya22.aspx
CWnd::ScreenToClient https://msdn.microsoft.com/ja-jp/library/7e1e9a57.aspx- 編集済み kenjinoteMVP 2017年6月23日 1:58
- 回答の候補に設定 立花楓Microsoft employee, Moderator 2017年6月23日 4:18
- 回答としてマーク saiking 2017年6月23日 4:25
-
まず、
「ダイアログの右上の端を(0,0)としてマウスを取り込んでしまう」ということはないと考えます。
もしそのようになっているのであれば、本件の質問内容については自明のはずではないか、と断定できるからです。
で、「左上の端を(0,0)」の誤りであると仮定します。一般に、マウスの座標は本質的にはデスクトップの座標としてシステムが保持していますが、
WM_MOUSEMOVE等のメッセージをウインドウに報告する場合、そのウインドウのクライアント領域の座標に変換されます。
この時の原点がクライアント領域の左上となります。さて、マウスの座標を変換する方法は数種類ありますが、
本件の対象の四角形が、DCに対しての描画の場合、マウス位置の座標系と、DCの座標系が一致している方が都合が良いと考えられます。
すなわち、CPoint::Offset()を使って、描画している「四角形の左上の座標」を減算するのが、もっとも妥当な手段であると考えられます。- 回答の候補に設定 立花楓Microsoft employee, Moderator 2017年6月23日 4:19
- 回答としてマーク saiking 2017年6月23日 4:25
-
OnLButtonDown の最後に CDialogEx::OnLButtonDown(nFlags, point); とあるので、ダイアログウィンドウ上でマウスメッセージを受け取っているとお見受けします。
この場合、マウスメッセージは「受け取ったウィンドウのクライアント座標」で取得されます。
そのため仲澤さんから指摘があるようにダイアログの「左上」を原点(0,0)とする、座標で取得されます。
これを特定のコントロール(画像からスタティックコントロールのフレームモードと見受ける)の座標とする場合、いくつかパターンがあります。
- kenjinoteさんから提案のある ClientToScreen -> ScreenToClient の組み合わせ
- 仲澤さんから提案のある コントロールの原点座標をオフセットとして取り出し移動する
- コントロールでマウスメッセージを受け取る
ということで、ここではあえて別の手段となるコントロールで受け取る方法を提示しておきます。
まず、スタティックコントロールの場合、そのままではマウスメッセージを受け取ることができません。
これは、コントロールがマウスメッセージを受け取らないように実装されているためです。
具体的には、WM_NCHITTEST でHTTRANSPARENT を返すことで自身がマウス通知を受け取らないという処理をしています。
なので、具体的な実装としては、
- CStatic の派生クラスを作り、OnNcHitTest と処理したいマウスメッセージハンドラを実装する(OnLButtonDownなど)
- OnNcHitTest で受け取った座標からコントロールのどの位置を表すかをきちんと返す(return HTCLIENTがないとマウスメッセージは受け取れない)
- 必要があれば、OnSetCursor なども実装する
- コントロールでマウスメッセージを処理する
という形になります。
コントロール内で完結する処理なので、コントロール側に実装を持たせてしまうというパターンになります。
とっちゃん@わんくま同盟, Visual Studio and Development Technologies http://blogs.wankuma.com/tocchann/default.aspx
- 回答の候補に設定 立花楓Microsoft employee, Moderator 2017年6月23日 4:19
- 回答としてマーク saiking 2017年6月23日 4:24
-
OnLButtonDownで送られてくるマウス座標はそのウィンドウのクライアント座標です。
なので、まずは、this->ClientToScreen( &point ); でスクリーン座標に変換し、それから h->ScreenToClient( &point ); で該当するウィンドウの座標に変換します。
とっちゃん@わんくま同盟, Visual Studio and Development Technologies http://blogs.wankuma.com/tocchann/default.aspx
- 回答の候補に設定 立花楓Microsoft employee, Moderator 2017年6月23日 4:18
- 回答としてマーク saiking 2017年6月23日 4:24
すべての返信
-
一旦スクリーン座標に変換して、そこからコントロールの座標に変換するのはどうでしょうか?
ClientToScreen(&point); h->ScreenToClient(&point);
参考サイト:
CWnd::ClientToScreen https://msdn.microsoft.com/ja-jp/library/92hfya22.aspx
CWnd::ScreenToClient https://msdn.microsoft.com/ja-jp/library/7e1e9a57.aspx- 編集済み kenjinoteMVP 2017年6月23日 1:58
- 回答の候補に設定 立花楓Microsoft employee, Moderator 2017年6月23日 4:18
- 回答としてマーク saiking 2017年6月23日 4:25
-
まず、
「ダイアログの右上の端を(0,0)としてマウスを取り込んでしまう」ということはないと考えます。
もしそのようになっているのであれば、本件の質問内容については自明のはずではないか、と断定できるからです。
で、「左上の端を(0,0)」の誤りであると仮定します。一般に、マウスの座標は本質的にはデスクトップの座標としてシステムが保持していますが、
WM_MOUSEMOVE等のメッセージをウインドウに報告する場合、そのウインドウのクライアント領域の座標に変換されます。
この時の原点がクライアント領域の左上となります。さて、マウスの座標を変換する方法は数種類ありますが、
本件の対象の四角形が、DCに対しての描画の場合、マウス位置の座標系と、DCの座標系が一致している方が都合が良いと考えられます。
すなわち、CPoint::Offset()を使って、描画している「四角形の左上の座標」を減算するのが、もっとも妥当な手段であると考えられます。- 回答の候補に設定 立花楓Microsoft employee, Moderator 2017年6月23日 4:19
- 回答としてマーク saiking 2017年6月23日 4:25
-
OnLButtonDown の最後に CDialogEx::OnLButtonDown(nFlags, point); とあるので、ダイアログウィンドウ上でマウスメッセージを受け取っているとお見受けします。
この場合、マウスメッセージは「受け取ったウィンドウのクライアント座標」で取得されます。
そのため仲澤さんから指摘があるようにダイアログの「左上」を原点(0,0)とする、座標で取得されます。
これを特定のコントロール(画像からスタティックコントロールのフレームモードと見受ける)の座標とする場合、いくつかパターンがあります。
- kenjinoteさんから提案のある ClientToScreen -> ScreenToClient の組み合わせ
- 仲澤さんから提案のある コントロールの原点座標をオフセットとして取り出し移動する
- コントロールでマウスメッセージを受け取る
ということで、ここではあえて別の手段となるコントロールで受け取る方法を提示しておきます。
まず、スタティックコントロールの場合、そのままではマウスメッセージを受け取ることができません。
これは、コントロールがマウスメッセージを受け取らないように実装されているためです。
具体的には、WM_NCHITTEST でHTTRANSPARENT を返すことで自身がマウス通知を受け取らないという処理をしています。
なので、具体的な実装としては、
- CStatic の派生クラスを作り、OnNcHitTest と処理したいマウスメッセージハンドラを実装する(OnLButtonDownなど)
- OnNcHitTest で受け取った座標からコントロールのどの位置を表すかをきちんと返す(return HTCLIENTがないとマウスメッセージは受け取れない)
- 必要があれば、OnSetCursor なども実装する
- コントロールでマウスメッセージを処理する
という形になります。
コントロール内で完結する処理なので、コントロール側に実装を持たせてしまうというパターンになります。
とっちゃん@わんくま同盟, Visual Studio and Development Technologies http://blogs.wankuma.com/tocchann/default.aspx
- 回答の候補に設定 立花楓Microsoft employee, Moderator 2017年6月23日 4:19
- 回答としてマーク saiking 2017年6月23日 4:24
-
OnLButtonDownで送られてくるマウス座標はそのウィンドウのクライアント座標です。
なので、まずは、this->ClientToScreen( &point ); でスクリーン座標に変換し、それから h->ScreenToClient( &point ); で該当するウィンドウの座標に変換します。
とっちゃん@わんくま同盟, Visual Studio and Development Technologies http://blogs.wankuma.com/tocchann/default.aspx
- 回答の候補に設定 立花楓Microsoft employee, Moderator 2017年6月23日 4:18
- 回答としてマーク saiking 2017年6月23日 4:24
-
資料ですか。。。見てないので実際のところどんなものかわかりませんが C++ による Windows プログラミングの学習 とページが MSDN ライブラリにありました。
網羅的に扱っているかどうかはわかりませんが、概要のところのウィンドウとはの部分に座標系のことがちらっと書かれているので、一通り目を通しておくといいかもしれません。
ほかにも探せばいろいろあるとは思いますが、HWNDな世界向けの書籍は出なくなって10年という単位が過ぎていますので、書籍を見て体系立ててというのはかなり厳しいと思います。
とっちゃん@わんくま同盟, Visual Studio and Development Technologies http://blogs.wankuma.com/tocchann/default.aspx