=========================ScrollWindow原型========================
BOOL ScrollWindow(
HWND hWnd,
int XAmount,
int YAmount,
const RECT *lpRect,
const RECT *lpClipRect
);
参数:
xAmount 指定了水平滚动的量,使用设备单位。在左滚时,该参数必须为负。 yAmount 指定了垂直滚动的量,使用设备单位。在上滚时,该参数必须为负。
lpRect 指向一个RECT结构,指定了要滚动的客户区的部分。假如lpRect为NULL,则将滚动整个客户区。假如光标区域与滚动矩形重叠,则插字符将被重定位。
lpClipRect 指向一个RECT结构,指定了要滚动的裁剪区域。只有这个矩形中的位才会被滚动。在矩形之外的位不会被影响,即使它们是在lpRect矩形之内。(见代码"测试一")假如lpClipRect为NULL,则不会在滚动矩形上进行裁剪。(见代码"测试二")
综上,一般将lpRect和lpClipRect指向同一个RECT。(见代码"测试三")
================================我的测试程序================================
LRESULT CALLBACK WndProc(HWND,UINT,WPARAM,LPARAM);
int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE yY,PSTR szCmdLine, int iCmdShow)
{
static TCHAR szWndClassName[] = TEXT( " yy " );
HWND hwnd;
MSG msg;
WNDCLASS wndclass;
wndclass.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
wndclass.lpfnWndProc = WndProc;
wndclass.cbClsExtra = 0 ;
wndclass.cbWndExtra = 0 ;
wndclass.hInstance = hInstance;
wndclass.hIcon = LoadIcon(NULL,IDI_APPLICATION);
wndclass.hCursor = LoadCursor(NULL,IDC_ARROW);
wndclass.hbrBackground = CreateSolidBrush ( RGB( 212 , 208 , 200 ) ) ;
wndclass.lpszMenuName = NULL;
wndclass.lpszClassName = szWndClassName;
if ( ! RegisterClass( & wndclass)){
MessageBox(NULL,TEXT( " This program requires Windows NT! " ),szWndClassName,MB_ICONERROR);
return 0 ;
}
hwnd = CreateWindow(szWndClassName,
TEXT( " ScrollWindow()函数 " ),
WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX,
CW_USEDEFAULT,
CW_USEDEFAULT,
300 ,
300 ,
NULL,
NULL,
hInstance,
NULL);
ShowWindow(hwnd,iCmdShow);
UpdateWindow(hwnd);
while ( GetMessage( & msg,NULL, 0 , 0 ) ){
TranslateMessage( & msg);
DispatchMessage( & msg);
}
return msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam){
HDC hdc;
PAINTSTRUCT ps;
static RECT rectScroll;
rectScroll.left = 50 ;
rectScroll.top = 50 ;
rectScroll.right = 220 ;
static RECT clipRectScroll;
TCHAR text[ 1000 ];
static LOGFONT lf; static HFONT hFont; static int cxText, cyText ;
switch (message){
case WM_CREATE:
// 字体:
lf.lfWeight = FW_LIGHT;
lf.lfCharSet = GB2312_CHARSET;
lf.lfHeight = 13 ;
hFont = CreateFontIndirect( & lf);
hdc = GetDC(hwnd);
SelectObject (hdc, GetStockObject(NULL_BRUSH));
SelectObject(hdc,hFont);
SetBkMode (hdc, TRANSPARENT) ;
TEXTMETRIC tm ;
GetTextMetrics (hdc, & tm) ;
cxText = tm.tmAveCharWidth ;cyText = tm.tmHeight ; // + tm.tmExternalLeading ;
rectScroll.bottom = rectScroll.top + 12 * cyText;
clipRectScroll.top = rectScroll.top + 3 * cyText;
clipRectScroll.bottom = rectScroll.top + 9 * cyText;
clipRectScroll.left = rectScroll.left + 2 * cxText;
clipRectScroll.right = rectScroll.right - 2 * cxText;
ReleaseDC(hwnd,hdc);
return 0 ;
case WM_KEYUP:
// ScrollWindow (hwnd, 0, -cyText, &rectScroll ,&clipRectScroll) ; // 测试一
// ScrollWindow (hwnd, 0, -cyText, &rectScroll ,0) ; // 测试二
ScrollWindow (hwnd, 0 , - cyText, & rectScroll , & rectScroll) ; // 测试三
return 0 ;
case WM_PAINT:
hdc = BeginPaint(hwnd, & ps);
static int tt,xx,yy,zz; // ScrollWindow的确产生了WM_PAINT消息!
TextOut(hdc, 0 , 0 ,text,wsprintf (text, TEXT ( " BOOL ScrollWindow( //tt:%d次 " ), ++ tt) );
TextOut(hdc, 0 ,cyText,text,wsprintf (text, TEXT ( " HWND hWnd,int XAmount,int YAmount, " ) ) );
TextOut(hdc, 0 , 2 * cyText,text,wsprintf (text, TEXT ( " const RECT *lpRect,const RECT *lpClipRect " ) ) );
TextOut(hdc, 0 , 3 * cyText,text,wsprintf (text, TEXT ( " ); " ) ) );
Rectangle(hdc,rectScroll.left - 1 , rectScroll.top - 1 , rectScroll.right + 1 , rectScroll.bottom + 1 ) ;
Rectangle(hdc,clipRectScroll.left - 1 ,clipRectScroll.top - 1 ,clipRectScroll.right + 1 ,clipRectScroll.bottom + 1 ) ;
DrawText (hdc, text, wsprintf (text, TEXT ( "
111111111111 222222222222222222222222
333333333333333333333333
444444444444444444444444
555555555555555555555555
666666666666666666666666
777777777777777777777777
888888888888888888888888
999999999999999999999999
10 10 10 10 10 10 10 10
11 11 11 11 11 11 xx: % d次
12 12 12 12 12 12 yy: % d次
测试三:lpRect是外框,lpClipRect是外框
任意键测试滚动效果 zz: % d次 " ),++xx,++yy,++zz),
& rectScroll,DT_NOCLIP);
EndPaint(hwnd, & ps);
return 0 ;
case WM_DESTROY:
PostQuitMessage( 0 );
DeleteObject (hFont);
return 0 ;
}
return DefWindowProc(hwnd,message,wParam, lParam) ;
}
================================以下为问题和结论================================
ScrollWindow的确产生WM_PAINT消息,产生WM_PAINT消息的确执行了case WM_PAINT:中的全部代码,但是ScrollWindow函数所影响的区域之外的区域并不"重画"(眼睛可见),也就是说执行case WM_PAINT:中的全部代码并不等于你眼睛看到的界面整个发生了变化--称为"没有全部重画"。
然后针对我的程序 还有个细节要问:
XX的值的确在变 因为只要产生WM_PAINT消息就执行了case WM_PAINT:中的全部代码!
但 为什么同样是在 rectScroll矩形内,YY在重画,XX没重画?
答:是不是这样!因为我的程序是向上滚动一行,那最下面的一行呢?滚到上面去了!下面就米有了!被"擦除"了,成了无效区域,于是仅这一行的区域马上就被重画。
还有,关于UpdateWindow(hwnd);除了CreateWindow时画出窗口,其他时候究竟有什么用?怎么我在ScrollWindow函数后加上 UpdateWindow(hwnd);后运行我的程序我的眼睛没有看到任何的不同?
答:UpdateWindow本来就是发送一个 WM_PAINT,效果和加没加一样--Windows不会将多个WM_PAINT消息都放在消息队列中。
总的来说,ScrollWindow本来是不产生无效区域的,所以除了仅仅滚动外并没有重画,但由于滚动的时候有的地方会滚到别处而原区域就被滚"不见"了,成了无效区域,于是,在ScrollWindow产生的WM_PAINT消息中,重画了此区域。
================================相关知识================================
1、呼叫了BeginPaint之后,整个显示区域即变为有效。
2、ValidateRect函数使显示区域内的任意矩形区域变为有效。如果这呼叫具有令整个无效区域变为有效的效果,则目前队列中的任何WM_PAINT消息都将被删除。
3、关于InvalidateRect
BOOL InvalidateRect(
HWND hWnd, // handle to window
CONST RECT* lpRect, // rectangle coordinates
BOOL bErase // erase state
);//该函数使lpRect的矩形区域失效,并产生一个WM_PAINT消息。如果lpRect为NULL,则整个客户区域需要重画。bErase specifies whether the background within the update region is to be erased when the update region is processed. If this parameter is TRUE, the background is erased when the BeginPaint function is called. If this parameter is FALSE, the background remains unchanged.
(1或N个)失效区域将累积起来(成为一个“update region”)等待更新,直到 WM_PAINT 发生,或者通过 ValidateRect or ValidateRgn 函数使这个update region变成“有效的”。If the bErase parameter is TRUE for any part of the “update region”, the background is erased in the entire “update region”(注意,不是“entire client area”), not just in the specified part.