Click here to Skip to main content
12,397,805 members (56,401 online)
Click here to Skip to main content
Add your own
alternative version

Stats

33.5K views
654 downloads
11 bookmarked
Posted

Flicker Free Main Frame Resizing

, 4 Oct 2008 CPOL
Rate this:
Please Sign up or sign in to vote.
Flicker free resizing of the main frame window

Problem

All frame windows in Windows flicker when they are resized, especially from the top/left corners.

Reason

When a window's size is increased during resizing, Windows automatically draws the old contents over the top left part of the window, then sends the WM_WINDOWPOSCHANGED message to the window, which ends up sending WM_SIZE to all the children in addition to a bunch of other messages which take some time to be processed. Only after a while are the old contents erased by some client window.

As a consequence, for example, it feels like the status bar of the frame window jumps up and down during resizing.

Solution

The class CMainFrameResize contains a screenshot of the frame window during resizing. Right after WM_WINDOWPOSCHANGED is received, it stretches the old window contents over the new window (using StretchBlt, because the new window size may be larger or smaller than the captured window). This way, there is an illusion of immediate resizing, and to the eye, it appears with almost no flickering. Here is the main code that does this:

LRESULT CMainFrameResize::OnWindowPosChanged(HWND hwnd, UINT uMsg, 
                                             WPARAM wParam, LPARAM lParam)
{
    LRESULT ret;
    CRect rcWnd;

    m_pWnd->GetWindowRect(&rcWnd);
    ret = 0;
    if(rcWnd.Size() != m_rcWnd.Size())
    {
        if(m_rcCapture == CRect(0, 0, 0, 0)) // capture for the first time 
            CaptureWindow();
    
        // first of all stretch the previous captured image to 
        // have something to show during the following lengthy operation 

        {
            CWindowDC dcWnd(m_pWnd);
            dcWnd.StretchBlt(0, 0, rcWnd.Width(), rcWnd.Height(), 
                             &m_dcCapture, 0, 0, m_rcCapture.Width(), 
                             m_rcCapture.Height(), SRCCOPY); 
        }
    
        // now wm_size is sent to all children, a lengthy operation 
        m_pWnd->SetRedraw(FALSE);
        ret = CallWindowProc((WNDPROC)m_hPrevProc, hwnd, uMsg, wParam, lParam);
        m_pWnd->SetRedraw(TRUE);

    
        // now get the new contents 
        CaptureWindow();

    
        // draw the new contents in one blit 
        {
            CWindowDC dcWnd(m_pWnd);
            dcWnd.BitBlt(0, 0, rcWnd.Width(), rcWnd.Height(), 
                         &m_dcCapture, 0, 0, SRCCOPY);
        }
    
        // update m_rcWnd 
        m_rcWnd = rcWnd;
    }
    else 
    if(!m_bResizing)
        ret = CallWindowProc((WNDPROC)m_hPrevProc, hwnd, uMsg, wParam, lParam);
    
    return ret; 

}

void CMainFrameResize::CaptureWindow()
{
    // use PrintWindow to capture the window to our dc
    m_pWnd->GetWindowRect(&m_rcCapture);
    m_pWnd->PrintWindow(&m_dcCapture, 0);
}

Using the code

Include the following variable in CMainFrame:

#include "MainFrmResize.h"

class CMainFrame : public CFrameWnd
{            
    ...
    CMainFrameResize m_resize;
}

And inside CMainFrame::OnCreate:

m_resize.Attach(this);

CS_HREDRAW and CS_VREDRAW

These two window class styles make a window repaint itself during resizing even if the main frame window has received SetRedraw(FALSE). They are also responsible for some of the flickering during resizing. Therefore, they have to be removed from all windows in the application.

Windows that you create on your own in custom CWnd derived classes can override PreCreateWindow and make sure that they do not pass CS_HREDRAW and CS_VREDRAW in AfxRegisterWndClass. The problem comes up when you need to remove these two class styles from existing classes like CToolBar and CStatusBar. Here is a template class that does exactly that:

