none
Visual C++ MFC でフォームビューでダブルバッファリングがうまくいきません。 RRS feed

  • 質問

  • H26年1月1日

    Visual C++ MFC でフォームビューでダブルバッファリングを使用すると、クライアント領域が白くなり、またコントロールの文字に他のコントロールの文字が上書きされます

    お世話になります。

    MFCでピクチャボックスにグラフィックを表示したいのですが、ちらつき防止のため、ダブルバッファリングを使用すると、下記2項のような不具合が発生します。何か良い方法はないでしょうか。

    どのような情報でもけっこうですので、何かご教示いただければ幸いです。

    当方はMFCの初心者で、特にフォームビューでコントロールを配置するのは初めてです。

    1.      当方で行ったこと(環境はWin8.1VS2013です)

    1.1 新規プロジェクトの作成

     (1)テンプレート:「MFCアプリケーション」

     (2)プロジェクト名:「Foutest

     (3)アプリケーションの種類:「シングルドキュメント」

     (4)プロジェクト形式:「MFC標準」

     (5)「メニューバーとツールバーを使用する」を選択。

     (6)「パーソナル化されたメニュー動作」のチェックを外す。

     (7)生成されたクラスの基本クラス:「CFormView

      以上の他はデフォルトのままです。

    1.2 コントロールの配置

    (1)ピクチャボックスを配置する。ピクチャボックスの左に次のコントロールを上から順に並べました。

    (2)ボタン1

    (3)コンボボックス1個。コンボボックスのプロパティは次の点をデフォルトから変更しました。

       ①「Type」を「ドロップダウンリスト」に変更

       ②「Sort」を「False」に変更

    (4)ラジオボタン2

    1.3 プログラミング

    (1)ピクチャボックスのコントロール変数として、「m_pict」を追加しました。

    (2)OnInitialUpdate()内にコンボボックスに表示する文字をプログラムしました。

    (3)OnPaint()内に、ネットに出ていたダブルバッファリングの処理を殆どそのまま持ってきました。

    (4)OnEraseBkgnd()をオーバライドし、何もしないようにしました。

    プログラムの詳細は3項に示す通りです。

    2.      不具合内容

    (1)ピクチャボックスの中だけを青く塗り潰しているのですが、その背景のウィンドウのクライアント領域がグレーのはずが、白くなってしまいます。

    (2)ボタンの輪郭の枠の線の色が上半分は薄く、下半分は濃くなってしまします。

    (3)コンボボックスの文字が、その下のラジオボタン2個に上書きされてしまいます。

    以上の不具合は、OnEraseBkgnd()の処理をデフォルトの通りに行うようにすると、全て直ります。直りますが、ウィンドウのサイズ変更の際に、ちらつきが発生します。

    3.      プログラムリスト

    (1)ヘッダーファイル(コントロール変数「m_pict」などの追加部分)

    // 生成された、メッセージ割り当て関数

    protected:

           afx_msg void OnFilePrintPreview();

           afx_msg void OnRButtonUp(UINT nFlags, CPoint point);

           afx_msg void OnContextMenu(CWnd* pWnd, CPoint point);

           DECLARE_MESSAGE_MAP()

    private:

           // ピクチャボックスのコントロール変数

           CStatic m_pict;

    public:

           afx_msg void OnPaint();

           afx_msg BOOL OnEraseBkgnd(CDC* pDC);

    private:

           CComboBox m_combo1;

    (2)ソースファイル(追加部分)

    void CFoutestView::OnInitialUpdate()

    {

           CFormView::OnInitialUpdate();

           GetParentFrame()->RecalcLayout();

           ResizeParentToFit();

           //-----------------------------------------ここから追加

           // コンボボックスの設定

           m_combo1.AddString(L"方形波");

           m_combo1.AddString(L"台形波");

           m_combo1.AddString(L"ノコギリ波");

           // 最初に最上段の文字列を表示する

           //Web:http://oshiete.goo.ne.jp/qa/966619.html

           m_combo1.SetCurSel(0);

           UpdateData(FALSE);

           //-----------------------------------------ここまで追加

    }

    void CFoutestView::OnPaint()

    {

           CPaintDC dc(this); // device context for painting

           // TODO: ここにメッセージ ハンドラー コードを追加します。

           // 描画メッセージで CFormView::OnPaint() を呼び出さないでください。

           //ダイアログ全体の大きさ取得

           CRect rect_dlg;

           GetClientRect(&rect_dlg);

           //青色ブラシ

           //CBrush br_white(RGB(255, 255, 255));

           CBrush br_blue(RGB(128, 128, 255));

           //CBrush br_red(RGB(255, 128, 128));

           //PictureControlのデバイスコンテキスト取得

           CDC* pDC = m_pict.GetDC();

           //PictureControlの大きさ取得

           CRect rect;

           m_pict.GetClientRect(&rect);

           //②仮想デバイスコンテキスト作成

           CDC memDC;

           memDC.CreateCompatibleDC(m_pict.GetDC());

           //③仮想デバイスコンテキスト用ビットマップ作成

           CBitmap memBmp;

           memBmp.CreateCompatibleBitmap(m_pict.GetDC(), rect.Width(), rect.Height());

           //④仮想デバイスコンテキストにビットマップ設定

           CBitmap* memOldBmp = memDC.SelectObject(&memBmp);

           //⑤元のブラシをoldbrに保持

           CBrush* oldbr = memDC.SelectObject(&br_blue);

           //⑤ブラシを白色にする

           memDC.SelectObject(&br_blue);

           //⑤背景を白にする

           memDC.Rectangle(&rect);

           //⑥描画

           BitBlt(pDC->GetSafeHdc(), 0, 0, rect.Width(), rect.Height(),

                   memDC.GetSafeHdc(), 0, 0, SRCCOPY);

           //元のブラシに戻す

           memDC.SelectObject(oldbr);

           //仮想デバイスコンテキストのビットマップを初期化

           memDC.SelectObject(memOldBmp);

           //仮想デバイスコンテキストのビットマップを廃棄

           memBmp.DeleteObject();

    }

    BOOL CFoutestView::OnEraseBkgnd(CDC* pDC)

    {

           // TODO: ここにメッセージ ハンドラー コードを追加するか、既定の処理を呼び出します。

         return false;

         //return CFormView::OnEraseBkgnd(pDC);

    }

    以上、よろしくお願いします。

    2014年1月1日 7:18

