簡易グラフィック(2)

任意のタイミイグで描画する

これには二つの方法があります。

第一の方法
GetDC()でデバイスコンテキストを「取得」して描画し、ReleaseDC()で解放します。 当然WM_PAINTに対する応答でも同じものを描画できるようにしておかないと、 ウィンドウが隠されて、再び見えるようになった時、以前描いた内容が 消えてしまいます。

第二の方法
InvaridateRect() APIで自分自身にたいしてWM_PAINTメッセージを送ります。 この方法なら描画ルーチンは一つで済むので簡単です。

実際に、ボタンを押したら描画する「カウンター」プログラムをつくってみましょう。 ボタンの追加方法は、もうわかりますよね。

第一の方法の例
void draw(HDC hdc, int counter){
	HFONT hfont, hfontOld;
	char buf[260];
	hfont=GetStockObject(OEM_FIXED_FONT);/*固定ピッチフォント*/
	hfontOld=SelectObject(hdc, hfont);
	SetTextColor(hdc, RGB(rand()%256, rand()%256, rand()%256)); /*文字色*/
	SetBkColor  (hdc, RGB(rand()%256, rand()%256, rand()%256)); /*背景色*/
	SetBkMode(hdc, OPAQUE);           /*背景色を不透明にする*/
	//SetBkMode(hdc, TRANSPARENT);      /*背景色を透明にする*/
	sprintf(buf, "%6d", counter);
	TextOut(hdc, 3, 3, buf, strlen(buf));
	SelectObject(hdc, hfontOld);
	DeleteObject(hfont);
}

BOOL FAR PASCAL count(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam){
	/*中略*/
	case WM_COMMAND:
		switch((WORD)wParam) {
		case IDCOUNT:
			counter++;
			hdc=GetDC(hDlg);
			draw(hdc, counter);
			ReleaseDC(hDlg, hdc);
			break;
	/*中略*/
	case WM_PAINT:
		hdc = BeginPaint(hDlg, &ps);
		draw(hdc, counter);
		EndPaint(hDlg, &ps);
		break;
第二の方法の例
	case WM_COMMAND:
		switch((WORD)wParam) {
		case IDCOUNT:
			counter++;
			InvalidateRect(hDlg, NULL, TRUE);/*WM_PAINTを自分自身に送る*/
			break;
	/*中略*/
	case WM_PAINT:
		hdc = BeginPaint(hDlg, &ps);
		hfont=GetStockObject(OEM_FIXED_FONT);/*固定ピッチフォント*/
		hfontOld=SelectObject(hdc, hfont);
		SetTextColor(hdc, RGB(rand()%256, rand()%256, rand()%256)); /*文字色*/
		SetBkColor  (hdc, RGB(rand()%256, rand()%256, rand()%256)); /*背景色*/
		SetBkMode(hdc, OPAQUE);           /*背景色を不透明にする*/
		//SetBkMode(hdc, TRANSPARENT);      /*背景色を透明にする*/
		sprintf(buf, "%6d", counter);
		TextOut(hdc, 3, 3, buf, strlen(buf));
		SelectObject(hdc, hfontOld);
		DeleteObject(hfont);
		EndPaint(hDlg, &ps);
		break;
「カウンター」の全ソース

連続して描画し続ける

変化しつづける表示をするために、以下のようなことをやってはいけません。
	case WM_PAINT:
		hdc = BeginPaint(hDlg, &ps);
		for (;;) {/*無限ループ、もしくはそれに近い、長時間かかるループ*/
			/*実際の描画処理*/
		}
		EndPaint(hDlg, &ps);
		break;
こういうことをすると、メッセージ処理が止まってしまいます。つまり、 そのアプリは、ユーザーが操作できなくなるということです。 Windowsには、「0.1秒ルール」というのがあります。これは、どんな メッセージも、原則として0.1秒以内に処理ルーチンからリターンしなければ ならないというものです。 こういう場合、タイマメッセージなど、一定のタイミングでやってくる メッセージを利用します。タイマメッセージは、SetTimer()で設定すると、WM_TIMERメッセージを送ってきます。 KillTimer()で解除します。

では作ってみましょう。こういう例題として「時計」はちょっとありきたりですが……。
「時計」の全ソース

なお、利用するメッセージは定期的にやってくるメッセージなら何でも良いので、 タイマメッセージを使ったのはあくまで例です。マルチメディア系のプログラムなら MM_WOM_DONE とか MM_MCINOTIFY とかを定期的に送ってくるようにプログラミングできますので、 そちらを使うほうが自然です。

「0.1秒ルール」には、例外が一点あります。メッセージボックスや、他のモーダル ダイアログボックスを出す場合は、ユーザはそちらを操作できます。実際、 MessageBox APIやDialogBox APIは、そのダイアログをユーザが閉じない限り リターンしないので、そもそも「0.1秒ルール」を守ることは不可能です。

それ以外の状況で、どうしても「0.1秒ルール」をやぶらなければならないときは、 カーソルを砂時計にするなどして、操作できないことをユーザに知らせます。

それとは別に、デバッグ目的であれ何であれ、WM_PAINTメッセージの応答中に メッセージボックスやダイアログボックスを出すのは厳禁です。 これは「時間がかかるから」という理由ではなく、その新たなダイアログを 閉じたり移動したりするとき、もう一度WM_PAINTメッセージが発生する可能性が あるからです。そうするとさらにダイアログが開かれ、これを無限に繰り返すことに なってしまいます。

あと、これは余談ですが、CPUの速度は最近のギガヘルツのオーダーのものから i386sx 16MHzのような超低速なものまであります。「0.1秒ルール」は可能なかぎり 遅いマシンでも達成できるようにすべきです。

前のページ

次のページ

一つ上のページに戻る