template<class BaseClass>
class CWndNoCSHVRedraw : public BaseClass
{
public:
    virtual BOOL PreCreateWindow(CREATESTRUCT& cs)
    {
        WNDCLASSEX wc;
        ATOM atmRegister;
        if(GetClassInfoEx(NULL, cs.lpszClass, &wc))
        {
            if(wc.style & (CS_HREDRAW | CS_VREDRAW))
            {
                wc.cbSize = sizeof(wc);
                CString strClassNew;
                strClassNew.Format(_T("%sNOCSREDRAW"), wc.lpszClassName);
                wc.lpszClassName = strClassNew;
                wc.style &= ~(CS_HREDRAW | CS_VREDRAW);
                atmRegister = RegisterClassEx(&wc);
                ASSERT(atmRegister);
                cs.lpszClass = (LPCTSTR)atmRegister;
            }
        }
        else
            cs.lpszClass = AfxRegisterWndClass(CS_DBLCLKS, ::LoadCursor(NULL, IDC_ARROW), 
            (HBRUSH) ::GetStockObject(WHITE_BRUSH), 
            ::LoadIcon(NULL, IDI_APPLICATION));

    cs.dwExStyle &= ~WS_EX_CLIENTEDGE;
        
        cs.style |= WS_CLIPCHILDREN;
        if(!BaseClass::PreCreateWindow(cs))
            return FALSE;

        return TRUE;
    };
};

In the sample application, inside MainFrm.h, we write:

CWndNoCSHVRedraw<CStatusBar> m_wndStatusBar;
CWndNoCSHVRedraw<CToolBar> m_wndToolBar;

and also we derive the view from that class (instead of simply from CView):

class CTestJitterView : public CWndNoCSHVRedraw<CView> 

and in CTestJitterView::PreCreateWindow (or we could remove the overridden PreCreateWindow function altogether):

return CWndNoCSHVRedraw<CView>::PreCreateWindow(cs); 

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author


You may also be interested in...

Comments and Discussions

 
GeneralMy vote of 1 Pin
skyformat99@gmail.com4-Aug-13 23:23
memberskyformat99@gmail.com4-Aug-13 23:23 
GeneralMy vote of 1 Pin
Jeroen Walter22-Dec-11 8:26
memberJeroen Walter22-Dec-11 8:26 
GeneralMy vote of 1 Pin
NetDave26-May-09 18:22
memberNetDave26-May-09 18:22 
GeneralMy vote of 1 Pin
Paul S. Vickery21-Jan-09 3:49
memberPaul S. Vickery21-Jan-09 3:49 
GeneralInvalidate(FALSE); Pin
The Idiot6-Oct-08 22:15
memberThe Idiot6-Oct-08 22:15 
GeneralRe: Invalidate(FALSE); Pin
David Lantsman7-Oct-08 10:02
memberDavid Lantsman7-Oct-08 10:02 
QuestionDemo? Elaborate on implementation? Pin
Damir Valiulin4-Oct-08 19:09
memberDamir Valiulin4-Oct-08 19:09 
AnswerRe: Demo? Elaborate on implementation? Pin
David Lantsman5-Oct-08 7:55
memberDavid Lantsman5-Oct-08 7:55 
GeneralRe: Demo? Elaborate on implementation? Pin
Damir Valiulin5-Oct-08 10:12
memberDamir Valiulin5-Oct-08 10:12 
GeneralRe: Demo? Elaborate on implementation? Pin
Damir Valiulin5-Oct-08 10:24
memberDamir Valiulin5-Oct-08 10:24 
GeneralRe: Demo? Elaborate on implementation? Pin
PJ Arends5-Oct-08 11:08
memberPJ Arends5-Oct-08 11:08 
GeneralRe: Demo? Elaborate on implementation? Pin
David Lantsman5-Oct-08 14:38
memberDavid Lantsman5-Oct-08 14:38 
GeneralRe: Demo? Elaborate on implementation? Pin
Damir Valiulin6-Oct-08 6:19
memberDamir Valiulin6-Oct-08 6:19 
GeneralRe: Demo? Elaborate on implementation? Pin
David Lantsman6-Oct-08 8:14
memberDavid Lantsman6-Oct-08 8:14 
JokeRe: Demo? Elaborate on implementation? Pin
Damir Valiulin6-Oct-08 14:42
memberDamir Valiulin6-Oct-08 14:42 
GeneralRe: Demo? Elaborate on implementation? Pin
David Lantsman6-Oct-08 16:51
memberDavid Lantsman6-Oct-08 16:51 
GeneralRe: Demo? Elaborate on implementation? Pin
Kochise7-Oct-08 9:22
memberKochise7-Oct-08 9:22 
GeneralRe: Demo? Elaborate on implementation? Pin
David Lantsman7-Oct-08 9:42
memberDavid Lantsman7-Oct-08 9:42 
GeneralRe: Demo? Elaborate on implementation? Pin
David Lantsman5-Oct-08 14:56
memberDavid Lantsman5-Oct-08 14:56 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web02 | 2.8.160721.1 | Last Updated 4 Oct 2008
Article Copyright 2008 by David Lantsman
Everything else Copyright © CodeProject, 1999-2016
Layout: fixed | fluid