回答

  • その不具合が生じるのは OnEraseBkgnd で何もしないと決めていることに対して、その代替行為(ダブルバッファ内でほかの部分の背景をクリアするなど)が存在しないことからでしょう。
    ダブルバッファで描画すると言うことは、背景の塗りつぶしを含めたすべての描画をオフスクリーンに一度に実施し、その結果を画面に一括で転送することですので、背景の塗りつぶしをなくすだけではおかしくなります。
    参照されたサンプルは こちらのサイト だと思いますが、OnEraseBkgnd をオーバーライドしていませんよね?

    ウィンドウをリサイズしたときには確かにデフォルトの背景色で塗るという行為の後に OnPaint で描くため、一瞬背景色が見える可能性はあります。

    手段として考えられるのは CStatic クラスを継承したクラスで今の OnEraseBkgnd / OnPaint の処理を実行しつつ、m_pict を CStatic からその継承クラスに変更することです。
    ただ、今時の Windows では DWM が暗躍してくれるので、ちらつきが目立つ場面は少ない印象を持っています。手元で実験してもなかなか「問題視するレベルのちらつき」というものを把握しづらく、改善案が効いているかどうか判断つきかねています。

    2014年1月3日 4:17
    モデレータ

すべての返信

  • その不具合が生じるのは OnEraseBkgnd で何もしないと決めていることに対して、その代替行為(ダブルバッファ内でほかの部分の背景をクリアするなど)が存在しないことからでしょう。
    ダブルバッファで描画すると言うことは、背景の塗りつぶしを含めたすべての描画をオフスクリーンに一度に実施し、その結果を画面に一括で転送することですので、背景の塗りつぶしをなくすだけではおかしくなります。
    参照されたサンプルは こちらのサイト だと思いますが、OnEraseBkgnd をオーバーライドしていませんよね?

    ウィンドウをリサイズしたときには確かにデフォルトの背景色で塗るという行為の後に OnPaint で描くため、一瞬背景色が見える可能性はあります。

    手段として考えられるのは CStatic クラスを継承したクラスで今の OnEraseBkgnd / OnPaint の処理を実行しつつ、m_pict を CStatic からその継承クラスに変更することです。
    ただ、今時の Windows では DWM が暗躍してくれるので、ちらつきが目立つ場面は少ない印象を持っています。手元で実験してもなかなか「問題視するレベルのちらつき」というものを把握しづらく、改善案が効いているかどうか判断つきかねています。

    2014年1月3日 4:17
    モデレータ
  • Azulean 様

    H26年1月4日

    お世話になります。書き込みありがとうございます。また返信が遅くなりすみません。

    CStatic クラスを継承したクラスで今の OnEraseBkgnd / OnPaint の処理を実行しつつ、m_pict CStatic からその継承クラスに変更することです。」とのことです。

    せっかくご教示いただいたのですが、まったくの初心者である今の私には、何のことを言われているのか、ちんぷんかんぷんです。少し勉強させてください。おっしゃることの意味が分かるようになったら、やってみたいと思います。

    取りあえずは、ちらつきを回避するため、ウィンドウのサイズ変更はあきらめました。サイズ変更を禁止してプログラムを作成しています。ダブルバッファリングは使いますが、OnEraseBkgndのオーバライドはやめました。

    2014年1月4日 7:01
  • フォーラム オペレーターの星 睦美です。

    fghck854 さん、こんにちは。

    アドバイスとして今後の参考になりそうだと思いましたので、今回は私のほうでAzulean さんからの返信に[回答としてマーク] させていただきました。

    それでは、これからもMSDN フォーラムをお役立てください。


    フォーラム オペレーター 星 睦美 - MSDN Community Support

    2014年1月7日 0:56