精华内容
下载资源
问答
  • MFC 窗体重绘

    千次阅读 2015-06-02 11:44:00
    MFC 窗体重绘 ���¼���ԭʼ��ҳ��ӡ Windows的窗口刷新机制相关 - 在水一方 - 博客频道 1、Windows的窗口刷新管理 窗口句柄(HWND)都是由操作系统内核管理的,系统内部有一个...

    MFC 窗体重绘

    Windows的窗口刷新机制相关 - 在水一方 - 博客频道

    1、Windows的窗口刷新管理

    窗口句柄(HWND)都是由操作系统内核管理的,系统内部有一个z-order序列,记录着当前窗口从屏幕底部(假象的从屏幕到眼睛的方向),到屏幕最高层的一个窗口句柄的排序,这个排序不关注父窗口还是子窗口。

    当任意一个窗口接收到WM_PAINT消息产生重绘,更新区域绘制完成以后,就搜索它的前面的一个窗口,如果此窗口的范围和更新区域有交集,就向这个窗口发送WM_PAINT消息,周而复始,直到执行到顶层窗口。才算完成。

    1.1 父子窗口间的刷新管理

    对于一个对话框(主窗口)来说,理论上其所有子窗口都在他的前面——也就是更靠近眼睛的位置),当主窗口接收WM_PAINT绘制完成后,会引起更新区域上所有子窗口的重绘(所有子窗口也是自底向上排序的)。

    子窗口是具有WS_CHILD或者WS_CHILDWINDOW样式的窗口。和一般窗口一样,子窗口通过WM_PAINT来绘图。子窗口也维护一个更新区域,应用程序和系统都可以通过设置该更新区域无效来产生WM_PAINT消息。

    子窗口的更新和显示区域受到父窗口的影响,其他样式的窗口则不会。系统常常设置父窗口的更新区域的同时设置子窗口的更新区域,使父窗口收到WM_PAINT消息的同时子窗口也能收到WM_PAINT消息。系统把子窗口的位置限制在父窗口的client区域,超出这个区域就会被裁减掉。

    无论何时,只要父窗口的更新区域包含了子窗口的一部分,系统就会为子窗口设置更新区域。此时,系统先向父窗口发送WM_PAINT消息,然后向子窗口发送消息让子窗口可以恢复被父窗口覆盖的内容。

    但是如果只有子窗口设置了更新区域,系统不会给父窗口也设置。在无效化子窗口时,系统不会给父窗口发WM_PAINT(因为被覆盖住了,根本没有必要)。同样的,如果使被子窗口覆盖住的父窗口的部分区域无效化,系统也不会给父窗口发送WM_PAINT的。在这种情况下,无论子窗口还是父窗口都不会收到WM_PAINT消息。

    父子窗口间的刷新,还受父窗口是否设置了WS_CLIPCHILDREN样式影响。

    父窗口如果设置了WS_CLIPCHILDREN这个样式的话,当父窗口的更新区域被设置的时候,子窗口的更新区域不会被设置。父窗口作用在子窗口下面的任何绘图全部被裁减掉。

    因此,当父窗口无效且收到WM_PAINT消息时,如果没有设置WS_CLIPCHILDREN样式,则所有子窗口都会在父窗口处理WM_PAINT之后收到WM_PAINT重绘消息;如果父窗口带有WS_CLIPCHILDREN样式,则不会引起子窗口重绘。

    1.2 兄弟窗口间的刷新管理

    如果两个窗口重叠,则两个窗口都会收到WM_PAINT消息。他们收到WM_PAINT消息的顺序与z-index相反,即最上面的(z-order最高)的收到WM_PAINT消息最晚。

    应用程序可以为窗口设置WS_CLIPSIBLING样式来避免兄弟窗口的绘制重叠。设置了这个,高z-order的窗口部分就会被上面的窗口裁减掉了,此部分被覆盖的区域就不会被刷新了。

    结论

    1)WS_CLIPCHILDREN样式主要是用于父窗口,也就是说当在父窗口绘制的时候,父窗口上还有一个子窗口,那么如果设置了这个样式的话,子窗口所在区域父窗口就不负责绘制;

    2)所有的overlapped和popup风格的窗口,都有WS_CLIPSIBLINGS属性。也就是说这类风格的窗口,你是去不掉WS_CLIPSIBLINGS样式的,这样就是它不会在其与兄弟窗口重叠的区域绘图;

    3)WS_CLIPSIBLINGS样式只适用于同级窗口,实际上还需要和控件的叠放顺序(z order)配合使用才能看出明显的效果。

    2、OnEraseBkGnd与OnPaint

    2.1 常见的问题

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

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

    以上问题解决方法有三个半:

    1)用OnEraseBkGnd实现,不要调用原来的OnEraseBkGnd函数。

    2)用OnPaint实现,同时重载OnEraseBkGn,其中直接返回TRUE。

    3)用OnPaint实现,创建窗口时设置背景刷为空。

    4)用OnPaint实现,但是要求刷新时用Invalidate(FALSE)这样的函数。(不过这种情况下,窗口覆盖等造成的刷新还是要闪一下,所以不是彻底的解决方法)。

    2.2 关于OnEraseBkGnd的返回值

    An application should return nonzero inresponse to WM_ERASEBKGND if it processes the message and erases thebackground; this indicates that no further erasing is required. If theapplication returns zero, the window will remain marked for erasing.(Typically, this indicates that the fErase member of the PAINTSTRUCT structurewill be TRUE.)

    2.3 OnPaint的执行

    Windows为每个视窗保存一个「绘图信息结构」,这就是PAINTSTRUCT,定义如下:

    typedef struct tagPAINTSTRUCT

    {

    HDC      hdc ;

            BOOL     fErase ;

    RECT     rcPaint ;

    BOOL     fRestore ;

    BOOL     fIncUpdate ;

    BYTE     rgbReserved[32] ;

    } PAINTSTRUCT ;

    在程序隐式调用BeginPaint时,Windows会适当填入该结构的各值。应用程序只使用前三个值,其他值由Windows内部使用。Hdc是设备DC句柄。在旧版本的Windows中,BeginPaint的传回值也曾是该值。在大多数情况下,fErase被标志为FALSE(0),这意味著Windows已经擦除了无效矩形的背景。这最早在BeginPaint函数中发生(如果要在窗口消息处理中自己定义一些背景擦除行为,可以自行处理WM_ERASEBKGND消息)。Windows使用WNDCLASS结构的hbrBackground指定的画刷来擦除背景,这个WNDCLASS结构程序在注册窗口类型时使用的。许多Windows程序使用白色画刷。以下叙述设定窗口类型结构中画刷的值:

    wndclass.hbrBackground = (HBRUSH)GetStockObject (WHITE_BRUSH) ;

    不过,如果程序通过调用Windows函数InvalidateRect使显示区域中的矩形失效,则该函数的最后一个参数会指定是否擦除背景。如果这个参数为FALSE(即0),则Windows将不会擦除背景,并且在调用完BeginPaint后PAINTSTRUCT结构的fErase将为TRUE(非零)。

    PAINTSTRUCT结构的rcPaint是RECT型态的结构。它定义了无效矩形的边界,这些值均以像素为单位,并相对于客户区域的左上角。无效矩形是指应该重画的区域。

    PAINTSTRUCT结构的rcPaint不仅是无效矩形,它还是一个“裁剪”矩形。这意味着Windows将绘图操作限制在该裁剪矩形内(更确切地说,如果无效矩形区域不为矩形,则Windows将绘图操作限制在这个区域内)。

    在处理WM_PAINT消息时,为了在更新的矩形外绘图,可以使用如下函数:

    InvalidateRect (hwnd, NULL, TRUE) ;

    该函数在BeginPaint调用之前进行,它使整个显示区域变为无效,并擦除背景。但是,如果最後一个参数等於FALSE,则不擦除背景,原有的东西将保留在原处。通常这是Windows程序在无论何时收到WM_PAINT讯息而不考虑rcPaint结构的情况下简单地重画整个显示区域最方便的方法。

    3、WM_PAINT消息

    在Windows API编程中,WM_PAINT是Windows窗口的一个重要消息,应用程序就是通过响应这个消息来完成窗口的绘制。

    The WM_PAINT message is generated by thesystem and should not be sent by an application.The system sends this messagewhen there are no other messages in the application's message queue.

    注意:WM_PAINT消息是由系统产生,非要等应用程序的消息队列为空时才发送WM_PAINT消息。

    其实系统会在很多的不同的机制下发送WM_PAINT消息,比如调用UpdateWindow函数,第一次创建窗口,改变了窗口的大小,最大化,最小化等等。这些动作的产生都是有系统来控制的,应用程序只是接收消息,并处理消息。

    当Window检测到窗口被覆盖的地方需要恢复的时候,它会向用户程序发送一个WM_PAINT消息,消息中包括了需要恢复的区域,然后由用户程序来决定如何恢复被覆盖的内容。窗口过程收到WM_PAINT消息后,并不代表整个客户区都需要被刷新,有可能客户区被覆盖的区域只有一小块,这个区域叫做“无效区域”,程序只需要更新这个区域。与WM_TIMER消息类似,WM_PAINT消息也是一个低级别的消息,虽然它不会像WM_TIMER消息一样被丢弃,但Windows总是在消息循环空的时候才把WM_PAINT放入其中,实际上,Windows为每个窗口维护一个“绘图信息结构”,无效区域的坐标就在其中,每当消息循环空的时候,如果Windows发现存在一个无效区域,就会放入一个WM_PAINT消息。

    无效区域的坐标并不附带在WM_PAINT消息的参数中,在程序中有其他方法可以获取,WM_PAINT消息只是通知程序有个区域需要更新而已,所以Windows也不会同时将两 条WM_PAINT消息放入消息循环中,当Windows要放入一条WM_PAINT消息的时候,如果发现已经存在一个无效区域了,那么它只需要把新旧两个无效区域合并计算出一个无效区域就可以了,消息循环中还是只需要一条WM_PAINT消息。

    如果程序在WM_PAINT消息中对客户区刷新完毕后工作并没有结束,如果不使无效区域变得有效,Windows会在下一轮消息循环中继续放入一个WM_PAINT消息,而不是根据程序是否执行了刷新过程,所以程序也可以不去刷新客户区,而是简单地用一个ValidateRect函数直接让客户区变得有效,以此来“欺骗”Windows已经没有无效区域了,当Windows检查“绘图信息结构”的时候发现没有了无效区域,也就不会继续发送WM_PAINT消息了。

    大多数的时候应用也需要能够主动引发窗口中的绘制操 作,比如当窗口显示的数据改变的时候,这一般是通过InvalidateRect和InvalidateRgn函数来完成的。InvalidateRect和InvalidateRgn把指定的区域加到窗口的Update Region中,当应用的消息队列没有其他消息时,如果窗口的Update Region不为空时,系统就会自动产生WM_PAINT消息。

    系统为什么不在调用Invalidate时发送WM_PAINT消息呢?又为什么非要等应用消息队列为空时才发送WM_PAINT消息呢?这是因为系统把在窗口中的绘制操作当作一种低优先级的操作,于是尽可能地推后做。不过这样也有利于提高绘制的效率:两个WM_PAINT消息之间通过 InvalidateRect和InvaliateRgn使之失效的区域就会被累加起来,然后在一个WM_PAINT消息中一次得到更新,不仅能避免多次重复地更新同一区域,也优化了应用的更新操作。像这种通过InvalidateRect和InvalidateRgn来使窗口区域无效,依赖于系统在合适的时机发送WM_PAINT消息的机制实际上是一种异步工作方式,也就是说,在无效化窗口区域和发送WM_PAINT消息之间是有延迟的;有时候这种延迟并不是我们希望的,这时我们当然可以在无效化窗口区域后利用SendMessage发送一条WM_PAINT消息来强制立即重画,但不如使用Windows GDI为我们提供的更方便和强大的函数:UpdateWindow和RedrawWindow。UpdateWindow会检查窗口的Update Region,当其不为空时才发送WM_PAINT消息;RedrawWindow则给我们更多的控制:是否重画非客户区和背景,是否总是发送 WM_PAINT消息而不管Update Region是否为空等。

    4、BeginPaint

    BeginPaint sets the update region of awindow to NULL. This clears the region, preventing it fromgenerating subsequentWM_PAINT messages. If an application processes a WM_PAINT message but does notcall BeginPaint or otherwise clear the update region, the application continuesto receive WM_PAINT messages as long as the region is not empty. In all cases,an application must clear the update region before returning from the WM_PAINTmessage.

    BeginPaint函数的作用之一就是将窗口需要重绘的区域设置为空(也就是Update Region置空)。在正常情况下,我们接收到了WM_PAINT消息后,窗口的Update Region都是非空的(如果为空就不需要发送WM_PAINT消息了)。而当你响应这个消息的时候又不调用BeginPaint来清空,窗口的 update Region就一直是非空的,系统就会一直发送WM_PAINT消息。这样就形成了一个处理WM_PAINT消息的死循环。

    BeginPaint和WM_ERASEBKGND消息也有关系。当窗口的Update Region被标志为需要擦除背景时,BeginPaint会发送WM_ERASEBKGND消息来重画背景,同时在其返回信息里有一个标志表明窗口背景是否被重画过。当我们用InvalidateRect和InvalidateRgn来把指定区域加到Update Region中时,可以设置该区域是否需要被擦除背景,这样下一个BeginPaint就知道是否需要发送WM_ERASEBKGND消息了。

    BeginPaint只能在WM_PAINT处理函数中使用,并且在调用了BeginPaint函数后,必须调用EndPaint函数,他们可是一对的。

    5、重绘函数

    5.1 InvalidateRect / InvalidateRgn

    The InvalidateRect function adds a rectangleto the specified window's update region. The update region represents theportion of the window's client area that must be redrawn.

    The invalidated areas accumulate in theupdate region until the region is processed when the next WM_PAINT message occurs or until the region isvalidated by using the ValidateRect or ValidateRgn function.

    The system sends a WM_PAINT message to awindow whenever its update region is not empty and there are no other messagesin the application queue for that window.

    If the bErase parameter is TRUE for any partof the update region, the background is erased in the entire region, not justin the specified part.

    InvalidateRect和InvalidateRgn把指定的区域加到窗口的Update Region中,当应用的消息队列没有其他消息时,如果窗口的Update Region不为空时,系统就会自动产生WM_PAINT消息。

    如果指定bErase为TRUE,整个的背景都将会被擦除。

    5.2 UpdateWindow

    The UpdateWindow function updates the clientarea of the specified window by sending a WM_PAINT message to the window if thewindow's update region is not empty. The function sends a WM_PAINT messagedirectly to the window procedure of the specified window, bypassing theapplication queue. If the update region is empty, no message is sent.

    UpdateWindow是直接调用窗口函数立即响应刷新消息,使窗口刷新消息优先被响应(消息队列中如果没有WM_PAINT消息就什么都不执行),一般是在ShowWindow之后调用。

    5.3 RedrawWindow

    RedrawWindow相当于先调用InvalidateRect,紧接着又调用UpdateWindow,此外RedrawWindow还提供了一些前两者没法做到的功能,如是否重画非客户区和背景,是否总是发送 WM_PAINT消息而不管Update Region是否为空等。

    其flags定义如下:

    Flag(无效化标记)

    描述

    RDW_ERASE

    当窗口重绘时将收到WM_ERASEBKGND消息。必须与RDW_INVALIDATE同时使用,否则无效。

    RDW_FRAME

    与无效区域有交集的非客户区域将收到WM_NCPAINT消息,必须与RDW_INVALIDATE同时使用,否则无效。

    在RedrawWindow处理过程中WM_NCPAINT消息不会被发送,除非指定了RDW_UPDATENOW或RDW_EARSENOW

    RDW_INTERNALPAINT

    Post一条WM_PAINT消息,不管Update Region是否为空

    RDW_INVALIDATE

    无效化参数指定的区域

    Flag(有效化标记)

    描述

    RDW_NOERASE

    阻止即将发生的WM_ERASEBKGND消息

    RDW_NOFRAME

    阻止即将发生的WM_NCPAINT消息,使用它要特别注意,可能会使窗口绘制不正确。

    它必须与RDW_VALIDATE同时使用,

    通常和RDW_NOCHILDREN一起使用。

    RDW_NOINTERNALPAINT

    阻止即将发生的NULL无效区域的WM_PAINT消息

    RDW_VALIDATE

    将参数的指定的区域有效化

    Flag(刷新时机标记)

    描述

    RDW_EARSENOW

    如果必要,将在RedrawWindow返回前使被影响的窗口(由RDW_ALLCHILDREN和RDW_NOCHILDREN指定)收到WM_NCPAINT和WM_ERASEBKGND消息

    RDW_UPDATENOW

    如果必要,将在RedrawWindow返回前使被影响的窗口(由RDW_ALLCHILDREN和RDW_NOCHILDREN指定)收到WM_NCPAINT、WM_ERASEBKGND和WM_PAINT消息

    Flag(影响标记)

    描述

    RDW_ALLCHILDREN

    如果有子窗口,子窗口将被影响

    RDW_NOCHILDREN

    如果有子窗口,子窗口不收影响

    默认情况下,如果不指定RDW_NOCHILDREN和RDW_ALLCHILDREN,受影响的窗口由WS_CLIPCHILDREN样式决定,如果指定的窗口设置了该样式子窗口不受影响,否则子窗口将会被递归式的影响直到遇到一个具有WS_CLIPCHILDREN样式的窗口。

    posted @ 2015-06-02 11:44 心灵捕手 阅读( ...) 评论( ...)   编辑 收藏
    展开全文
  • 简单动态显示窗体背景,采用定时器刷新,点击截图可以截取当前屏幕显示图片并保存BMP格式
  • 在较大或者耗时较长的循环处理中,程序会不相应消息队列,程序的界面不会刷新,点击也没有反应,程序处于假死状态。我们可以调用如下函数进行处理: void ProcessMessages() { MSG msg; while (1) { if ...

    在较大或者耗时较长的循环处理中,程序会不相应消息队列,程序的界面不会刷新,点击也没有反应,程序处于假死状态。我们可以调用如下函数进行处理:

    void ProcessMessages()
    {
    	MSG msg;
    	while (1)
    	{
    		if (::PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) == 0)
    		{
    			break;
    		}
    		::TranslateMessage(&msg);//转换消息,
    		::DispatchMessage(&msg); //将转换消息发送到窗口的消息处理函数
    	}
    }
    

    其中,//PeekMessage接收属于当前调用线程的窗口的消息(PeekMessage不接收属于其他线程的窗口的消息),有消息时返回TRUE,没有消息返回FALSE 如果hWnd为-1,PeekMessage只返回hWnd值为NULL的消息,该消息由函数PostThreadMessage寄送。如果wMsgFilterMin和wMsgFilterMax都为零,PeekMessage返回所有可得的消息(即,无范围过滤)。

    展开全文
  • 这样Windows向应用程序发送消息WM_CTLCOLOR,应用程序处理WM_CTLCOLOR消息并返回一个用来绘画窗体背 景的刷子句柄 ==================================== //放在OnPaint()里 {//设置背景图片 CRect rect;  ...
      

    打开vc+的mfc工程,先载入一张图片,ID为IDB_BITMAP2

    TestDlg.h中:
    CBrush m_brBk;//在public中定义
    TestDlg.cpp中:
    在初始化函数OnInitDialog()中加入:
    BOOL CTestDlg::OnInitDialog()
    {
    CDialog::OnInitDialog();
    CBitmap bmp;
    bmp.LoadBitmap(IDB_BITMAP2);
    m_brBk.CreatePatternBrush(&bmp);
    bmp.DeleteObject();
    return TRUE; // return TRUE unless you set the focus to a control
    }

    再打开类向导,找到WM_CTLCOLOR消息,重载得对应函数OnCtlColor(),
    添加如下:
    HBRUSH CTestDlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
    {
    HBRUSH hbr = CDialog::OnCtlColor(pDC, pWnd, nCtlColor);

    if (pWnd == this)
    {
    return m_brBk;
    }
    return hbr;
    }

    按照上面的方法一路COPY下来运行,OK!并且由于图片是做为背景显示的,所以再添的按钮都能很好的显示出来,非常方便。

    总结一下其中出现的变量和函数。
    CBrush:类CBrush封装了Windows图形设备接口(GDI)中的画刷,画刷也就是采取什么方案填充图形的背景的工具。
    OnInitDialog ( ):用于对对话框类的变量的初始化(注意:是在产生对话框之前就初始化),是WM_INITDIALOG消息产生的消
    息处理函数,覆盖该函数可改变对话框初始设置。
    用法:
    virtual BOOL OnInitDialog();返回值指定对话框是否对它的一个控件设置输入焦点。如果OnInitDialog返回非零
    值,Windows 将输入焦点设在对话框的第一个控件上,只有在对话框明确将输入焦点设在某控件上,应用返回0。
    CBitmap:类CBitmap封装了Windows图形设备接口(GDI)中的位图,并且提供操纵位图的成员函数。
    LoadBitmap ( ):CBitmap类的一个成员函数,从应用的可执行文件中加载一个命名的位图资源来初始化位图对象。
    用法:
    BOOL LoadBitmap( LPCTSTR lpszRecourceName );BOOL LoadBitmap( UINT nIDResource );返回值调用成功时返回非零值,
    否则为0。参数lpszResourceName指向一个包含了位图资源名字的字符串(该字符串以null结尾)。NIDResource指定位图资源
    中资源的ID号。本函数从应用的可执行文件中加载由lpszResourceName指定名字或者由nIDResource指定的ID号标志的位图资
    源。加载的位图被附在Cbitmap对象上。如果由lpszResourceName指定名字的对象不存在,或者没有足够的内存加载位图,函
    数将返回0。可以调用函数CgdiObject::DeleteObject删除由LoadBitmap加载的位图,否则Cbitmap的析构函数将删除该位图对象。
    CreatePatternBrush ( ):CBrush类的一个成员函数,用位图指定的模式初始化画刷。
    用法:
    BOOL CreatePatternBrush( CBitmap* pBitmap );返回值调用成功时返回非零值,否则为0。参数pBitmap指定一个位图。本
    函数用位图指定的模式初始化画刷。此画刷随后就可用于任何支持光栅操作的设备上下文。由bBitmap指定的位图一般用以下
    的函数初始化:CBitmap:: CreateBitmap、CBitmap::CreateBitmapIndirect、CBitmap::LoadBitmap或Cbitmap::
    CreateCompatibleBitmap。
    DeleteObject ( ):CgdiObject类的一个成员函数,从内存中删除附加给CGdiObject的Windows GDI对象,释放与此对象相关
    的系统存储空间。GdiObject类为各种Windows图形设备接口(GDI)对象,如位图、区域、画刷、画笔、调色板、字体等提供
    了一些基本类。我们不会直接构造一个CGdiObject对象,而是使用某一个派生类如CPen或CBrush创建。
    用法:
    BOOL DeleteObject( );如果GDI对象被成功删除,则返回非零值,否则为0。通过释放附加的GDI对象占有的系统存储来删除它
    们。与CGdiObject对象有关的存储不受此调用的影响。如果CGdiObject对象正被选入设备上下文中,则应用不可对此对象调用
    DeleteObject,。当一个模式画刷被删除时,与之相关联的位图不被删除。位图必须被独立删除。
    HBRUSH:数据类型,用于定义画刷句柄。在Windows环境中,句柄是用来标识项目的,这些项目包括:module, task,
    instance, file ,block of memory, menu, control, font, resource, icon, cursor, string, GDI object等,包括
    bitmap, brush, metafile, palette, pen, region以及设备描述表device context。实际上,句柄是一个标识符,用来表示
    对象或者项目,是一个32位的正整数。应用程序几乎总是通过调用一个Windows函数来获得一个句柄,之后其他的Windows函数
    就可以使用这个句柄,以引用相应的对象。
    WM_CTLCOLOR消息:WM_CTLCOLOR是一个由控制(Control)发送给它父窗口的通知消息(Notification message)。利用向导映射
    该消息产生函数:HBRUSH CAboutDlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor);参数pDC是TestDlg的设备上下
    文,pWnd是TestDlg中发送该消息的control指针,nCtlColor是Control的类型编码。WM_CTLCOLOR是系统在绘制控件的时候自
    动发送的,如果需要自定义,就截取这个消息并重载它的响应函数,用classWizard添加WM_CTLCOLOR消息然后编辑其
    OnCtlColor函数。这样Windows向应用程序发送消息WM_CTLCOLOR,应用程序处理WM_CTLCOLOR消息并返回一个用来绘画窗体背
    景的刷子句柄

    ====================================

    //放在OnPaint()里

    {//设置背景图片
    CRect    rect;   
    GetClientRect(&rect);   
    CDC    *pDC=GetDC();   
    CDC    memdc;   
    memdc.CreateCompatibleDC(pDC);   
    CBitmap    bitmap;   
    //从资源中载入位图   
    bitmap.LoadBitmap(IDB_BITMAP1);   
    memdc.SelectObject(bitmap);   
    pDC->BitBlt(0,0,rect.Width(),rect.Height(),&memdc,0,0,SRCCOPY);
    }


    ==========================================


    对于VC++文档、视结构中的视图,从用户的角度来看,只是可以改变大小、位置的普通窗口,同其他基于Windows应用程序的窗口是一样的;从程序员的 角度来看,视图并不是普通的窗口,而是从MFC库中CView类派生的类对象。像任何VC++对象一样,视图对象的行为由类的成员函数(数据成员)决定, 包括派生类中应用程序定义的函数和从基类继承来的函数。

    提出问题 
    视图的背景一般来说是白色的,在缺省情况下,它和系统定义的颜色COLOR_WINDOW是一致的。设计者一般会希望自己的 程序可以让用户轻松地改变窗口背景颜色,或是用漂亮的图片来充填背景。我们可以用Windows函数SetSysColors来重新指定 COLOR_WINDOW所对应的实际颜色,来达到改变视图背景颜色的目的。但这样会同时改变其他应用程序的视图窗口背景,使得整个Windows系统的 颜色设置产生混乱。另外,我们可能会用以下方法来设置视图的背景颜色,即在CView的OnDraw函数中添写如下一段程序代码: 
    void CTestView::OnDraw(CDC* pDC) 

    CTestDoc* pDoc = GetDocument(); 
    ASSERT_VALID(pDoc); 
    CRect rectClient; 
    CBrush brushBkColor; 
    GetClientRect(rectClient); 
    brushBkColor.CreateSolidBrush(RGB(255,0,0)); 
    pDC->DPtoLP(rectClient); 
    pDC->FillRect(rectClient,&brushBkColor); 
    … 

    这样可以达到改变当前应用程序的视图背景的目的,但同时也产生了一些不良影响,使得程序运行效果不尽如人意。

    分析问题 
    我们知道,在VC++的文档、视结构中,CView的OnDraw函数用于实现绝大部分图形绘制的工作。如果用户改变窗口尺寸, 或者显示隐藏的区域,OnDraw函数都将被调用来重画窗口。并且,当程序文档中的数据发生改变时,一般必须通过调用视图的Invalidate(或 InvalidateRect)成员函数来通知Windows所发生的改变,对Invalidate的调用也会触发对OnDraw函数的调用。正因为 OnDraw函数被频繁调用,所以在其执行时,每次都刷新填充一次视图客户区域,便会使屏幕不稳定,产生闪烁现象。 
    笔者通过对VC++应用程 序框架结构和Windows消息映射系统的仔细研究,找到另外一种改变视图背景的方法,其执行效果比上述两种方法都好。其实在程序调用OnDraw函数之 前,会触发一个Windows消息:WM_ERASEBKGND,以擦除视图刷新区域。在缺省情况下,Windows系统使用视图窗口注册时窗口类中的成 员hbrBackground所描述的画刷来擦除屏幕,这一般会将屏幕刷新成COLOR_WINDOW所对应的颜色。因此,在OnDraw函数中设置背景 颜色的执行过程是这样的:先将屏幕刷新成COLOR_WINDOW所对应的颜色,接着又在OnDraw函数中填充其他颜色,这正是产生屏幕闪烁的根本原 因。

    解决问题 
    通过上述分析,我们应将视图背景颜色填充移到Windows消息:WM_ERASEBKGND所对应的消息映射函数中,而不是在 OnDraw函数中。我们可以通过下列步骤实现这一过程:在文档类中增加一成员变量m_viewBkColor保存当前背景颜色,同时增加两个成员函数 GetViewBkColor和SetViewBkColor对其进行读写操作。这样做的好处是可以对m_viewBkColor成员进行序列化,将其和 文档联系在一起,打开某一文档时,其背景将和上一次程序操作该文档时的背景保持一致。在视图类中为视图的Windows消息WM_ERASEBKGND增 加消息映射函数OnEraseBkgnd,代码如下: 
    BOOL CTestView::OnEraseBkgnd(CDC* pDC)  

    CRect rect; 
    CBrush brush; 
    brush.CreateSolidBrush(GetDocument()->GetViewBkColor()); 
    pDC->GetClipBox(rect); 
    pDC->FillRect(rect,&brush); 
    return true; 

    在该函数中不需要对客户区域矩形进行设备坐标到逻辑坐标的转换,并且Windows在调用该函数时会自动进行裁剪区域的计算,使得需要刷新的屏幕面积达到最小。这样我们可以在程序中通过设计下列菜单函数轻松地改变视图背景的颜色,而且运行效果相当令人满意。 
    void CTestView::OnChangeViewBkcolor()  

    CColorDialog cdlg; 
    if(cdlg.DoModal()==IDOK) 

    GetDocument()->SetViewBkColor 
    (cdlg.GetColor()); 
    InvalidateRect(NULL); 

    }

    转载于:https://my.oschina.net/bigfool007139/blog/513969

    展开全文
  • 刷新--MFC界面刷新函数比较

    千次阅读 2015-09-29 16:27:00
     UpdateWindow只向窗体发送WM_PAINT消息,在发送之前判断GetUpdateRect(hWnd,NULL,TRUE)看有无可绘制的客户区域,如果没有,则不发送WM_PAINT。  RedrawWindow()则是具有Invalidate()和UpdateWindow()...

    一:什么时候才会发生重绘窗口的消息?

          当需要更新或重新绘制窗口的外观时,应用程序就会发送WM_PAINT消息。对窗口进行重新绘制。

    二:Invalidate() -- RedrawWindow() -- UpdateWindow()三个函数有什么异同?

          Invalidate()是强制系统进行重画,但是不一定就马上进行重画。因为Invalidate()只是通知系统,此 时的窗口已经变为无效。强制系统调用WM_PAINT,而这个消息只是Post就是将该消息放入消息队列。当执行到WM_PAINT消息时才会对敞口进行重绘。

         UpdateWindow只向窗体发送WM_PAINT消息,在发送之前判断GetUpdateRect(hWnd,NULL,TRUE)看有无可绘制的客户区域,如果没有,则不发送WM_PAINT。

         RedrawWindow()则是具有Invalidate()和UpdateWindow()的双特性。声明窗口的状态为无效,并立即更新窗口,立即调用WM_PAINT消息处理。

    展开全文
  • MFC窗体窗体 使用

    千次阅读 2013-08-29 19:13:02
    当我们使用 MFC AppWizard 生成一个 MFC 程序,选用所有默认的设置(当然也是 Multiple Documents ,本文讨论主要基于 Multiple Documents ,对于 Single Document 情况仅以简单表述提及,皆因后者和前者...
  • MFC界面刷新函数

    千次阅读 2015-12-15 19:18:10
     UpdateWindow只向窗体发送WM_PAINT消息,在发送之前判断GetUpdateRect(hWnd,NULL,TRUE)看有无可绘制的客户区域,如果没有,则不发送WM_PAINT。  RedrawWindow()则是具有Invalidate()和UpdateWindow()...
  • MFC强行刷新子窗口

    2017-10-09 10:02:00
    当父窗体设置了 WS_CLIPCHILDREN 的属性后, 默认状态下,RedrawWindow 和 InvalidateRect 不会导致子窗体重绘,因此,如果子窗体同时设置了 WS_EX_TRANSPARENT 属性,子窗体就会被父窗体刷没了。 解决的办法是 ...
  • MFC界面刷新函数比较

    2017-04-10 13:50:14
     UpdateWindow()只向窗体发送WM_PAINT消息,在发送之前判断GetUpdateRect(hWnd,NULL,TRUE)看有无可绘制的客户区域,如果没有,则不发送WM_PAINT。  RedrawWindow()则是具有Invalidate()和UpdateWindow()的双...
  • 问题描写叙述: 就是首先用CDC来截图,保存图片的路径通过dlg窗体来手动设置并传入。但是截下来的图片就会连带那个对话框也截图下来。 就是这样。我想截后面那个图。前面这个对话框是要传入一个图片保存路径。但是...
  • MFC 刷新界面

    千次阅读 2018-02-28 08:23:32
    刷新窗口时经常要调用重绘函数MFC提供了三个函数用于窗口重绘  InvalidateRect(&Rect)  Invalidate()  UpdateWindow()  当需要更新或者重绘窗口时,一般系统会发出两个消息WM_PAINT(通知客户区有变化)和WM_...
  • MFC中窗口刷新函数详解 https://blog.csdn.net/francisapp/article/details/52764117
  • MFC 刷新失效的Picture控件

    千次阅读 2017-01-15 08:30:22
    问题描述:如在摄像头显示时,关闭摄像头,此时Picture控件仍然显示最后一帧图像,需要刷新掉,还原Picture控件。或者重复显示两张不同大小的图片时,第二张背景有第一张图片残留。   解决方法1:(最笨的...
  • MFC+OpenGL去除闪烁的方法网上很多。... 上文相当详细的解释了MFC中使用OpenGL的基本的流程。...现在给出第一个问题:如果你在MFC生成向导中选择了“拆分窗口”,你会发现即使重载了OnEraseBkgnd,也无法解决
  • 2.设置父窗体刷新时不重绘子窗体 CWnd *_mWnd = AfxGetMainWnd(); HWND _hHwnd = _mWnd->m_hWnd; LONG_PTR Style = ::GetWindowLongPtr(_hHwnd, GWL_STYLE); //获取父窗体属性 Style = Style | WS_...
  • MFC中,4个动作对应着四种位图bmp, 首先,将代表四种状态的位图加载入资源中,将对应的按钮设置为BitmapButton 第二,在OnInitDialog()函数中添加如下两行代码 这里需要注意的是使用的按钮类型为 bitmapButton...
  • cocos2d-X 2.0嵌入MFC的子窗体的方法

    千次阅读 2013-09-13 14:31:09
    Cocos2d-X 2.0嵌入MFC的子窗体的方法(1.0姐妹篇) ... 之前讲解了如何将Cocos2d-x 1.0版本嵌入MFC的窗体中的具体方法,应朋友们的要求,将Cocos2d-x 2.0版本嵌入MFC窗体的方法也整理发布。    首先,我们
  • MFC+OpenCV 刷新Picture控件

    千次阅读 2011-04-05 20:03:00
    利用OpenCv的CvvImage类把图片显示在MFC的Picture... 后经查询, 发现一挺好方法, 即把Picture这静态窗体进行刷新, 如此便可清除画面残余 e.g:  GetDlgItem(ID)->ShowWindow(FA

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,925
精华内容 770
关键字:

mfc窗体刷新问题