トップ回答者
MFC ダイアログ上のコントロールをドラッグ操作で動かしたい

質問
-
お世話になります。VC++初心者のarubi_momoと申します。
現在、VisualStudio2010 MFCアプリケーションで開発中です。
ダイアログ上のスタティックコントロール(PictureControl)を
ドラッグ操作で動かしたいと考えています。
そこで、CStaticの派生クラスCStaticExを作成し、
LBUTTONDOWN処理でコントロールがクリックされたことをダイアログに通知し、
ダイアログ側のMOUSEMOVEでコントロールを動かせるようにしようとしましたが、
ダイアログへどのように通知したらよいのかがわかりません。
CStaticEx上に親ダイアログの変数を宣言するとエラーになってしまったため、
アプリケーションのpublicで宣言してみたのですが、
CStaticEx上で認識されませんでした。(定義されていないとエラーが出る)
ちなみにincludeはしています。
そもそもやり方が違うのでしょうか。
ダイアログへの通知方法など、ご存知の方がいらっしゃいましたら、よろしくお願いいたします。
回答
-
まず、「初心者」かどうかはおいといて、
メンバー変数の定義宣言でとまどっているようでは先は長そうです。
それを自覚していただいた上で、必要となる知識を一覧しておきます。1.WM_MOUSEMOVEを連続的に受け取るには、マウスをキャプチャーすることが必要です。
SetCapture()を使います。2.ウインドウは自身を動かすために他からの働きかけは無用で、
自身に対するSetWindowPos()で動かせます。
つまり、本質的には、自身を移動するために、
親に連絡を取る必要はまったくありません。ただし、親を知っておく必要はあります。3.基本的な座標系についての知識が必要です。
WM_MOUSEMOVEでの位置は自身のクライアント座標系です。
SetWindowPos()で指定する位置は、親のクライアント座標系です。4.あるウインドウのクライアント座標系での位置を別のウインドウの
クライアント座標系に変換するには、一旦スクリーン座標系に
変換する必要があり、ClientToScreen()を使います。
逆の変換にはScreenToClient()を使用します。で、移動させる手順ですが、
A.対象StaticはWM_LBUTTONDOWNでマウスキャプチャーを含む
移動開始処理を実行する。
B.WM_MOUSEMOVEで自身に対する移動処理を行う。
C.WM_LBUTTONUPで、移動完了処理を行う。その他、
X.移動中にキャプチャーが外れた場合はどうするかをきめる必要があります。
WM_CAPTURECHANGEDで、そのストラテジを実装する。てな感じになると思います。
任意のウインドウの矩形をスクリーン座標で取得するにはGetWindowRect()を使います。- 編集済み 仲澤@失業者 2013年5月29日 4:49
- 回答としてマーク arubi_momo 2013年5月29日 5:09
すべての返信
-
まず、「初心者」かどうかはおいといて、
メンバー変数の定義宣言でとまどっているようでは先は長そうです。
それを自覚していただいた上で、必要となる知識を一覧しておきます。1.WM_MOUSEMOVEを連続的に受け取るには、マウスをキャプチャーすることが必要です。
SetCapture()を使います。2.ウインドウは自身を動かすために他からの働きかけは無用で、
自身に対するSetWindowPos()で動かせます。
つまり、本質的には、自身を移動するために、
親に連絡を取る必要はまったくありません。ただし、親を知っておく必要はあります。3.基本的な座標系についての知識が必要です。
WM_MOUSEMOVEでの位置は自身のクライアント座標系です。
SetWindowPos()で指定する位置は、親のクライアント座標系です。4.あるウインドウのクライアント座標系での位置を別のウインドウの
クライアント座標系に変換するには、一旦スクリーン座標系に
変換する必要があり、ClientToScreen()を使います。
逆の変換にはScreenToClient()を使用します。で、移動させる手順ですが、
A.対象StaticはWM_LBUTTONDOWNでマウスキャプチャーを含む
移動開始処理を実行する。
B.WM_MOUSEMOVEで自身に対する移動処理を行う。
C.WM_LBUTTONUPで、移動完了処理を行う。その他、
X.移動中にキャプチャーが外れた場合はどうするかをきめる必要があります。
WM_CAPTURECHANGEDで、そのストラテジを実装する。てな感じになると思います。
任意のウインドウの矩形をスクリーン座標で取得するにはGetWindowRect()を使います。- 編集済み 仲澤@失業者 2013年5月29日 4:49
- 回答としてマーク arubi_momo 2013年5月29日 5:09
-
仲澤@失業者様
お世話になっております。
丁寧なご回答をありがとうございます。できました!!
コントロールがクリックされたことは、コントロール側でないとわからないと思い、
派生クラスを作成していたのですが、親ダイアログ側で取得できるんですね。。。
そもそもそこがわかっていませんでした。
LBUTTONDOWN:コントロールが選択されているかを判断し、移動フラグを立てる
MOUSEMOVE:移動フラグが立っていた場合、移動処理を行う
LBUTTONUP:移動フラグを下ろす
すべてダイアログ側の命令で実行することができました。
これから精進してまいりますので、今後ともよろしくお願いいたします。
ありがとうございました。
-
自分が期待した結果とやや異なる実装になってしまったようですね(笑)。
でも、それでもかまいません。要は意図した実装になっていれば良いわけです。それでも、少し意見しておかなければならない点があります。
1.その実装でうまく動くのはStaticコントロールの特殊な仕様によるものです。
ということですね。
スタティックコントロールは、他の一般的なコントロールと異なり、
マウスメッセージを親に透過してしてしまうという特殊な仕様になっています。
そのため、今回の実装でOKになっているわけですね。
すなわち、そのコードでは、その動作を他のコントロールでも動作するように
一般化することはできない点に注意が必要です。 -
仲澤@失業者様
お世話になっております。
違ってましたか。(笑) すみません。
なるほど、スタティックコントロールは特殊なのですね・・・。
今回の場合、ダイアログ上で動かしたいのはスタティックコントロールのみなので、
この実装でも問題ないとは思いますが、とはいえ、ひとつひとつを理解していかないと
いけないと思いますので、時間がある時に他のコントロールでの動きも試してみたいと思います。
他のコントロールではマウスメッセージを親に透過しないということは、
コントロール側でマウスメッセージを受け取れるということですよね。
ちょっとまだ想像がついていませんが><
近いうちに家で試してみようと思います。ありがとうございました。
-
スタティックコントロールのマウスメッセージがなぜダイアログに届くかだけ。。。
スタティックコントロールは、マウスがウィンドウのどこにあるか?という、WM_NCHITTEST のメッセージに対し、HTTRANSPARENT(ウィンドウのその部分はマウス処理を不要とする透過エリアである)という値を返します。
一応。。。WM_NCHITTEST でいう透過はあくまでもマウスのヒットテスト(その座標がウィンドウのどこであるかの判定処理)における透過であって実際に描画されない領域ではないので注意してくださいね。
スタティックコントロールは、ヒットテストでウィンドウのどこにマウスがあっても常に透過を返すため、マウスから見るとそこにはウィンドウがないのと同じ意味になります。
そのため、マウスメッセージは、スタティックコントロールに送られてくることはなく、そのまま親ウィンドウに送られていきます。
ちなみに、WM_MOUSEMOVE や、LBUTTONDOWN などを受け入れたい場合は、HTCLIENT(クライアント領域である)と返します。
そうすると、ダイアログではなくスタティックコントロール上でマウス処理を行えるようになります。
わんくま同盟,Microsoft MVP for Visual C++(Oct 2005-) http://blogs.wankuma.com/tocchann/