Click here to Skip to main content
Click here to Skip to main content

Saving a windows size position and state in MFC

, 30 May 2001
Rate this:
Please Sign up or sign in to vote.
Saving the size and positions of windows so that they can be restored next time the app loads up

Introduction

When building a UI application, one feature which I always include is saving the size and positions of my windows so that they can be restored next time the app loads up. Each time I make a new application, I always try a new method of doing this. Finally I've come up with what I think is the simplest and best solution to save and restore windows sizes and positions.

Get/SetWindowPlacement()

The trick lies behind the GetWindowPlacement/SetWindowPlacement functions. The GetWindowPlacement function is part of the Windows API and it basically retrieves the show state and the restored, minimized, and maximized positions a window and stores it into a WINDOWPLACEMENT structure. This structure can then be passed to the SetWindowPlacement function to restore the window to its old state.

Saving the window placement

Saving the WINDOWPLACEMENT structure is fairly simple and can be done from anywhere in your program. Since I use MFC for my UI applications I found that the best place to save the window's size and position is in the DestroyWindow virtual function, since it is called every time the window closes, for every type of window:

BOOL CMainFrame::DestroyWindow() 
{
    WINDOWPLACEMENT wp;
    GetWindowPlacement(&wp);
    AfxGetApp()->WriteProfileBinary("MainFrame", "WP", (LPBYTE)&wp, sizeof(wp));

    return CMDIFrameWnd::DestroyWindow();
}

The WriteProfileBinary() is part of the MFC class CWinApp which simply dumps the WINDOWPLACEMENT structure into the registry as a REG_BINARY value called "WP" in a key called MainFrame. A good idea, especially for child windows of the CMainFrame class, is to replace the hard-coded MainFrame with the caption of the frame.

Restoring the window placement

Next, we need to restore the window. The SetWindowPlacement function will only work after the window has been created, so the best place to call it is when the window is about to be shown for the first time. To my knowledge there is no single windows message or over-ridable virtual function to serve this purpose. However it is possible to handle the WM_SHOWWINDOW message and call SetWindowPlacement from there. Ideally it will be as simple as this:

void CMainFrame::OnShowWindow(BOOL bShow, UINT nStatus) 
{
    CMDIFrameWnd::OnShowWindow(bShow, nStatus);

    if(bShow && !IsWindowVisible())
    {
        WINDOWPLACEMENT *lwp;
        UINT nl;

        if(AfxGetApp()->GetProfileBinary("MainFrame", "WP", (LPBYTE*)&lwp, &nl))
        {
            SetWindowPlacement(lwp);
            delete [] lwp;
        }
    }
}

The formal parameter bShow is true when the window is about to be shown, and false when it is about to be hidden. With the if() statement we are ensuring that we will reposition the window only when the window is hidden and about to be shown. There are two problems here:

  1. If your application hides the window and shows it during program execution, it will be displayed at the saved coordinates each time.
  2. SetWindowPlacement causes a WM_SHOWWINDOW to be fired.

A simple workaround fixes the problem:

void CMainFrame::OnShowWindow(BOOL bShow, UINT nStatus) 
{
    CMDIFrameWnd::OnShowWindow(bShow, nStatus);

    static bool bOnce = true;

    if(bShow && !IsWindowVisible()
        && bOnce)
    {
        bOnce = false;

        WINDOWPLACEMENT *lwp;
        UINT nl;

        if(AfxGetApp()->GetProfileBinary("MainFrame", "WP", (LPBYTE*)&lwp, &nl))
        {
            SetWindowPlacement(lwp);
            delete [] lwp;
        }
    }
}

A static variable inside a function is only initialized once. Since the if() statement now requires bOnce to be true in order to execute the block, we are assured that the code will be only executed once; the first time the window is shown. Instead of having bOnce as a static it could obviously also be a member variable. In fact, if you create two instances of the same class, bOnce will only be true once, which means that you'll have to create it as a member variable. Happy Coding!

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

Share

About the Author

