OnPaintとOnDraw関数

MFCプログラムを作成すると画面描写に注意する必要があります。

たとえば、画面描写をしっかりしないと画面からウインドウが外れて再び表示されると途切れて描写されます。 そこで、画面の再描写が必要なときをしる必要があります。

MFCでは画面の再描写のタイミングをOnPainやOnDrawで知ることができます。

OnPaintとOnDrawの使い分け

OnPaintとOnDrawがあるけどどちらを使えばいいかわからなくなります。 これらの使い分けは

  • ダイアローグベース->OnPaint
  • ドキュメント・ビュークラス->OnDraw

でOKでしょう。 電卓のようなアプリなら、OnPaint ワードやメモ帳のようなアプリを作るなら、OnDraw、 といった感じです。

OnPaintやOnDrawが呼ばれるタイミング

OnPaintやOnDrawが呼ばれるタイミングは

  • アプリの起動時
  • 画面が隠れて再度あらわれたとき
  • Invalidate関数、InvalidateRect関数が実行されたとき

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

プログラム例

ダイアローグベースのプログラムで、OnPaintを使ってみます。 ここでは、picture controlをぬって表示するプログラムを作ります。

GUIを作る

picture controlとbuttonを適当に貼り付けましょう。   そして、

  • Picture Controlの「IDをIDC_STATIC_PICT」、「タイプをオーナー描画」とする
  • Button1をダブルクリックしてイベントを作成
  • フォームを選択して、プロパティの「メッセージ」からWM_CLOSEとWM_PAINTよりOnCloseとOnPaintイベントを作成

とします。

変数の追加

ダイアローグのクラスを右クリックして以下の変数を追加します。

  • char* Buffer;
  • CRect myRect;
  • CDC myDC;

Bufferは画素データが入る場所のアドレスです。myRectはPictureControlの大きさが入ります。また、myDCは一時保存用のDCで画面に表示する際に使用します。   あとは以下のようにコードをかいていきます。

OnInitial Update

void CMFCApplication1View::OnInitialUpdate()
{
	CFormView::OnInitialUpdate();
	GetParentFrame()->RecalcLayout();
	ResizeParentToFit();

	((CStatic*)GetDlgItem(IDC_STATIC_PICT))->GetClientRect(myRect);
	//表示画素データのメモリを確保する
	Buffer=(char*)malloc(sizeof(char)*myRect.Width()*myRect.Height()*4);
	for(int i=0;i<myRect.Width();i++){
		for(int j=0;j<myRect.Height();j++){
			Buffer[(i+j*myRect.Width())*4]=100;
			Buffer[(i+j*myRect.Width())*4+1]=100;
			Buffer[(i+j*myRect.Width())*4+2]=100;
		}
	}
	//ピクチャコントロールと同じDCを確保する
	CDC *pDC=((CStatic*)GetDlgItem(IDC_STATIC_PICT))->GetDC();	//ピクチャコントロールのDCを取得
	myDC.CreateCompatibleDC(pDC);	//pDCと同じDCを作る
	((CStatic*)GetDlgItem(IDC_STATIC_PICT))->ReleaseDC(pDC);//
}

表示用画素データのメモリBufferの確保と、一時DCの取得を行っています。

OnPaint

void CMFCApplication1View::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(myRect.Width(),myRect.Height(),1,32,Buffer);
	myDC.SelectObject(&bitmap);

	//仮DCからピクチャボックスへ転送する
	pDC->BitBlt(1,1,myRect.Width(),myRect.Height(),&myDC,0,0,SRCCOPY);

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

}

Bufferの内容をbitmapへ写し、bitmapを一度仮想DCに表示してから一気にpicture controlへ描写しています。

OnBnClickedButton1

void CMFCApplication1View::OnBnClickedButton1()
{
	// TODO: ここにコントロール通知ハンドラー コードを追加します。
	for(int i=0;i<myRect.Width();i++){
		for(int j=0;j<myRect.Height();j++){
			Buffer[(i+j*myRect.Width())*4]=(char)255;
			Buffer[(i+j*myRect.Width())*4+1]=(char)255;
			Buffer[(i+j*myRect.Width())*4+2]=(char)255;
		}
	}
	//OnPaintを呼ぶ
	Invalidate();
}

Bufferをすべて255にして白色にします。そして、InvalidataでOnPainを呼び出しています。

OnClose

void CMFCApplication1View::OnClose()
{
	// TODO: ここにメッセージ ハンドラー コードを追加するか、既定の処理を呼び出します。
	DeleteDC(myDC);
        free(Buffer);

	CFormView::OnClose();
}

メモリの解放を行っています。

著者:安井 真人(やすい まさと)