グラフや四角形などを描画しているのですが、
この画面のスクロール時にグラフを再描画(OnDraw())をするために
Invalidate() をコールしています。
これだと、再描画時にグラフや四角形などの表示がちらつくため、
なんとか、ちらつかないようにできないものでしょうか?
よろしくお願い致します。
Invalidate() が呼ばれると、
まず WM_ERASEBKGND というメッセージが飛んできて、画面全体を背景色で塗りつぶしま
す。その後、WM_PAINT というメッセージが飛んできて、画面を再描画することになりま
す(WM_PAINTのメッセージハンドラOnPaint()から OnDraw()が呼ばれます)。
一度、画面全体を背景色で塗りつぶすために、ちらつくのです。
なので、WM_ERASEBKGNDのメッセージハンドラ OnEraseBkgnd() を実装し、ベースクラス
のOnEraseBkgnd()を呼ばないようにすると背景色での塗りつぶしが無くなるためちらつ
かなくなります。
ただし、その場合は、背景に当たる部分も OnDraw() 内で描画しなければならないこと
に注意してください。
> 一度、画面全体を背景色で塗りつぶすために、ちらつくのです。
であるならば、Invalidate()の呼び出しをInvalidate(FALSE)に変えることで、
背景を消去を行なわないようにすることが出来るので、OnEraseBkgnd()を実装す
るより、より簡単かと思います。
あれ? 何か勘違いしてるのかな。
http://msdn.microsoft.com/library/ja/default.asp?
url=/library/ja/vclib/html/_mfc_cwnd.3a3a.invalidate.asp
によれば、bErase = TRUE により、背景が消去されるのはBeginPaint()が呼び出された時って
書いてある。
BeginPaint()って普通はOnPaint()から呼び出すと思うけど、違ったかな?
本題に戻って...
スクロール処理をするのならば、CScrollViewを使った方が簡単だと思うけど如何でしょうか。
すでに使っているのであれば悪しからず。
参考までに
Invalidate()する方法をやめて
::ScrollWindowEx( hwnd, 0, scrl_y, NULL, NULL, NULL, NULL, ( SW_INVALIDATE |
SW_ERASE));
を使用するという方法もあります(上の例はY方向だけにスクロールします)。
スクロールされた結果無効になった部分だけが再描画の対象リージョンになり、
OnPaintで全てを描画しなおしても、このリージョンだけが表示上の
更新対象となります。結果としてチラツキが抑えられます。
ご返答ありがとうございます。
Invalidate(FALSE); は、行ってみたのですが・・・
それでも、まだ、ちらつきは直らなかったのです。
よろしくお願い致します。
毎度、チラツキ→これしか案内しないパターンです。
>> Flicker Free Drawing In MFC
> www.codeproject.com/gdi/flickerfree.asp?target=CMemDC
CMemDCを使ったほうが早いと思います。WM_ERASEBKGNDやスクロールも
含めて全て記載されているので。
(ダウンロードにはメンバ登録が必要ですが。フリーメールもOKです)
# かといって、チラツキが直らない原因を知らなくてもよい、とは言いませんが。
ご返答ありがとうございます。
Invalidate()を ScrollWindowEx()に変更すると、
残念ながら・・・
そもそも描画処理自体が何も行われないようです。
ご返答ありがとうございます。
よくわからないのですが・・・
Invalidate()を ScrollWindowEx()に変更する方法では、
OnDraw() が呼ばれないようですが、これは再描画(OnDraw)する必要がなく
ScrollWindowEx()内で、再描画まで行われるという理解で
よろしいのでしょうか?
環境がよく分かりませんが、下記はVS2005MFCです
以前私も、描画に関する質問を、この掲示板で質問させていただき
皆さんのお世話で、解決できました、あまり自信はありませんが、いかがでしょうか
多分察するに、Invalidate()する、インスタンスが取れていないように見受けられます。
【背景色再描画処理】
背景スタイル 四角
背景色 黒
ハッチング 有り
CDC m_MemDC;
void InitializationScreen::InvalidateCtrl()
{
CClientDC dc(this);
CRect rcClient;
GetClientRect(rcClient);
if (m_MemDC.GetSafeHdc() == NULL)
{
m_MemDC.CreateCompatibleDC(&dc);
m_Bitmap.CreateCompatibleBitmap(&dc,rcClient.Width(),rcClient.Height());
m_MemDC.SelectObject(m_Bitmap);
// draw scale
m_MemDC.SetBkColor(RGB(0,0,0));
CBrush bkBrush(HS_CROSS,RGB(0,128,0));
m_MemDC.FillRect(rcClient,&bkBrush); // ハッチング
}
InvalidateRect(rcClient, FALSE);
}
個人的に良く使う手は、
1. ウィンドウのクラススタイルから CS_HREDRAWと、CS_VREDRAWを外す。
2. WM_ERASEBKGND をハンドリングする。(bunさんのレスと同じ)
3. ScrollWindowEx()で帰ってくるクリップ矩形のみを、'同期的に'再描画する。
(つまり、Invalidate系を使わない)
です。これだけやれば、描画のちらつきは、まず無くなります。
あと、描画図形が複雑で、且つ余り更新されない場合は
ダブルバッファによる描画も良いでしょう。
皆様ご返答ありがとうございます。
以下の簡単なコード例は、教えて戴いた ScrollWindowEx() する方法で、
ちらつきは、無くなりスクロールできました。
・・・が、OnDraw() するたびに、赤、青のグラフを交互に描画したいのですが、
常に、赤しか描画されないのは、どういうわけでしょうか?
ちなみに、ScrollWindowEx() でなく、Invalidate() を呼ぶと、ちらつきは発生
しますが、グラフ描画は、赤、青 交互に描画されます。
よろしくお願い致します。
int gFlg = 1;
// CTestFormView メッセージ ハンドラ
void CTestFormView::OnDraw(CDC* pDC)
{
if ( gFlg == 1 ) {
// 赤グラフ描画
pDC->FillSolidRect(10, 10, 100, 100, RGB(255, 0, 0));
gFlg = 0;
} else {
// 青グラフ描画
pDC->FillSolidRect(10, 100, 100, 200, RGB(0, 0, 255));
gFlg = 1;
}
}
// OK ボタンのクリックイベント
void CTestFormView::OK_Button1()
{
//Invalidate(TRUE);
// スクロール
ScrollWindowEx( 5, 5, NULL, NULL, NULL, NULL, ( SW_INVALIDATE | SW_ERASE));
}
ScrollWindowExは、わざわざ再描画する(OnDrawを呼ぶ)とは限らないから、では。
必要がなければ、既に書いてあるデータを移動するだけですから、
gFlg = xxx を含むOnDraw自体が呼ばれないこともあるでしょう。
# クリックイベントの方で、gFlgを更新するべきだと思いますが、
# なぜにOnDraw?この例はシンプルに表現するためのサンプルですか?
# 蛇足の補足
#> クリックイベントの方で、gFlgを更新するべきだと思いますが、
# 単に所管であって、こうすれば直るという話ではないです。
ご返答ありがとうございます。
ScrollWindowEx でも、再描画(OnDraw)は、常に呼ばれているようです。
呼ばれて・・・
pDC->FillSolidRect(10, 10, 100, 100, RGB(255, 0, 0));
または、
pDC->FillSolidRect(10, 100, 100, 200, RGB(0, 0, 255));
のいすれかが実行されているのですが、表示が変わらないのです。
よろしくお願い致します。