MFCの基礎 - 画面の描画

提供:MochiuWiki : SUSE, EC, PCB
2021年11月15日 (月) 00:33時点におけるWiki (トーク | 投稿記録)による版 (文字列「<source lang」を「<syntaxhighlight lang」に置換)
ナビゲーションに移動 検索に移動

概要

MFCプログラムを作成すると画面描写に注意する必要がある。
例えば、画面描写に不具合があると、画面からウインドウが外れて再び表示されると途切れて描写される。
そこで、画面の再描写が必要なタイミングを知る必要がある。

MFCでは、画面の再描画のタイミングをOnPaintメソッドやOnDrawメソッドで知ることができる。


OnPaintメソッドとOnDrawメソッドの使い分け

OnPaintメソッドとOnDrawメソッドの使い分けを下記に記述する。

ダイアローグベース : OnPaintメソッド
ドキュメント・ビュークラス : OnDrawメソッド


OnPaintメソッドとOnDrawメソッドが呼ばれるタイミング

OnPaintメソッドとOnDrawメソッドが呼ばれるタイミングを下記に記述する。

  • アプリの起動時
  • 画面が隠れて再度表示する時
  • Invalidate関数、InvalidateRect関数が実行された時


画面全体を再描画させる場合はInvalidata関数を使い、指定した四角形領域だけを再描画する場合はInvalidateRect関数を使用する。


サンプルコード

ダイアローグベースのプログラムで、OnPaintメソッドを使った例を以下に記述する。
ここでは、ピクチャコントロールを塗って表示するプログラムを作成する。

  • ダイアログのコントロールにて、ピクチャコントロールとボタンを貼る。
  • ピクチャコントロールのIDをIDC_STATIC_PICT、タイプをオーナー描画とする。
  • ボタンコントロールのイベントを作成する。(OnBnClickedButtonメソッド)
  • フォームを選択して、プロパティのメッセージからWM_CLOSEとWM_PAINTよりOnCloseイベントとOnPaintイベントを作成する。


後は、以下のようにサンプルコードを記述する。

<syntaxhighlight lang="c++">
// CDlgView.h
// 下記の変数を追加する(private, public どちらでもよい)
char  *m_pBuffer; // 画素データ
CRect m_Rect;     // ピクチャコントロールのサイズ
CDC   m_DC;       // 画面に表示する時に使用する仮想DC(一時保存)

// CDlgView.cpp
void CDlgView::OnInitialUpdate()
{
   CFormView::OnInitialUpdate();
   GetParentFrame()->RecalcLayout();
   ResizeParentToFit();

   ((CStatic*)GetDlgItem(IDC_STATIC_PICT))->GetClientRect(m_Rect);

   // 表示画素データのメモリを確保する
   m_pBuffer = new char[m_Rect.Width() * m_Rect.Height() * 4];
   for(int i = 0; i < m_Rect.Width(); i++)
   {
      for(int j = 0; j < m_Rect.Height(); j++)
      {
         m_pBuffer[(i + j * m_Rect.Width()) * 4]     = 100;
         m_pBuffer[(i + j * m_Rect.Width()) * 4 + 1] = 100;
         m_pBuffer[(i + j * m_Rect.Width()) * 4 + 2] = 100;
       }
   }

   // ピクチャコントロールと同じDCを確保する
   CDC *pDC=((CStatic*)GetDlgItem(IDC_STATIC_PICT))->GetDC(); // ピクチャコントロールのDCを取得
   m_DC.CreateCompatibleDC(pDC);                              // pDCと同じDCを作成
   ((CStatic*)GetDlgItem(IDC_STATIC_PICT))->ReleaseDC(pDC);   // ピクチャコントロールのDCを解放
}

void CDlgView::OnPaint()
{
   CPaintDC dc(this); // device context for painting
   // TODO: ここにメッセージ ハンドラー コードを追加します。
   // 描画メッセージで CFormView::OnPaint() を呼び出さないでください。
   CDC *pDC = ((CStatic*)GetDlgItem(IDC_STATIC_PICT))->GetDC(); //ピクチャコントロールのDCを取得

   // 画素データを読み取り、仮想DCにセットする
   CBitmap bitmap;
   bitmap.CreateBitmap(m_Rect.Width(), m_Rect.Height(), 1, 32, m_pBuffer);
   myDC.SelectObject(&bitmap);

   // 仮想DCからピクチャコントロールへ転送する
   pDC->BitBlt(1, 1, m_Rect.Width(), m_Rect.Height(), &m_DC, 0, 0, SRCCOPY);

   ::DeleteObject(bitmap);
   ((CStatic*)GetDlgItem(IDC_STATIC_PICT))->ReleaseDC(pDC);
}

void CDlgView::OnBnClickedButton()
{
   // TODO: ここにコントロール通知ハンドラー コードを追加します。
   // m_pBufferを全て白色(255)に変更する
   for(int i = 0; i < m_Rect.Width(); i++)
   {
      for(int j = 0; j < m_Rect.Height(); j++)
      {
         m_pBuffer[(i + j * m_Rect.Width()) * 4]     = (char)255;
         m_pBuffer[(i + j * m_Rect.Width()) * 4 + 1] = (char)255;
         m_pBuffer[(i + j * m_Rect.Width()) * 4 + 2] = (char)255;
      }
   }

   Invalidate(); // OnPaintを呼ぶ
}

void CDlgView::OnClose()
{
   // TODO: ここにメッセージ ハンドラー コードを追加するか、既定の処理を呼び出します。
   // DCとメモリの解放を行う
   DeleteDC(m_DC);
   delete[] m_pBuffer;

   CFormView::OnClose();
}
</source>