关于窗口重绘的问题

vanssan 2010-07-28 10:33:12
我做了一个对话框程序,添加了一个绘图成员函数DrawCurrentNote(),此函数中根据接收的消息在一个矩型框中加载图片,图片是由很多小图片组成的,为了减少刷新频率,我只有当有消息到时才调用DrawCurrentNote(),而不是在OnPaint中调用该函数,现在有个问题,当有别的程序占用该窗口的这部分矩型区域时,图片会消失,只有当我再做一个按钮点击,触发DrawCurrentNote()函数时才会把图片加载上去。但这样太被动,请问下有没有什么好办法,当有别的程序占用该窗口后,当再次用这程序时进行DrawCurrentNote()。在OnPaint()中直接调用DrawCurrentNote()有问题,因为我在对话框中还有张bmp的背景图片,每次OnPaint()重绘时连背景图片也会一起刷新。
...全文
680 21 打赏 收藏 转发到动态 举报
写回复
用AI写文章
21 条回复
切换为时间正序
请发表友善的回复…
发表回复
xxd_qd 2010-07-28
  • 打赏
  • 举报
回复
对话框背景图片的显示放在OnEraseBkgnd中,而不要放在OnPaint中。每次调用InvalidateRect的时候,只对你需要的那个小矩形进行(如果某次需要更新两个以上的小矩形,就多次调用InvalidateRect),而不要直接传参数NULL来更新整个客户区。InvalidateRect的第三个参数一定要FALSE。

窗口被覆盖然后移开,系统自动会调用InvalidateRect,不用你管。
secretcf 2010-07-28
  • 打赏
  • 举报
回复
消息驱动InvalidateRect。
zxdlms 2010-07-28
  • 打赏
  • 举报
回复
窗口被覆盖然后移开怎么使用InvalidateRect呢?

我觉得还是消息到了就调用DrawCurrentNote()等函数绘制,然后在OnPaint里用双缓冲再把所有显示,你的背景,DrawCurrentNote之类的重绘一下就行了。这样重绘的时候并不多啊,除非你非要拿个其它窗口在它上面移来移去。
na_he 2010-07-28
  • 打赏
  • 举报
回复
[Quote=引用 1 楼 fishion 的回复:]
就放到OnPaint()中就行了,刷新的时候就用InvalidateRect好了
[/Quote]

这种做法就可以了,不必考虑刷新多次的问题,系统会处理的。你每次发消息出发绘制容易出现问题。
secretcf 2010-07-28
  • 打赏
  • 举报
回复
[Quote=引用 1 楼 fishion 的回复:]

就放到OnPaint()中就行了,刷新的时候就用InvalidateRect好了
[/Quote]

InvalidateRect不会导致背景图片被刷。
vanssan 2010-07-28
  • 打赏
  • 举报
回复
我是这样想的,当有别的程序覆盖时,定义一个变量为1,当此程序重新获得顶层窗口时调用函数,在函数里判断该值是否为1,当为1时调用DrawCurrentNote()等函数重绘,但这样应该怎么样实现?
vanssan 2010-07-28
  • 打赏
  • 举报
回复
[Quote=引用 1 楼 fishion 的回复:]
就放到OnPaint()中就行了,刷新的时候就用InvalidateRect好了
[/Quote]
我试过,会出现每次OnPaint的时候连背景图片一起刷新,这样看起来不好。并且刷新频率很高,因为我那个对话框上有很多这样的矩型区域,不同的消息有不同的绘图函数,DrawCurrentNote(),只是其中一个。
fishion 2010-07-28
  • 打赏
  • 举报
回复
就放到OnPaint()中就行了,刷新的时候就用InvalidateRect好了
xxd_qd 2010-07-28
  • 打赏
  • 举报
回复
另外,也许你已经改烦了,不过OnActivate这种办法肯定不行。也许我前面说得复杂了一点,其实你只要按下述步骤试试就知道了:
1、运行你的程序。
2、运行Windows的计算器。
3、抓住计算器的窗口在你的程序窗口上方擦来擦去。
你看看会是什么结果?
xxd_qd 2010-07-28
  • 打赏
  • 举报
回复
DrawCurrentNote的参数肯定应该是&dc,而不会是dc。

背景不能在对话框资源中用Picture静态加载,因为Picture是Static型的控件(子窗口),它永远放置于父窗口之上,因此一定会挡住你在父窗口中所画的一切东西,除非你把所画的那些小矩形也统统改为Picture,不过这样一来,各个Picture之间的绘制顺序你就更无法掌握了。相互重叠的控件如何才能保证正确的绘制顺序是最令人头疼的问题之一。

