[WINAPI]画面のチラつきを抑えるには?様々なケースの対策法を紹介!

WinAPI

ウィンドウズアプリの開発をしているによく起こる問題の1つに画面のチラつきがあります。

誰もが一度は経験をしたことがある問題であり、なかなか解決することができないのがこのチラつきです。

これは画面の再描画をしている最中に画面の更新が行われてしまうのが原因で、再描画により画面を一度単色で塗りつぶすことでチラついて見えてしまいます。
つまり、画面を塗りつぶさなければチラつきを抑えることができます。

スポンサーリンク

チラつきを抑える方法は?

では具体的な方法です。
画面を塗りつぶさなくするには、ウィンドウメッセージのWM_ERASEBKGND を受けた時に1 を返すようにします。
これにより、WM_ERASEBKGND のデフォルトの処理である画面の塗りつぶしをカットできます。

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	switch (message) {

	case WM_ERASEBKGND:
		return 1;
	}
}

次に、WM_PAINT 内の処理なのですが、普通にhdcを使って描画するのではなく、オフスクリーンバッファを作成して、そちらに画面を描画して書き終わり次第、メインスクリーンに転送するようにします。

具体的には、ビットマップを作成してそこに画面を描画し、それをBitBlt で転送して表示することでじつげんできます。

これによりチラつきは出なくなります。

void OnPaint( HWND a_hWnd, HDC a_hdc )
{
	RECT rc;
	HDC hMemDC;
	HBITMAP hMemBmp, hOldBmp;

	GetClientRect( a_hWnd, &rc );
	hMemDC = CreateCompatibleDC( a_hdc );
	hMemBmp = CreateCompatibleBitmap( a_hdc, rc.right, rc.bottom );
	hOldBmp = (HBITMAP)SelectObject( hMemDC, hMemBmp );


	//
	//  背景の描画
	//
	FillBackGround( hMemDC, RGB( 255, 255, 255 ) );

	OnDoubleBuffer( a_hWnd, hMemDC );

	BitBlt( a_hdc, 0, 0, rc.right, rc.bottom, hMemDC, 0, 0, SRCCOPY );

	SelectObject( hMemDC, hOldBmp );
	DeleteObject( hMemBmp );
	DeleteDC( hMemDC );
}

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	switch (message) {

	case WM_PAINT:
		{
			PAINTSTRUCT ps;
			HDC hdc = BeginPaint(hWnd, &ps);
			OnPaint( hWnd, hdc );
			EndPaint(hWnd, &ps);
		}
		break;
	}

}

チラつき防止をしてもチラつく時は?

時には、上記の方法を使っても画面がチラつく時があります。

なぜか?
その理由は、チラつき防止をされていないウィンドウがどこかにあるからです。

これは色々なケースが考えられます。

ケース1. 親ウィンドウが見えない

親ウィンドウのクライアント領域いっぱいに子ウィンドウを配置。
子ウィンドウにチラつき防止処理をしている。

この場合、画面のサイズを変更すると親ウィンドウにも画面サイズ変更や再描画のメッセージが飛びます。
もちろんWM_ERASEBKGND も飛びます。

そのため、親のウィンドウを塗りつぶしてしまうので、子ウィンドウでチラつき防止をしていても結果として画面はチラつきます。

ケース2. 配置したコントロールがチラつく

子ウィンドウとして配置したコントロールがチラつく場合は、コントロールにもチラつき防止処理が必要です。
しかし、それは実装が大変ですよね。

そんな時は、ウィンドウスタイルにWS_CLIPCHILDREN を設定します。

	HWND hWnd = CreateWindowEx( 0,
		s_wsClassName,
		m_wsWindowName,
		WS_CHILD | WS_CLIPCHILDREN | WS_VISIBLE,
		0, 0, 10, 10,
		a_hParent,
		NULL,
		hInst,
		0 );

これをすれば、再描画時、子ウィンドウの領域は再描画しなくなるのでチラつきません。

子ウィンドウが重なっている部分がチラつく。
そんな場合は、子ウィンドウを作成する時のウィンドウスタイルに WM_CLIPSIBLINGS を設定します。

	HWND hWnd = CreateWindowEx( 0,
		s_wsClassName,
		m_wsWindowName,
		WS_CHILD | WM_CLIPSIBLINGS | WS_VISIBLE,
		0, 0, 10, 10,
		a_hParent,
		NULL,
		hInst,
		0 );

これでチラつきが抑えられます。

リストビューがチラつく。

拡張ウィンドウスタイルにWA_EX_DOUBLEBUFFERD を設定します。

	HWND hWnd = CreateWindowEx( WA_EX_DOUBLEBUFFERD,
		s_wsClassName,
		m_wsWindowName,
		WS_CHILD | WS_VISIBLE,
		0, 0, 10, 10,
		a_hParent,
		NULL,
		hInst,
		0 );

このようにチラつきを抑えるには色々な方法があります。
チラつきが抑えられない原因の多くは親ウィンドウにチラつき防止がされていなかったというものです。
チラつき防止をしているにもかかわらずチラつく時は、親ウィンドウに対するチラつき防止をチェックしてくださいね。

スポンサーリンク







ページの先頭へ