一个有点钻牛角尖的问题!!!!!!!

dronly 2009-07-28 04:44:47
一个基于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);

感谢高手们。
...全文
187 19 打赏 收藏 转发到动态 举报
写回复
用AI写文章
19 条回复
切换为时间正序
请发表友善的回复…
发表回复
dronly 2009-08-03
  • 打赏
  • 举报
回复
自己再顶一下。
dronly 2009-07-31
  • 打赏
  • 举报
回复
顶一下。
dronly 2009-07-30
  • 打赏
  • 举报
回复
首先,感谢 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造成的这个背景透明的效果,单单绘制一条线或者一个矩形还是合符理论的。
Lin 2009-07-30
  • 打赏
  • 举报
回复
Remarks:

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的解释,你自己看吧。你自己的理解不一定对。
dronly 2009-07-28
  • 打赏
  • 举报
回复
自己再顶一下~
dronly 2009-07-28
  • 打赏
  • 举报
回复
根据我最新的测试我把

//在界面上绘制
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);点击按钮,线条是存在的,虽然被其他窗口遮住再出现就消失了。
dronly 2009-07-28
  • 打赏
  • 举报
回复
[Quote=引用 12 楼 lin 的回复:]
你知道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%的时候是正确的
[/Quote]

首先感谢 Lin 的详尽回答

你说的第1点和第3点我非常赞同。

我的理解是InvalidateRect是告诉操作系统有一个无效区域需要更新。

然后还是操作系统去调用我们的窗口处理函数处理消息。

因此在我的理解,InvalidateRect里面并没有发送WM_PAINT而是设置窗口的图形数据结构。

是操作系统在发消息跟处理消息。

我在OnPaint里面加InvalidateRect是要欺骗操作系统说无效区德范围是窗口的整个客户区。

这样无论是我们主动外面调用InvalidateRect,还是窗口给临时遮盖,窗口的整个客户区都可以更新。

我其实也不想钻牛角尖,但这个关系到我对操作系统的消息运作结构的整体理解和思考,固此贴。
Lin 2009-07-28
  • 打赏
  • 举报
回复
你知道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%的时候是正确的
dronly 2009-07-28
  • 打赏
  • 举报
回复
自己顶一下。
dronly 2009-07-28
  • 打赏
  • 举报
回复
[Quote=引用 9 楼 jackson35296 的回复:]
使用InvalidateRect(NULL,true)的时候还会发送WM_ERASEBKGND消息,擦除背景,你可以试试在OnEraseBkgnd里处理下,直接返回TRUE或FALSE,不让其擦除
[/Quote]
感谢 jackson35296 的关注
根据你说的情况我尝试了一下,发现会触发两次的ERASEBKGND 但我的问题还是没有解决。

我的问题:
1,第二种情况是怎么产生的呢?为什么跟理论部一样
2. MSDN上说InvalidateRect(NULL,true);这个时候BeginPaint会清空界面,而很多书上有说系统默认的画刷是白色的,为什么这个时候Dialog的客户区还是系统窗口的颜色?
3.InvalidateRect(NULL,true); 如果屏蔽是否真的相当于 InvalidateRect(NULL,false);
jackson35296 2009-07-28
  • 打赏
  • 举报
回复
使用InvalidateRect(NULL,true)的时候还会发送WM_ERASEBKGND消息,擦除背景,你可以试试在OnEraseBkgnd里处理下,直接返回TRUE或FALSE,不让其擦除
dronly 2009-07-28
  • 打赏
  • 举报
回复
[Quote=引用 5 楼 lambochan 的回复:]
InvalidateRect( NULL, TRUE ) 把它移出OnPaint(),别处调用..
[/Quote]
首先感谢 lambochan 的关注
我试过做一个按钮,然后OnLButtonDown事件里面就只有
InvalidateRect( NULL, TRUE );
结果发现
1.InvalidateRect( NULL, FALSE); 跟InvalidateRect(NULL,true); 被屏蔽的时候,现象一样。
2.InvalidateRect(NULL,true); 跟InvalidateRect(NULL,true); 没被屏蔽的时候,现象一样。
dronly 2009-07-28
  • 打赏
  • 举报
回复
[Quote=引用 3 楼 shuigsls 的回复:]
OnPaint()
有两次绘画过程,第二次绘画把第一次覆盖了
[/Quote]

首先感谢 shuigsls 的关注,我觉得两次这个是有一定道理的,能说得再清楚一点么?
dronly 2009-07-28
  • 打赏
  • 举报
回复
[Quote=引用 4 楼 zhoujianhei 的回复:]
OnPaint()之前MFC已经BeginPaint(&ps)啦,你这里是多余的。

[/Quote]
首先感谢 zhoujianhei 的关注,我知道MFC在OnPaint里面有
BeginPaint(&ps);
EndPaint(&ps);
否则,OnPaint里面直接return 0;是会死循环的。

但我觉得这两步是在OnPaint之后的,
因为只要在OnPaint里面加ValidateRect(NULL);界面就没有被绘制了。这说明因为ValidateRect(NULL);后程序欺骗windows的更新区域已经消失,BeginPaint就没产生或者失效了。
lambochan 2009-07-28
  • 打赏
  • 举报
回复
InvalidateRect( NULL, TRUE ) 把它移出OnPaint(),别处调用..
zhoujianhei 2009-07-28
  • 打赏
  • 举报
回复
OnPaint()之前MFC已经BeginPaint(&ps)啦,你这里是多余的。

高山-流水 2009-07-28
  • 打赏
  • 举报
回复
OnPaint()
有两次绘画过程,第二次绘画把第一次覆盖了
dronly 2009-07-28
  • 打赏
  • 举报
回复
感谢 flei_gu 的关注,绘制的时间顺序不一样。这个我觉得还是有一定道理,这个涉及到TextOut的细节。
flei_gu 2009-07-28
  • 打赏
  • 举报
回复
以前好象遇到过这种情况,不过没有你那么执着。当时我给自己的解释是,绘制的时间顺序不一样。

19,468

社区成员

发帖
与我相关
我的任务
社区描述
VC/MFC 图形处理/算法
社区管理员
  • 图形处理/算法社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