InvalidateRect

원형 BOOL InvalidateRect(HWND hWnd, CONST RECT *lpRect, BOOL bErase);
MFC 원형 void CWnd::InvalidateRect( LPCRECT lpRect, BOOL bErase = TRUE );
인수

▶hWnd:무효 영역을 설정할 윈도우의 핸들. NULL일 경우 모든 윈도우를 무효화한다.

▶lpRect:무효화할 영역. NULL이면 작업 영역 전체가 무효화된다.

▶bErase:무효 영역의 배경을 먼저 지울 것인가를 지정한다. TRUE이면 BeginPaint 함수가 배경을 먼저 지운 후 작업 영역을 그린다.

리턴 성공하면 nonzero, 실패시 0을 리턴한다.
설명

운영체제는 윈도우의 작업 영역중 일부에 무효 영역이 있으면 WM_PAINT 메시지를 보내 다시 그리도록 한다. 프로그램 내부에서 작업 영역을 변경한 경우 이 함수로 변경된 부분을 무효화해 주어야 WM_PAINT 메시지가 발생하며 화면이 다시 그려진다. 배경을 지우고 그려야 할 때는 bErase를 TRUE로 설정해 주어야 이전에 출력되어 있던 내용이 삭제되고 다시 그려진다. 그리기 속도를 최대한 빠르게 하려면 lpRect에 무효화할 최소한의 영역을 지정하여 꼭 필요한 부분만 다시 그리도록 해 주어야 한다.

예제 1

다음 예제는 키보드로부터 문자를 입력받아 화면으로 출력한다. 입력된 키는 str버퍼에 누적되며 누적된 문자열을 WM_PAINT에서 출력하도록 하기 위해 작업 영역을 무효화한다. 이전 입력된 문자열이 누적되어 계속 출력되므로 작업 영역을 지울 필요가 없어 bErase는 FALSE로 주었다. InvalidateRect 호출문을 삭제하면 키보드를 눌러도 화면으로 문자열이 출력되지 않는다.

LRESULT CALLBACK WndProc(HWND hWnd,UINT iMessage,WPARAM wParam,LPARAM lParam)
{
	HDC hdc;
	PAINTSTRUCT ps;
	static char str[256];
	int len;

	switch(iMessage) {
	case WM_CHAR:
		len = strlen(str);
		str[len]=(TCHAR)wParam;
		str[len+1]=0;
		InvalidateRect(hWnd,NULL,FALSE);
		return 0;
	case WM_PAINT:
		hdc=BeginPaint(hWnd,&ps);
		TextOut(hdc,100,100,str,strlen(str));
		EndPaint(hWnd,&ps);
		return 0;
	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;
	}
	return(DefWindowProc(hWnd,iMessage,wParam,lParam));
}

실행 결과는 다음과 같다. 키보드를 누를 때마다 작업 영역이 무효화되며 눌러진 키가 화면으로 출력된다. 이처림 내부적인 변화에 의해 화면이 다시 그려저야 할 때 InvalidateRect 함수를 호출하여 화면을 무효화해 주어야 한다.

예제 2

다음 예제는 눌러진 키 하나만 출력한다.

LRESULT CALLBACK WndProc(HWND hWnd,UINT iMessage,WPARAM wParam,LPARAM lParam)
{
	HDC hdc;
	PAINTSTRUCT ps;
	static char ch[2];


	switch(iMessage) {
	case WM_CHAR:
		ch[0]=(TCHAR)wParam;
		InvalidateRect(hWnd,NULL,TRUE);
		return 0;
	case WM_PAINT:
		hdc=BeginPaint(hWnd,&ps);
		TextOut(hdc,100,100,ch,strlen(ch));
		EndPaint(hWnd,&ps);
		return 0;
	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;
	}
	return(DefWindowProc(hWnd,iMessage,wParam,lParam));
}

매번 키가 눌러질 때마다 (100,100)위치에 문자가 출력되므로 이전에 출력된 문자를 반드시 화면에서 삭제한 후 새 문자를 출력해야 한다. 그래서 bErase를 TRUE로 설정하여 키가 입력될 때마다 배경을 지우도록 하였다. 만약 bErase를 FALSE로 하여 배경을 지우지 않으면 이전에 출력된 문자가 지워지지 않아 두 개의 문자가 겹쳐서 나타날 수도 있다. 다음은 bErase를 FALSE로 설정하고 W와 J를 연속해서 눌러본 것이다.

먼저 출력된 W가 지워지지 않았기 때문에 J와 W가 겹쳐 보이게 된다. bErase를 TRUE로 바꾸면 이 문제를 해결할 수 있다.

예제 3

다음 예제는 InvalidateRect 함수의 두번째 인수인 lpRect를 테스트하는 예제이다. 타이머를 설치하고 1초에 한번씩 시간을 작업 영역에 출력하도록 하였다. 배경에는 바둑판 모양의 복잡한 그림이 그려져 있다.

LRESULT CALLBACK WndProc(HWND hWnd,UINT iMessage,WPARAM wParam,LPARAM lParam)
{
	HDC hdc;
	PAINTSTRUCT ps;
	static HANDLE hTimer;
	SYSTEMTIME st;
	static char sTime[128];
	RECT rt;
	int x,y;

	switch(iMessage) {
	case WM_CREATE:
		hTimer=(HANDLE)SetTimer(hWnd,1,1000,NULL);
		SendMessage(hWnd, WM_TIMER, 1, 0);
		return 0;
	case WM_TIMER:
		GetLocalTime(&st);
		wsprintf(sTime,"지금 시간은 %d:%d:%d입니다",
			st.wHour,st.wMinute,st.wSecond);
		SetRect(&rt,100,100,400,120);
		InvalidateRect(hWnd,&rt,TRUE);
		break;
		return 0;
	case WM_PAINT:
		hdc=BeginPaint(hWnd,&ps);
		for (x=0;x<800;x+=5) {
			MoveToEx(hdc,x,0,NULL);
			LineTo(hdc,x,600);
		}
		for (y=0;y<600;y+=5) {
			MoveToEx(hdc,0,y,NULL);
			LineTo(hdc,800,y);
		}
		TextOut(hdc,100,100,sTime,strlen(sTime));
		EndPaint(hWnd,&ps);
		return 0;
	case WM_DESTROY:
		KillTimer(hWnd,1);
		PostQuitMessage(0);
		return 0;
	}
	return(DefWindowProc(hWnd,iMessage,wParam,lParam));
}

WM_TIMER 메시지에서 InvalidateRect 함수를 호출하여 작업 영역을 무효화하되 시간이 출력되는 부분만 무효화함으로써 배경의 바둑판 모양은 다시 그려지지 않도록 했다. 실행중의 모습은 다음과 같다.

만약 InvalidateRect(hWnd,NULL,TRUE); 로 함수를 호출하면 작업 영역 전체가 무효화되고 바둑판 무늬 전체가 다시 지워졌다가 그려지므로 화면에 깜박임이 보이게 된다. 배경에 복잡한 그림이 있고 화면의 일부만 변경해야 할 경우는 lpRect에 필요한 최소한의 영역만 계산하여 꼭 필요한 부분만 다시 그리도록 해 주어야 한다.

참고함수 InvalidateRgn, ValidateRect
플렛폼 95이상
참조 무효 영역에 대해서는 4장, 12장을 참고한다.

written by http://www.winapi.co.kr