環境 WindowsXP VisualC++6.0 MFC, SDI
ダイアログウインドに貼り付けたピクチャー内への描画で2日ほど嵌っていて前に進め
ないため、質問させて下さい。
やりたいことはピクチャ内へグラフを描画することです。
ダイアログのOnPaint内に描画内容を記述すれば、動作することは確認しましたが、再描
画(具体的にはスライダの動き)の必要がある度にInvalidate(TRUE) を送っているため、
ダイアログ内の他の部分がちらついてしまいます。
そこでいろいろと調べた結果 ピクチャーをサブクラス化してサブクラス内のOnPaintに
描画内容を記載すれば良いのではないかと推測しました。
ここで問題が発生したのですが、上記やり方だとピクチャーの描画で背景色が一度塗り
潰されなず、所望の動作をしてくれません。
---------------------------------------------------------------------------
//ダイアログ内の関数からサブクラス化したクラスにInvalidateRectを送る
void CxxxDialog::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
CWnd* hWnd = GetDlgItem(IDC_PICTURE); //IDC_PICTUREはサブクラスしたPICTURE
hWnd->InvalidateRect(NULL, TRUE);
}
--------------------------------------------------------------------------
これだとなぜかピクチャーの背景が消去されません。
hWnd->InvalidateRect(NULL, TRUE); の代わりに
InvalidateRect(NULL, TRUE)でダイアログウインドウに送れば全体がチラツキますが動
作は正しくします。
どこが間違っているのかお手数ですがアドバイス願います。
ピクチャーと言われているのは多分ピクチャーコントロールの事だと
思いますが、ピクチャーコントロールの種類は何を設定しているんでしょう?
枠になっていると背景の塗潰しはしないのではないかと思います。
あと、せっかくInvaidateRectを使っているのですから、
再描画の必要があるところをきちんと認識して使えば、チラつきも少しは
抑えられると思います。
あとは、なるべく画面の直接書くのではなくてメモリ上のDCに描画して
描画し終わったものを画面に転送するとかでしょうか。
ちらつくと言うのは描画が遅い為に描画の過程が見えてしまっているからです。
この点に注意するとチラつきをある程度は抑えられます。
後は一回の描画量をなるべく減らして描画短時間に完結されるように
してみてはいかがでしょう。
現象はPATIO さんのおっしゃっているとおりだと思います。
解決方法として、一番簡単なのは、OnPaint()の中で背景を塗りつぶしてしま
うことだと思います。
InvalidateRect(NULL, TRUE);
で、WM_ERASEBKGNDメッセージが発生し、その結果として、背景が塗りつぶさ
れるのが通常のパターンです。
今回は、WM_ERASEBKGNDメッセージの結果として背景の塗りつぶしがおこらな
いだけです。
背景の塗りつぶしはWM_ERASEBKGNDメッセージでなければいけないという規則
はないので、OnPaint()の最初で背景を塗りつぶしてしまえばいいのです。
PATIO様、bun様 アドバイスありがとうございます。
おかげで解決しましたが、ひとつ釈然としないことがあるため、可能でしたらアドバイ
スをお願いいたします。
■問題解決の報告
PATIO様がご指摘のとおり、ピクチャーコントロールのフレームを選択していたた
め、背景塗りつぶしが起こっていなかったことが原因でした。
サブクラス化したクラスのOnPaint()の最初に背景塗りつぶしを行うことで所望の動作が
完成しました。
■疑問点
今回はサブクラス化を用いましたが、サブクラス化を用いないでダイアログ内でピクチ
ャーコントロールの座標を取得(rect)して、InvalidateRect(&rect, TRUE)とピクチャー
コントロール内のみを再描画すればダイアログのOnPaintが呼ばれて所望の動作ができる
と思いましたが、OnPaintが呼ばれません。
---------------------------------------------------------------------------
//ピクチャーコントロールの座標を取得してこの部分のみ再描画
void CxxxDialog::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
CWnd* hWnd = GetDlgItem(IDC_PICTURE); //IDC_PICTUREはピクチャーコントロール
hWnd->GetWindowRect(&rect);
InvalidateRect(&rect, TRUE);
}
----------------------------------------------------------------------
この記述だとダイアログのOnPaintが呼ばれません。
InvalidateRectの使用の仕方が間違っているのでしょうか?
また今回のようなケースはサブクラスを使用するのが常套なのでしょうか?
たぶん、座標変換をしていないからだと思います。
CWnd* hWnd = GetDlgItem(IDC_ST_PICTURE);
hWnd->GetWindowRect(&rect);
ScreenToClient(&rect); // <- この行を追加
InvalidateRect(&rect, TRUE);
bun様
度々のアドバイスありがとうございます。
おっしゃるとおり、座標変換をしていないことが原因でした。
スクリーン座標をクライアント座標に変換することで所望の動作をしました。
いままでは画面のちらつきを気にせずInvalidateRect(NULL, TRUE)で全領域を
再描画しており、今回の問題を通してWindowsの描画システムの理解が深まりました。
お忙しい中誠にありがとうございました。
...さん、subaruさん、PATIOさん、ご意見ありがとうございます。
>...さん
>透過ファイルを使ったマウスカーソル非表示
この方法については、一番最初の書き込みの
>自作したカーソルの無いカーソルを設定する方法
で実践しましたが、残念ながら他のウィンドウのカーソルに
適用することが出来ませんでした。
>subaruさん
>カーソルを非表示にしたいウインドウに対してAttachThreadInputで
>スレッドにアタッチしておき、その状態でShowCursorを呼び出してみては
>どうでしょうか。
ありがとうございます。おっしゃったAttachThreadInputを使い、
ShowCursorでカーソルを非表示にすることが出来ました。
>PATIOさん
subaruさんの方法で非表示にすることが出来ましたが、
アプリ側から制御を行っているみたいでして、非表示にしても
すぐに表示されてしまうことが頻繁に発生します。
(マウスイベント類が発生するとSetCursorを行っているみたいです)
ですから、
>一定時間毎に非表示にしているのかもしれません。
をする必要があるみたいです。
まだまだツールの機能としては不備が多く完成度はまだまだ低いですが、
subaruさんから教えていただいたAttachThreadInputを行うことで
他のウィンドウのカーソルを非表示にすることが出来ました。
これからもこのツールの機能を高めて、より汎用的なツールになるように
開発を続けていこうと思っています。
書き込んでいただいた皆様、お忙しい中本当にありがとうございました。
あ、すみません、間違いました;;