【转】对话框的OnPaint()和OnEraseBkgnd()消息的理解

对话框的OnPaint()和OnEraseBkgnd()消息的理解

一个对话框重写OnPaint()和OnEraseBkgnd(),执行发现OnEraseBkgnd()比OnPaint()执行的次数多很多,但是执行OnPaint()前一定会执行OnEraseBkgnd()。项目中用GDI双缓冲,绘制对话框背景图片的时候,放在OnEraseBkgnd()会出现闪烁,放在OnPaint()里面就不会闪烁。

这种问题关系到这两个消息函数的的理解:特定找了一些资料,以作备查。

个人理解:

OnEraseBkgnd()的return CDialogEx::OnEraseBkgnd(pDC),是擦除背景的,如果不想擦除背景,可以字节return true。

OnPaint()触发的情况:

       1. 移动窗口(movewindow()),窗口被其它窗口遮住的时候,把窗口最小化,再打开的时候。

       2. 你自己想更新某个区域可以发送Invalidate或InvalidRect,再加上UpdateWindow()也可以触发。

       注意:在OnPaint()里面不能执行再次触发WM_PAINT的语句,不然会死循环。

以下是别人的理解:摘自http://hi.baidu.com/yang_qi168/item/a648bec135bf1ec8ee183bcc

WM_PAINT是窗口每次重绘都会产生的一个消息。 
OnPaint是对这个消息的反应函数
mfc 的 CWnd::OnPaint 没做什么,只是丢给系统处理。

一 :
先执行OnEraseBkgnd,擦除背景(如果想自绘控件,这个函数直接return TRUE就可以了,这样就不会擦除背景,不会闪)

OnEraseBkGnd与OnPaint的区别与联系

在OnEraseBkGnd中,如果你不调用原来缺省的OnEraseBkGnd只是重画背景则不会有闪烁.而在OnPaint里面,由于它隐含的调用了OnEraseBkGnd,而你又没有处理OnEraseBkGnd 函数,这时就和窗口缺省的背景刷相关了.缺省的 OnEraseBkGnd操作使用窗口的缺省背景刷刷新背景(一般情况下是白刷),而随后你又自己重画背景造成屏幕闪动.

OnEraseBkGnd不是每次都会被调用的.如果你调用Invalidate的时候参数为TRUE,那么在OnPaint里面隐含调用BeginPaint的时候就产生WM_ERASEBKGND消息,如果参数是FALSE,则不会重刷背景.

void Invalidate( BOOL bErase = TRUE ); 该函数的作用是使整个窗口客户区无效。窗口的客户区无效意味着需要重绘,参数bErase为TRUE时,重绘区域内的背景将被重绘即擦除,否则,背景将保持不变。调用Invalidate等函数后窗口不会立即重绘,这是由于WM_PAINT消息的优先级很低,它需要等消息队列中的其它消息发送完后才能被处理。

OnPaint里面会调用BeginPaint函数自动设置显示设备内容的剪切区域而排除任何更新区域外的区域更新区域。如果更新区域被标记为可擦除的,BeginPaint发送一个WM_ERASEBKGND消息给窗口。WM_ERASEBKGND消息的响应函数既是OnEraseBkGnd()

所以解决方法有三个半:
1.用OnEraseBkGnd实现,不要调用原来的OnEraseBkGnd函数.
2.用OnPaint实现,同时重载OnEraseBkGnd,其中直接返回.
3.用OnPaint实现,创建窗口时设置背景刷为空
4.用OnPaint实现,但是要求刷新时用Invalidate(FALSE)这样
的函数.(不过这种情况下,窗口覆盖等造成的刷新还是要闪一
下,所以不是彻底的解决方法)
都挺简单的.
在MFC中 任何一個window元件的繪圖 都是放在這兩個member function中
在設定上 OnEraseBkgnd()是用來畫底圖的 而OnPaint()是用來畫主要物件的
舉例說明 一個按鈕是灰色的 上面還有文字
則OnEraseBkgnd()所做的事就是把按鈕畫成灰色
而OnPaint()所做的事 就是畫上文字

