被ScrollWindow折腾出来的——关于区域、重画、无效、WM_PANIT等

本文探讨了ScrollWindow函数的参数和工作原理,分析了它如何影响窗口的重画过程。当ScrollWindow导致某些区域滚动消失,这些区域会变成无效区域,触发WM_PAINT消息,但并非所有区域都会重画。同时,文章讨论了UpdateWindow、BeginPaint、InvalidateRect等相关函数在重画和无效区域管理中的作用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 

=========================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。(见代码"测试三")
================================我的测试程序================================

#include  < windows.h >

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. 

 
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值