OnEraseBkgnd没什么好了解的,你只要在对话框的中增加对事件WM_ERASEBKGND的响应,VC自动就会为你添加这个函数,你只要在函数体内填写好要做的事情就好(在你的程序中,就是把背景图画一遍)。
vanssan 2010-07-28
  • 打赏
  • 举报
回复
[Quote=引用 18 楼 xxd_qd 的回复:]
引用 17 楼 vanssan 的回复:
DrawCurrentNote()我用的是CCLientDC绘图。用多线程的原因是当切换程序时响应OnActivate()消息时,要先进行控件等的重绘,我在多线程中停了0.1秒等对话框其它图片和控件加载完后才绘图。这只能用多线程处理。不用多线程还是会出现先调用DrawCurrentNote绘图,然后在绘制对话框控件和加载背景图片。

哦?假若前端本来……
[/Quote]
现在整个框架都已经差不多了,我也是在其它人写的程序框架上改的,暂时这样不想改了,不过还是要谢谢你给我说了这么多,如果在OnPaint中是不是这样写:
void CDT_SeverDlg::OnPaint()
{
if (IsIconic())
{
CPaintDC dc(this); // 用于绘制的设备上下文

SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);

......

// 绘制图标
dc.DrawIcon(x, y, m_hIcon);

}
else
{
CPaintDC dc(this);
DrawCurrentNote(dc);

}

}
DrawCurrentNote(dc)函数用这个dc写文字或画图。背景图片如果是在对话框资源中用pricture静态加载的话要不要处理背景图片?现在对OnEraseBkgnd还不太了解。
xxd_qd 2010-07-28
  • 打赏
  • 举报
回复
[Quote=引用 17 楼 vanssan 的回复:]
DrawCurrentNote()我用的是CCLientDC绘图。用多线程的原因是当切换程序时响应OnActivate()消息时,要先进行控件等的重绘,我在多线程中停了0.1秒等对话框其它图片和控件加载完后才绘图。这只能用多线程处理。不用多线程还是会出现先调用DrawCurrentNote绘图,然后在绘制对话框控件和加载背景图片。
[/Quote]
哦?假若前端本来是一个占用巨大内存的程序,当切换回你的程序时,你的背景图及控件加载可能需要花1秒以上的时间来进行(因为需要到硬盘上进行虚拟内存交换),你等待0.1秒有什么用?
再有,你的程序是在WM_ACTIVE的时候重画,那假定你的程序窗口与另一个程序(比如记事本)窗口在桌面上并列摆放(相互不覆盖),然后打开一个程序把你的窗口挡住,再把该程序关掉,此时被设置为活动的窗口可能是记事本,而不是你的程序,因此你并不会重画,但你的窗口却已经露出来了,这时候你怎么办?系统处理何时重画都是通过WM_PAINT进行的,你不用这个来做,用任何其它方法都会有漏洞。

再说一遍:在WM_PAINT中进行绘图,必须用CPaintDC或者直接调用SDK的BeginPaint,因为这里面系统已经做了各种工作,包括确定绘制的时机、范围和顺序(你非不肯用这个,当然会存在绘图与背景顺序相反的问题),等等。这都是系统已经替你做好的,你硬要自己来的话,那么你就需要把相当数量的系统代码全都用自己的程序重新写一遍,才能做到适应任何情况。
vanssan 2010-07-28
  • 打赏
  • 举报
回复
[Quote=引用 15 楼 xxd_qd 的回复:]
引用 13 楼 vanssan 的回复:
OnPaint()中我就在esle中注释CDialog::OnPaint();写上DrawCurrentNote().

嗯?没有声明CPaintDC变量?那么你的DrawCurrentNote里面DC是怎么获得的?我前面不是说了吗?在WM_PAINT消息的响应中,必须用CPaintDC变量来绘图,或者直接用SDK中的BeginPaint来获得HDC……
[/Quote]
DrawCurrentNote()我用的是CCLientDC绘图。用多线程的原因是当切换程序时响应OnActivate()消息时,要先进行控件等的重绘,我在多线程中停了0.1秒等对话框其它图片和控件加载完后才绘图。这只能用多线程处理。不用多线程还是会出现先调用DrawCurrentNote绘图,然后在绘制对话框控件和加载背景图片。
vanssan 2010-07-28
  • 打赏
  • 举报
回复
[Quote=引用 14 楼 vnking 的回复:]
楼主的程序架构可能需要调整一下,画界面只能放进OnPaint里,其他东东适应它吧。
[/Quote]
为什么呢?当要重绘时我调用RedrawWindow(&rctt);在调用DrawCurrentNote();绘图函数,效果应该是一样的。我的框架是有点问题,在VC2005下编译后,点调试运行后,关闭程序会出现一个内存错误,只能进任务管理器关掉,而进入文件点图标运行就不会出现这样的问题。
xxd_qd 2010-07-28
  • 打赏
  • 举报
