MFC中改变对话框背景的几个消息函数OnEraseBkgnd、 OnPaint、 OnCtlColor的调用顺序

简介: 设置对话框背景颜色及背景图片可在OnCtlColor(),OnEraseBkgnd(),OnPaint()里设置,对话框初始化完毕,显示时调用OnSize()->OnEraseBkgnd(),->OnPaint()->OnCtlColor(), 若想改变对话框大小,比如全屏显示ShowWindo...
设置对话框背景颜色及背景图片可在OnCtlColor(),OnEraseBkgnd(),OnPaint()里设置,对话框初始化完毕,显示时调用OnSize()->OnEraseBkgnd(),->OnPaint()->OnCtlColor(),

若想改变对话框大小,比如全屏显示ShowWindow(SW_SHOWMAXIMIZED);UpdateWindow();

其中 ShowWindow会调用OnSize()->OnEraseBkgnd(),

        UpdateWindow();调用OnPaint()->OnCtlColor(),

      若对话框中没有设置消息响应OnEraseBkgnd(),,则系统默认消息响应OnEraseBkgnd()会调用OnCtlColor()设置对话框背景(即替代OnEraseBkgnd())

      对话框的背景设置可在OnCtlColor()中进行,因为OnCtlColor()一般会被多次调用,所以要想设置的CFont,CBrush等应在OnInitDialog中初始化,若要在OnCtlColor()中设置,在设置前先调用Detach就可以了,如下示例

HBRUSH CDb3Dlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
{

if(pWnd->GetDlgCtrlID()==IDC_STATIC5)
   {
     
    m_font.CreatePointFont(300,"宋体");
    pDC->SelectObject(&m_font);
    m_font.Detach();            
    pDC->SetBkMode(TRANSPARENT);  
    return (HBRUSH)::GetStockObject(NULL_BRUSH);      
   }

}

但是如果在OnCtlColor()在设置背景图片,则图片不会随对话框大小按比例缩放

所以可调用StretchBlt()函数设置,如下示例:

void CDb3Dlg::OnPaint()
{

CClientDC cdc(this); CDC comdc;
comdc.CreateCompatibleDC(&cdc);
CBitmap bitmap;
bitmap.LoadBitmap(IDB_BITMAP2);
comdc.SelectObject(&bitmap);
CRect rect;
GetClientRect(rect);
BITMAP bit;
bitmap.GetBitmap(&bit);
cdc.StretchBlt(0,0,rect.Width(),rect.Height(),&comdc,0,0,bit.bmWidth,bit.bmHeight,SRCCOPY);

}//全屏显示对话框背景图片(限bmp格式)

    用了两年的VC,其实对OnPaint的工作原理一直都是一知半解。这两天心血来潮,到BBS上到处发帖询问,总算搞清楚了,现在总结一下。

     对于窗口程序,一般有个特点:窗口大部分的区域保持不变,只有不分区域需要重新绘制。如果将整个窗口全部刷新的画,就做了许多不必要的工作,因而,MFC采用了一套基于无效区的处理机制。在分析无效区处理之前,我们要明白一个现实,现在的机器还不够牛,如果够牛的话,我们干脆将整个窗口不断的重新绘制好了。事实上即使够牛也不行,对于一个单线程程序,通过一个while循环不断的刷新窗口,程序也无法相应其他消息(除非使用多线程),看来使用无效区的处理机制还是有其必然性的。

     VC程序是基于消息机制的,你所做的任何操作,比如点击鼠标,拖动窗口,首先进入系统的消息队列。这里的系统消息队列包括多个程序的消息,系统再将消息发送给相应的程序。既然是队列,这就有一个先进先出的问题,屏幕上的无效区更新消息出现的频率就会特别高。比如当左上角更新的消息还没有处理,右下角更新的消息已经过来了。为了避免多次处理WM_PAINT消息,系统就将这些窗口更新消息合并到一条,只是将无效区范围变成包括这两次更新无效区范围在内的矩形区域。这样就减少了WM_PAINT消息的处理次数,提高了效率。

     那么,在OnPaint消息处理函数中,又是怎样实现更新无效区的呢?首先,要明白MFC中所有绘图操作都是基于设备描述表(Device Context,简称DC)的,具体信息可参看任何一本VC教材。DC中包含了绘图设备的各种信息,对于屏幕绘图,其实就是有一块内存(显存),专门用来存放要显示到屏幕上的信息,显示器以85HZ的频率(我以前的显示器)将其内容刷新的屏幕上。这里就到了关键点,显示器的刷新是将显存中的内容完全更新到显示器上,不存在无效区处理的问题,那么,无效区的处理一定发生在DC的绘图处理上。事实确实如此,当程序调用OnPaint消息时,首先将无效区范围传递给DC,DC在进行绘图操作时,就只更新无效区范围内的信息,其他地方的不管,这就提高了效率。开启OnPaint函数有下面三种选择:

