Click here to Skip to main content
15,881,281 members
Articles / Desktop Programming / MFC

Flicker Free Main Frame Resizing

Rate me:
Please Sign up or sign in to vote.
1.75/5 (17 votes)
4 Oct 2008CPOL2 min read 66.1K   893   11   19
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:

C++
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:

C++
#include "MainFrmResize.h"

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

And inside CMainFrame::OnCreate:

C++
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:

C++
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:

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

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

C++
class CTestJitterView : public CWndNoCSHVRedraw<CView> 

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

C++
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)


Written By
Web Developer
Israel Israel

Comments and Discussions

 
GeneralMy vote of 1 Pin
skyformat99@gmail.com4-Aug-13 23:23
skyformat99@gmail.com4-Aug-13 23:23 
GeneralMy vote of 1 Pin
Jeroen Walter22-Dec-11 8:26
Jeroen Walter22-Dec-11 8:26 
GeneralMy vote of 1 Pin
NetDave26-May-09 18:22
NetDave26-May-09 18:22 
GeneralMy vote of 1 Pin
Paul Vickery21-Jan-09 3:49
professionalPaul Vickery21-Jan-09 3:49 
GeneralInvalidate(FALSE); Pin
Shao Voon Wong6-Oct-08 22:15
mvaShao Voon Wong6-Oct-08 22:15 
GeneralRe: Invalidate(FALSE); Pin
David Lantsman7-Oct-08 10:02
David Lantsman7-Oct-08 10:02 
QuestionDemo? Elaborate on implementation? Pin
Damir Valiulin4-Oct-08 19:09
Damir Valiulin4-Oct-08 19:09 
AnswerRe: Demo? Elaborate on implementation? Pin
David Lantsman5-Oct-08 7:55
David Lantsman5-Oct-08 7:55 
GeneralRe: Demo? Elaborate on implementation? Pin
Damir Valiulin5-Oct-08 10:12
Damir Valiulin5-Oct-08 10:12 
GeneralRe: Demo? Elaborate on implementation? Pin
Damir Valiulin5-Oct-08 10:24
Damir Valiulin5-Oct-08 10:24 
GeneralRe: Demo? Elaborate on implementation? Pin
PJ Arends5-Oct-08 11:08
professionalPJ Arends5-Oct-08 11:08 
GeneralRe: Demo? Elaborate on implementation? Pin
David Lantsman5-Oct-08 14:38
David Lantsman5-Oct-08 14:38 
GeneralRe: Demo? Elaborate on implementation? Pin
Damir Valiulin6-Oct-08 6:19
Damir Valiulin6-Oct-08 6:19 
GeneralRe: Demo? Elaborate on implementation? Pin
David Lantsman6-Oct-08 8:14
David Lantsman6-Oct-08 8:14 
JokeRe: Demo? Elaborate on implementation? Pin
Damir Valiulin6-Oct-08 14:42
Damir Valiulin6-Oct-08 14:42 
GeneralRe: Demo? Elaborate on implementation? Pin
David Lantsman6-Oct-08 16:51
David Lantsman6-Oct-08 16:51 
GeneralRe: Demo? Elaborate on implementation? Pin
Kochise7-Oct-08 9:22
Kochise7-Oct-08 9:22 
GeneralRe: Demo? Elaborate on implementation? Pin
David Lantsman7-Oct-08 9:42
David Lantsman7-Oct-08 9:42 
GeneralRe: Demo? Elaborate on implementation? Pin
David Lantsman5-Oct-08 14:56
David 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.