回复
[Quote=引用 13 楼 vanssan 的回复:]
OnPaint()中我就在esle中注释CDialog::OnPaint();写上DrawCurrentNote().
[/Quote]
嗯?没有声明CPaintDC变量?那么你的DrawCurrentNote里面DC是怎么获得的?我前面不是说了吗?在WM_PAINT消息的响应中,必须用CPaintDC变量来绘图,或者直接用SDK中的BeginPaint来获得HDC,其它方法(比如GetDC之类)都不行。

你的那段代码并不合理,切换程序的时候,即使别的窗口没有遮挡你,你也要全部重画一遍,另外,为什么用线程?难道是因为绘制过程花时间很长,你不希望窗口停止响应?那问题又来了:假若窗口被快速切换两遍,你的第一次绘制线程还没有退出的时候,第二次绘制又开始,此时两个线程同时要使用DC绘图,你的程序崩溃或者出现奇怪现象的可能性很大。没有足够的理由和必要,不要随意使用线程

vnking 2010-07-28
  • 打赏
  • 举报
回复
楼主的程序架构可能需要调整一下,画界面只能放进OnPaint里,其他东东适应它吧。
vanssan 2010-07-28
  • 打赏
  • 举报
回复
[Quote=引用 12 楼 xxd_qd 的回复:]
切换程序的时候先调用DrawCurrentNote,然后才刷新对话框背景?把你的OnPaint和OnEraseBkgnd的全套代码贴上来看看。
[/Quote]
OnPaint()中我就在esle中注释CDialog::OnPaint();写上DrawCurrentNote().
不过我用另外一种方法解决了,在OnActivate中写入
CDialog::OnActivate(nState, pWndOther, bMinimized);
if(LOWORD(nState)!=WA_INACTIVE)
{
AfxBeginThread(DrawProc,(LPVOID)this);

}
else
{
// application is being deactivated
}
在多线程中调用绘图等函数
UINT DrawProc(LPVOID pWnd)
{
Sleep(100);
CDT_SeverDlg *pDlg=(CDT_SeverDlg*)pWnd;
pDlg->DrawCurrentMac();
pDlg->DrawCurrentNote();
pDlg->DrawCurrentManager();
return 0;
}
xxd_qd 2010-07-28
  • 打赏
  • 举报
回复
切换程序的时候先调用DrawCurrentNote,然后才刷新对话框背景?把你的OnPaint和OnEraseBkgnd的全套代码贴上来看看。
vanssan 2010-07-28
  • 打赏
  • 举报
回复
[Quote=引用 10 楼 xxd_qd 的回复:]
对话框重载OnPaint时,除非自己什么都不做,否则不能调用基类的OnPaint。因为要在OnPaint中绘制界面,就必须声明一个CPaintDC类型的变量,并且使用这个变量来绘制,而不能使用GetDC之类的函数来获取DC。这是在OnPaint中绘制与在其它代码中绘制所不同的地方。
实际上,如果你熟悉SDK的话,就会知道,响应WM_PAINT消息,必须以调用::BeginPaint开头,以::E……
[/Quote]
还是不行,当我切换程序时,先调用DrawCurrentNote(),然后是Dialog对话框控件,背景什么的在刷新,然后覆盖掉了DrawCurrentNote()绘出的图型。并且我用鼠标单击图片可修改图片时,图片大小为16*16象素,刷新太慢。
xxd_qd 2010-07-28
  • 打赏
  • 举报
回复
对话框重载OnPaint时,除非自己什么都不做,否则不能调用基类的OnPaint。因为要在OnPaint中绘制界面,就必须声明一个CPaintDC类型的变量,并且使用这个变量来绘制,而不能使用GetDC之类的函数来获取DC。这是在OnPaint中绘制与在其它代码中绘制所不同的地方。
实际上,如果你熟悉SDK的话,就会知道,响应WM_PAINT消息,必须以调用::BeginPaint开头,以::EndPaint结尾。所有绘制窗口的操作必须在这两者之间完成(CPaintDC的作用就是:其构造函数调用了::BeginPaint,析构函数调用了::EndPaint,所以就不用显式地调用这两个函数)。像你上面的代码,先调用CDialog::OnPaint(),而在这个过程中,早已经调用完成了::EndPaint,再此之后的一切绘制函数都是无效的,出现奇怪结果也就不奇怪了。这就是为什么在你自己的OnPaint函数中部能调用任何基类OnPaint的原因。

解决方案:
1、把CDialog::OnPaint()删掉,改成CPaintDC dc(this);
2、修改DrawCurrentNote,让它接受一个CDC *的参数,然后在调用的时候把&dc传进去。
2、把DrawCurrentNote的调用放到else块内部(IsIconic的时候还有什么好画的?)。
加载更多回复(1)

15,979

社区成员

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

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