1)  直接发送WM_PAINT消息,用PostMessage(),SendMessage()函数发送WM_PAINT消息。使用以上两函数发送WM_PAINT消息,能将WM_PAINT消息发送到WINDOWS程序消息队列中,当WINDOWS将WM_PAINT消息发送给具体的消息处理函数时,如果窗口的无效区域为空则WINDOWS将不理睬该消息。若存在无效区域,则调用窗口处理函数处理。要注意的这里需要存在无效区域,因此要调用2)中的函数使得窗体(或者部分)无效,其处理过程与2)相同,将WM_PAINT消息送入消息处理队列。与3)不同的是WM_PAINT并不立即处理;

2)  调用相应的API实现WM_PAINT消息的发送:Invalidate(),InvalidateRect(), InvalidateRgn():以上函数将窗口的特定区域标定为无效,当WINDOWS检测到窗口中存在无效区域时将向消息队列发送WM_PAINT 消息。我当时用的就是Invalidate()函数;

3)  UpdateWindow():该函数调用后WINDOWS将向窗口发送一个非队列化的WM_PAINT消息,它不经过消息循环而直接发送给了窗口消息处理函数。如果窗口无效区域不存在,WINDOWS将不理睬该消息。注意这里因为要使得窗口无效区不存在,因此还是调用Invalidate(),InvalidateRect(), InvalidateRgn()函数,和2)中不同的是这里的WM_PAINT消息会被立即处理,而2)中是加入消息处理队列。

简单起见,你可以使用2)中方案进行问题解决。

     现在你明白OnPaint的处理是怎么一回事了吧?这里还想说一下Invalidate和UpdateWindow的区别。Invalidate在消息队列中加入一条WM_PAINT消息,其无效区为整个客户区。而UpdateWindow直接发送一个WM_PAINT消息,其无效区范围就是消息队列中WM_PAINT消息(最多只有一条)的无效区。效果很明显,调用Invalidate之后,屏幕不一定马上更新,因为WM_PAINT消息不一定在队列头部,而调用UpdateWindow会使WM_PAINT消息马上执行的,绕过了消息队列。如果你调用Invalidate之后想马上更新屏幕,那就加上UpdateWindow()这条语句。

目录
相关文章
|
7月前
Qt3个窗口类的区别
一个普通的窗口,不包含菜单栏、状态栏,除了登录界面,新建项目时建议使用QWidget,因为大部分的窗口可能都要做成无边框窗口,需要自定义标题栏,实现拉伸等;QMainWindow使用的场景不多。:对话框,常用来做登录窗口、弹出窗口(例如设置界面):包含菜单栏、工具栏、状态栏。
63 0
|
6月前
|
前端开发
自定义QT QWidget对象响应弹出菜单的解决方法
自定义QT QWidget对象响应弹出菜单的解决方法
102 1
|
C# 数据安全/隐私保护
C# 窗体之间参数互相传递的两种方法与使用
C# 窗体之间参数互相传递的两种方法与使用
Qt按钮对象和一些函数
Qt按钮对象和一些函数
208 0
|
安全 JavaScript API
【笔记】用 window.postMessage 进行窗口之间的隐式信息传递
用 window.postMessage 进行窗口之间的隐式信息传递
350 0
【QT】解决QT两个类之间传递变量失败,或者extern变量引用无效。
【QT】解决QT两个类之间传递变量失败,或者extern变量引用无效。
MFC怎么用代码改变CComboBox的下拉方式?
MFC怎么用代码改变CComboBox的下拉方式?
169 0
vc里面怎样实现对话框之间传递变量的值
Dialog1的类名是CDialog1, 头文件是dialog1.h。里有成员变量CString str1, str2;Dialog2的类名是CDialog2, 头文件是dialog2.h。里有成员变量CString str11, str22;要想将Dialog1里的变量传递给Dialog2里的变量...
819 0
|
C# C++
WPF附加属性的Set函数不调用的问题
原文:WPF附加属性的Set函数不调用的问题 今天写程序的时候用到了附加属性,我是用VS内置的propa的代码段来实现的,代码如下:     class Attach    {        public static bool GetIsEnabl...
854 0