[James Pullicino]
Web Developer
United Kingdom United Kingdom
We need to develop some good software for the brain.

Comments and Discussions

 
SuggestionBetter Solution for OnShowWindow PinmemberMember 871346010-Apr-13 2:47 
GeneralMy vote of 4 Pinmemberddas-edEn12-Feb-12 23:07 
GeneralTitle bar not painted - solved PinmemberMember 380048931-Jul-08 4:57 
GeneralRe: Title bar not painted - solved Pinmembershamada19-Nov-08 2:26 
GeneralRe: Title bar not painted - solved PinmemberArmand Molinski18-May-11 10:30 
GeneralModeless dialogs PinmemberJase Jennings13-Jan-04 16:29 
GeneraliModeless dialogs PinmemberJase Jennings13-Jan-04 16:26 
GeneralSaving MDI Windows PinmemberGreg Online30-Dec-02 12:29 
GeneralRe: Saving MDI Windows Pinmemberfz256-Nov-03 16:17 
GeneralSame Old Problem... PinmemberJames R. Twine7-Jun-01 11:33 
   Many people use the same technique, but make the same mistakes when restoring the window information.  Also, no checking is done to ensure that the data read from the registry is somewhat valid.
 
   Try this on for size for restoring... (Note the code below is not formatted as I normally do, I had to make changes for it to display correctly.)
 
    WINDOWPLACEMENT *pWP = NULL;
    CMyApp *pApp = (CMyApp*)AfxGetApp();
    LPBYTE pData = NULL;
    UINT uBytes = 0;
    RECT rMaxWA;
 
    // Get Min/Max Screen Information
    ::SystemParametersInfo( SPI_GETWORKAREA, 0, &rMaxWA, 0 );
 
    // Get WP Structure
    if( pApp -> GetProfileBinary( _T( "Configuration" ), _T( "OldMFPosB" ), &pData, &uBytes ) )
    {
        // If Read In
        if( uBytes == sizeof( WINDOWPLACEMENT ) )
        {
            // And Size Seems Right
            pWP = (WINDOWPLACEMENT*)pData;
            if( pWP -> showCmd == SW_SHOWMINIMIZED )
            {
                //(Prevent Launch To Minimized)
                pWP -> showCmd = SW_SHOWNORMAL;
            }
            if( ( pWP -> rcNormalPosition.left >= rMaxWA.left ) &&
                ( pWP -> rcNormalPosition.top >= rMaxWA.top ) &&
                ( pWP -> rcNormalPosition.right <= rMaxWA.right ) &&
                ( pWP -> rcNormalPosition.bottom <= rMaxWA.bottom ) )
            {
                // If All Values Within The Visible Screen (Taking Into Account Only The Primary Monitor...!), Set The Placement
                ::SetWindowPlacement( pWP );
            }
            delete [] pData;
        }
    }
 
And in PreCreateWindow(...) (this prevents you from loading the entire binary structure just for the ShowState):
 
    uShowState = pApp -> GetProfileInt( _T( "Configuration" ), _T( "OldMFState" ), -1 );
    if( ( uShowState != -1 ) && ( uShowState != SW_MINIMIZE ) )
    {
            // If Obtained, And Not Minimized
            // Set Show State
    }
 
And for saving:
 
    WINDOWPLACEMENT wpWinInfo;
    CMyApp *pApp = (CMyApp*)AfxGetApp();
 
    // Get Window Info
    ::GetWindowPlacement( &wpWinInfo );
 
    // Store Window Position Data
    pApp -> WriteProfileBinary( _T( "Configuration" ), _T( "OldMFPosB" ), (LPBYTE)&wpWinInfo, sizeof( WINDOWPLACEMENT ) );
 
    // Store Window State As Integer
    pApp -> WriteProfileInt( _T( "Configuration" ), _T( "OldMFState" ), wpWinInfo.showCmd );
 
Peace!
 
-=- James.

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    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
Web04 | 2.8.141223.1 | Last Updated 31 May 2001
Article Copyright 2001 by [James Pullicino]
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid