一个基于Dialog的测试程序的OpPaint部分,VC2005SP1,XP下编程void CTest2Dlg::OnPaint()
{
    //计算运行了多少次
    m_Counter++;    //转换成字符串缓冲
    TCHAR buf[10];
    memset(buf,0,10*sizeof(TCHAR));
    int a=_itot_s(m_Counter,buf,10);
    
    //InvalidateRect(NULL,true);  //我发现这个地方如果屏蔽跟不屏蔽是不一样的。    //在界面上绘制
    PAINTSTRUCT ps;
    BeginPaint(&ps);
    ::TextOut(ps.hdc,0,0,buf,(int)_tcslen(buf));
    EndPaint(&ps);    //下面是VC自动加的代码。
if (IsIconic())
{
    /*这里是VC自动加的代码*/
          }
else
{
CDialog::OnPaint();
}
}
我的想法是:
1.InvalidateRect(NULL,true); 如果屏蔽,本人试验的结果是相当于 InvalidateRect(NULL,false) ,Dialog客户区左上角输出数字自动增加而且白底黑字。
2.InvalidateRect(NULL,true); 不屏蔽,Dialog客户区左上角应该是什么都没有的。显示情况是:
1.InvalidateRect(NULL,true); 屏蔽的时候,Dailog客户区左上角的确有白底黑字的输出,这个跟理论情况一样。
2.InvalidateRect(NULL,true); 不屏蔽的时候,Dialog客户区左上角的是背景跟Dialog背景一样的,黑色数字的输出。
看上去有点背景透明的感觉。我的问题:
1,第二种情况是怎么产生的呢?为什么跟理论部一样
2. MSDN上说InvalidateRect(NULL,true);这个时候BeginPaint会清空界面,而很多书上有说系统默认的画刷是白色的,为什么这个时候Dialog的客户区还是系统窗口的颜色?
3.InvalidateRect(NULL,true); 如果屏蔽是否真的相当于 InvalidateRect(NULL,false);感谢高手们。