既然這兩個member function都是用來畫出元件的
那為何還要分OnPaint() 與 OnEraseBkgnd() 呢
其實OnPaint() 與 OnEraseBkgnd() 特性是有差的
1. OnEraseBkgnd()的要求是快速在裡面的繪圖程式最好是不要太耗時間
因為每當window元件有任何小變動都會馬上呼叫OnEraseBkgnd()
2. OnPaint() 是只有在程式有空閒的時候才會被呼叫
3. OnEraseBkgnd() 是在 OnPaint() 之前呼叫的
所以 OnPaint()被呼叫一次之前 可能會呼叫OnEraseBkgnd()好幾次

如果我們是一个在做图形化使用者界面的人
常会需要把一张图片设为我们dialog的背景
把绘图的代码放在OnPaint() 之中 可能会常碰到一些问题
比方說拖拽一个窗口在我们做的dialog上面一直移动
则dialog会变成灰色 直到动作停止才恢复
这是因为每次需要重绘的時候 程序都会马上呼叫OnEraseBkgnd()
OnEraseBkgnd()就把dialog画成灰色
而只有动作停止之后 程序才会呼叫OnPaint() 这时才会把我們要画的背景貼上去

这个问题的解法 比较差劲的方法是把OnEraseBkgnd() 改写成不做事的function
如下所示

BOOL CMyDlg::OnEraseBkgnd(CDC* pDC)
{
  return TRUE;
}

以上本来是会调用CDialog::OnEraseBkgnd() 但是如果我们不调用的話
程序便不会画上灰色的背景了

Q:基于对话框的程序中如何重载OnEraseBkGnd()函数

A:这是一个消息WM_ERASEBKWND 
在CLASS WIZARD中 
选择CLASSINFO页面 
在MESSAGEFILTER中的选项设在WINDOW就可以看到这个消息了.

比較好的做法是直接將繪圖的程式從OnPaint()移到OnEraseBkgnd()來做
如下所示

// m_bmpBKGND 為一CBitmap物件 且事先早已載入我們的底圖
// 底圖的大小與我們的視窗client大小一致

BOOL CMyDlg::OnEraseBkgnd(CDC* pDC)
{
    CRect rc;
    GetUpdateRect(&rc);
    CDC srcDC;
    srcDC.CreateCompatibleDC(pDC);
    srcDC.SelectObject(m_bmpBKGND);

    pDC->BitBlt(rc.left,rc.top,rc.GetWidth(),
    rc.GetHeight(),&srcDC,rc.left,rc.top,SRCCOPY);
    return TRUE;
}

特別要注意的是 取得重畫大小是使用GetUpdateRect() 而不是GetClientRect()
如果使用GetClientRect() 會把不該重畫的地方重畫

时间: 2024-08-11 09:48:42

【转】对话框的OnPaint()和OnEraseBkgnd()消息的理解的相关文章

Invalidate、OnPaint、OnEraseBkgnd函数

Invalidate void Invalidate( BOOL bErase = TRUE ); 该函数的作用是使整个窗口客户区无效.窗口的客户区无效意味着需要重绘,例如,如果一个被其它窗口遮住的窗口变成了前台窗口,那么原来被遮住的部分就是无效的,需要重绘.这时Windows会在应用程序的消息队列中放置WM_PAINT消息.MFC为窗口类提供了WM_PAINT的消息处理函数OnPaint,OnPaint负责重绘窗口.视图类有一些例外,在视图类的OnPaint函数中调用了OnDraw函数,实际的

白龙卫士+异步消息的理解

白龙卫士+异步消息的理解 手机卫士 MobileSafe MobileSafe 1.0 Splash界面的(设置一个渐变动画) 只加载计入界面,–>主页 根据功能模块划分 Activity com.itheima.mobilesafe.activty 后台服务 com.itheima.mobilesafe.service 广播接受者 com.itheima.mobilesafe.receiver 数据库 com.itheima.mobilesafe.db.dao 对象(java bean) co

WTL对话框应用程序响应键盘消息

修改对话框程序的PreTranslateMessage()消息 1 BOOL CMainDlg::PreTranslateMessage(MSG* pMsg) 2 { 3 int nCurSel; 4 if((nCurSel = m_wndOutputList.GetCurSel()) >= 0) 5 { 6 //WCHAR strSource[256]={0}; 7 if(WM_KEYDOWN == pMsg->message) 8 { 9 switch (pMsg->wParam)

结合windows消息系统理解C#中WndProc函数和DefWndProc函数

Windows消息系统由3部分组成: 1.消息队列.Windows应用程序的消息是由Windows统一在一个消息队列中管理的. 2.消息循环.应用程序从Windows消息队列中获得自己的消息,并将其分配给窗体函数进行处理. 3.窗口过程.负责处理接收到的消息,每个窗口都有对应的窗口过程,负责截获消息并响应.WndProc是窗口过程函数,负责处理接收到的消息,在我们写代码时,不会注意到有这个函数,这是因为开发环境自动为我们生成了.WndProc函数通过switch...case...判断并处理消息

消息队列理解笔记

消息队列解决的是将突发大量请求转换为后端能承受的队列请求,比如你的服务器一秒能处理100个订单,但秒杀活动1秒进来1000个订单,持续10秒,在后端能力无法增加的情况下,你可以用消息队列将总共10000个请求压在队列里,后台consumer按原有能力处理,100秒后处理完所有请求(而不是直接宕机丢失订单数据) 所谓队列,就是按照队首先出的规则建立的数据结构,消息队列就是根据消息到来后按照一定的规则进行排序,但一定是队首的消息先得到应答的队列.比如: public static String do

[00008]-[2015-08-21]-[00]-[Windows 程序设计 --- OnEraseBkGnd() OnPaint() DrawItem() 绘图处理]

问题是这样产生的.在OnEraseBkGnd中,如果你不调用原来缺省的OnEraseBkGnd只是重画背景则不会有闪烁.而在OnPaint里面,由于它隐含的调用了OnEraseBkGnd,而你又没有处理OnEraseBkGnd函数,这时就和窗口缺省的背景刷相关了.缺省的OnEraseBkGnd操作使用窗口的缺省背景刷刷新背景(一般情况下是白刷),而随后你又自己重画背景造成屏幕闪动.另外一个问题是OnEraseBkGnd不是每次都会被调用的.如果你调用Invalidate的时候参数为TRUE,那么

如何给对话框中的控件发送消息呢?Windows消息分类

以博文CTabCtrl中介绍的那样,给Tab添加子对话框来显示Tab内容.那么如果这个子对话框中含有个CTreeCtrl控件,有个Button控件,我想要模拟给这两个控件发送消息,该怎么办呢?直接把给控件的消息给控件容器(控件的父窗口)是没有用的.为什么呢?首先要明白windows的消息分类: Windows消息的分类 1. 标准消息(队列消息)除WM_COMMAND之外,所有以WM_开头的消息都是标准消息,如WM_MOUSEMOVE.WM_LBUTTONUP.WM_KEYDOWN.WM_C

C# MessageBox 消息对话框

在程序中,我们经常使用消息对话框给用户一定的信息提示,如在操作过程中遇到错误或程序异常,经常会使用这种方式给用于以提示.在C#中,MessageBox消息对话框位于System.Windows.Forms命名空间中,一般情况,一个消息对话框包含信息提示文字内容.消息对话框的标题文字.用户响应的按钮及信息图标等内容.C#中允许开发人员根据自己的需要设置相应的内容,创建符合自己要求的信息对话框. MessageBox消息对话框只提供了一个方法Show(),用来把消息对话框显示出来.此方法提供了不同的

对话框捕获WM_KEYDOWN消息

在对话框程序中,我们经常利用对话框上的子控件进行命令响应来处理一些事件,如果我们想要让对话框类本身(子控件的父窗口)来响应我们的按键消息,须利用ProcessMessageFilter虚函数来捕获对话框的WM_KEYDOWN消息: virtual BOOL ProcessMessageFilter( int code, LPMSG lpMsg ); 1.新建MFC工程:WinSun 2.在CWinSunApp类上右击添加成员变量:HWND m_hwndDlg 3.在CWinSunApp类的源文件