解决方案 »

  1.   

    感谢 flei_gu 的关注,绘制的时间顺序不一样。这个我觉得还是有一定道理,这个涉及到TextOut的细节。
      

  2.   

    OnPaint()
    有两次绘画过程,第二次绘画把第一次覆盖了
      

  3.   

    OnPaint()之前MFC已经BeginPaint(&ps)啦,你这里是多余的。
      

  4.   

    InvalidateRect( NULL, TRUE ) 把它移出OnPaint(),别处调用..
      

  5.   

    首先感谢 zhoujianhei 的关注,我知道MFC在OnPaint里面有
    BeginPaint(&ps);
    EndPaint(&ps);
    否则,OnPaint里面直接return 0;是会死循环的。但我觉得这两步是在OnPaint之后的,
    因为只要在OnPaint里面加ValidateRect(NULL);界面就没有被绘制了。这说明因为ValidateRect(NULL);后程序欺骗windows的更新区域已经消失,BeginPaint就没产生或者失效了。
      

  6.   


    首先感谢 shuigsls 的关注,我觉得两次这个是有一定道理的,能说得再清楚一点么?
      

  7.   

    首先感谢 lambochan 的关注
    我试过做一个按钮,然后OnLButtonDown事件里面就只有
    InvalidateRect( NULL, TRUE );
    结果发现
    1.InvalidateRect( NULL, FALSE); 跟InvalidateRect(NULL,true); 被屏蔽的时候,现象一样。
    2.InvalidateRect(NULL,true); 跟InvalidateRect(NULL,true); 没被屏蔽的时候,现象一样。
      

  8.   

    使用InvalidateRect(NULL,true)的时候还会发送WM_ERASEBKGND消息,擦除背景,你可以试试在OnEraseBkgnd里处理下,直接返回TRUE或FALSE,不让其擦除
      

  9.   

    感谢 jackson35296 的关注
    根据你说的情况我尝试了一下,发现会触发两次的ERASEBKGND 但我的问题还是没有解决。我的问题: 
    1,第二种情况是怎么产生的呢?为什么跟理论部一样 
    2. MSDN上说InvalidateRect(NULL,true);这个时候BeginPaint会清空界面,而很多书上有说系统默认的画刷是白色的,为什么这个时候Dialog的客户区还是系统窗口的颜色? 
    3.InvalidateRect(NULL,true); 如果屏蔽是否真的相当于 InvalidateRect(NULL,false); 
      

  10.   

    你知道InvalidateRect是干啥用的么?1、WM_PAINT消息是个处理级别非常低的消息,Windows会在Idle的时候处理,而且是有可能几个WM_PAINT被合并成一个,这样做的好处是显而易见的:提高代码效率,减少闪烁,虽然不一定有效。
    2、你在OnPaint里面调用InvalidateRect,而InvalidateRect本身会发送WM_PAINT消息,虽然该消息不一定会马上得到处理,这样的结果是未知的。但是从理论上说,嵌套调用的结果,可能是系统资源耗尽或者堆栈溢出。
    3、默认的话刷,对Windows来说,颜色是GeySystemColor(COLOR_WINDOW),对对话框来说,是GeySystemColor(COLOR_BTNFACE)
    4、InvalidateRect的第二个参数是确定是否需要擦出背景,这需要看你是怎么处理WM_ERASEBKGND消息的。不管擦出不擦出背景,都会导致发送WM_PAINT给窗口,屏蔽和不屏蔽效果显然是不一样的。不是钻牛角尖,这样的尖不值得钻,你其实仔细看看MSDN,就会明白你问的这几个问题。
    MSDN在99%的时候是正确的
      

  11.   

     首先感谢 Lin 的详尽回答你说的第1点和第3点我非常赞同。我的理解是InvalidateRect是告诉操作系统有一个无效区域需要更新。然后还是操作系统去调用我们的窗口处理函数处理消息。因此在我的理解,InvalidateRect里面并没有发送WM_PAINT而是设置窗口的图形数据结构。是操作系统在发消息跟处理消息。我在OnPaint里面加InvalidateRect是要欺骗操作系统说无效区德范围是窗口的整个客户区。这样无论是我们主动外面调用InvalidateRect,还是窗口给临时遮盖,窗口的整个客户区都可以更新。我其实也不想钻牛角尖,但这个关系到我对操作系统的消息运作结构的整体理解和思考,固此贴。
      

  12.   

    根据我最新的测试我把//在界面上绘制
    PAINTSTRUCT ps;
    BeginPaint(&ps);
    ::TextOut(ps.hdc,0,0,buf,(int)_tcslen(buf));
    EndPaint(&ps);这一段换成了//在界面上绘制
    PAINTSTRUCT ps;
    BeginPaint(&ps);
    CPoint pt;
    HPEN hPen=(HPEN)GetStockObject(BLACK_PEN);
    HPEN oldPen=(HPEN)::SelectObject(hdc,hPen);
    ::MoveToEx(hdc,0,0,&pt);
    ::LineTo(hdc,100,100);
    ::SelectObject(hdc,oldPen);
    EndPaint(&ps);发现,无论InvalidateRect(NULL,true);屏蔽不屏蔽,Dailog客户区左上角的都能画出线条来。但如果我添加一个按钮,LBUTTONDOWN里面添加代码
    CPoint pt;
    HDC hdc=::GetDC(m_hWnd);
    HPEN hPen=(HPEN)GetStockObject(BLACK_PEN);
    HPEN oldPen=(HPEN)::SelectObject(hdc,hPen);
    ::MoveToEx(hdc,0,0,&pt);
    ::LineTo(hdc,100,100);
    ::SelectObject(hdc,oldPen);
    ::ReleaseDC(m_hWnd,hdc);
    InvalidateRect(NULL,false);这个时候,OnPaint上的
    1.InvalidateRect(NULL,true); 如果屏蔽,点击按键,线条能够出来
    2.InvalidateRect(NULL,true); 不屏蔽,点击按键,线条没有出现在OnPaint上的InvalidateRect(NULL,true);屏蔽的情况下,把按钮LBUTTONDOWN上的
    1,改成InvalidateRect(NULL,true);点击按钮,看的出线条是先出来再消失的。
    2. 改回InvalidateRect(NULL,false);点击按钮,线条是存在的,虽然被其他窗口遮住再出现就消失了。
      

  13.   

    Res:The hWnd parameter cannot be NULL.The invalidated areas accumulate in the update region until the region is processed when the next WM_PAINT message occurs or until the region is validated by using the ValidateRect function.The system sends a WM_PAINTmessage to a window whenever its update region is not empty and there are no other messages in the application queue for that window.If the bErase parameter is TRUE for any part of the update region, the background is erased in the entire region, not just in the given part.这是MSDN的解释,你自己看吧。你自己的理解不一定对。
      

  14.   

    首先,感谢 Lin 的再次关注accumulate in the update region  这句表明InvalidateRect并没有发送WM_PAINT,而是累加设备描述表上的一个无效区域,因此WM_PAINT 还是操作系统发的消息。The system sends a WM_PAINTmessage to a window whenever its update region is not empty and there are no other messages in the application queue for that window. 
    这个表明系统在消息队列空下来的时候才发WM_PAINT 的,这表明非常的低优先级。第三段说的是背景擦除的时候是系统记录的整个无效区而不紧紧是传进去的参数上的Rect但很可惜的是MSDN没有提及这个刷除是在BeginPaint之前,之后,还是就是BeginPaint开始刷的。而且根据我后面的实验发现,是TextOut造成的这个背景透明的效果,单单绘制一条线或者一个矩形还是合符理论的。