It's very useful to let your application window have the feature of restoring position and state when it is re-launched after being closed. Most commercial software have this feature.
There are two very important API functions:
BOOL GetWindowPlacement( HWND hWnd, WINDOWPLACEMENT *lpwndpl );
BOOL SetWindowPlacement( HWND hWnd, WINDOWPLACEMENT *lpwndpl );
Using these functions, you can get the current window placement information and set the window to a special placement. These two functions work fine when the window is shown and is ready to execute your commands. But they don't seem to work when you want to save at exit and restore at startup. Unfortunately, it is the latter case that we want to use the most. The problem is that people often don't know when to call these two functions.
The common class
CWndPosManager and the three other MFC classes
CMDIFrameWndEx are designed to solve these problems.
To gain the basic function for your application, you just need to derive your own class
CDialogEx in a dialog based application, or derive your own class
CFrameWndEx in a SDI application or from
CMDIFrameWndEx in a MDI application. Usually, the basic function is enough.
Of course, these classes have some other functions, for example, to restore the window to the default placement and also to restore a window in different ways, such as always restore it to maximized state. And with the provided functions your can save more than one window placement, and easily restore your window to any one of them.
In this section, I will explain how to add the features to your applications.
As said earlier, to gain the basic function, you just need to derive your main window class from the corresponding class that I have provided instead of the default one.
Take the newly created SDI application for example. Firstly, replace the base class of your class
baseCMainFrame. Don't forget to do the same thing in the file MainFrm.cpp as well as in the file MainFrm.h. Secondly, add the following include and macro definition code in the file MainFrm.h just before the definition of the class
CMainFrame like this:
#define baseCMainFrame CFrameWndEx
class CMainFrame : public baseCMainFrame
Then copy the files WndPosManager.h and WndPosManager.cpp into your project directory and add them to your project. Now you should be able to compile and run your application.
It is now that the basic function is realized. The main window can be restored to the placement where it was closed last time in the recommended way. That's, if the last state is normal or maximized, then it will be restored to the same state; if the last state is minimized, then it will be restored to the state before it was minimized last time. It may be normal or maximized. All the restorations will keep the window's normal position and size unchanged unless you save it to a different normal placement. This point also holds true in the other restoring ways.
You can add a toolbar button or/and a menu item with the ID for example
ID_VIEW_DEFAULTLAYOUT, and then add a message map to the class
CMainFrame like this:
Then in the message handler function
OnViewDefaultLayout, just add the single line to the function:
When you run the application, you can execute the command, and the window will be replaced to the default placement, which I have set in the center of the screen. Its size is the same as its initial normal size.
There are five different ways to restore your window. They are listed below:
|Restore in the recommended way.||Nor->Nor; Max->Max; (Max, Min)->Max; (Nor, Min)->Nor|
|Restore to the exact state when exited the last time.||Nor->Nor; Max->Max; Min->Min|
|Restore to normal state.||Any->Nor|
|Restore to maximized state.||Any->Max|
|Restore to maximized state.||Any->Min|
* 1. Nor stands for normal; Max stands for Maximized; Min stands for Minimized; Any stands for any state;
* 2. -> stands for the window is closed and re-launched. (A, B) stands for a sequence of states.
In the class
CWndPosManager, I have provided two functions for you to customize the restoring way:
int GetResOption() const;
void SetResOption(int nResOption);
Each of the other three classes
CMDIFrameWndEx has a
CWndPosManager object member
m_wpMgr by which you can call these two functions to display and specify the current restoring way.
Here, I will give a simple example to illustrate how to use it. Of course, you can also use it in some other ways you want.
In the last SDI project, add a series of menu items with IDs arranged from, for example
ID_VIEW_RESTOREMINIMIZED. Make sure that these IDs' real values are in increasing sequence.
Now add the following message maps to your class
And then modify these two message handlers like this:
void CMainFrame::OnSetRestoreOption(UINT nID)
m_wpMgr.SetResOption(nID - ID_VIEW_RESTORERECOMMENDED);
void CMainFrame::OnUpdateRestorerOptions(CCmdUI *pCmdUI)
ID_VIEW_RESTORERECOMMENDED + 1);
ID_VIEW_RESTORERECOMMENDED + 2);
ID_VIEW_RESTORERECOMMENDED + 3);
ID_VIEW_RESTORERECOMMENDED + 4);
pCmdUI->SetCheck(pCmdUI->m_nID == ID_VIEW_RESTORERECOMMENDED
Now you can specify the restoring ways you want.
<A name=Extended_function_3:_Save/Restore_your_window_to/from_more_than_one_placement>Extended function 3: Save/Restore your window to/from more than one placement
Strictly speaking, this is not an extended function because the basic function has used the functions that I am going to use again. They are member functions of
BOOL SaveWndPos(HWND hWnd, LPCTSTR lpszEntry,
LPCTSTR lpszSection = _T("Position")) const;
BOOL RestoreWndPos(HWND hWnd, LPCTSTR lpszEntry,
LPCTSTR lpszSection = _T("Position"));
Usually, there is no need for the last default parameter to change. As for the second parameter, when you specify a different
lpszEntry, the placement for the window specified by the first parameter
hWnd will be stored to a different position in the registry or in an INI file. The class
_T("Dlg") acquiescently. And
CMDIFrameWndEx specify it to
Just for an example, I will explain how to use it. Also you can use it in any way you want.
Add two toolbar buttons or/and two menu items with the IDs, for example
ID_VIEW_RESTORELASTPOSITION. Then add these message maps to the class
Then implement the message handlers:
Here, we use the string
_T("MainWnd2") to save the window to a different position so that we can easily restore the window using the saved information.
Here I want to discuss the time when to save and restore the window placement.
Since a dialog application as well as a SDI or MDI application receives a
WM_DESTROY message when it exits, it's high time that we save the placement information of the window that is about to be destroyed. This is not very hard to handle.
But, when to restore the window placement seems not so easy. If you call
SetWindowPlacement too early, it's possible that you will fail to restore the window placement you want. Because the window placement may be changed by the MFC architect in the process of creating the window. So the most appropriate time to restore the window placement is when the window is just being to be shown for the first time. As for a dialog, the function
OnInitDialog is a good place to do this.
For a SDI or MDI Frame window, the handler
void OnShowWindow(BOOL bShow, UINT nStatus) of the message
WM_SHOWWINDOW is a good place to do this. But there is another problem that every time
SetWindowPlacement is called, the
WM_SHOWWINDOW will be received again and its handler
OnShowWindow will also be called again. If there is no control, this loop can even crash the application. Since, we just want to call the
SetWindowPlacement once, we can set a control variable for example
m_bEnableRes which is set to
TRUE at an early time, for example, when we handle the message
WM_CREATE, and do the thing like this:
void baseCWindowEx::OnShowWindow(BOOL bShow, UINT nStatus)
if (m_bEnableRes == TRUE && bShow == TRUE)
m_bEnableRes = FALSE;
Note that the parameter
bShow will be set to
TRUE when the window is being shown and to
FALSE when it is to be hidden.
Another problem seems to occur in SDI applications. The window will be shown quickly for a moment when a new document is being created. If you don't change the window placement, it results in flicker. To avoid this flicker, we can set the member variable
m_nCmdShow of the
CWinApp object to
SW_HIDE at an early time, for example, when we handle the message
WM_CREATE. Thus we will not see the flicker in the process of startup and it will seem to be smoother.
- 23rd September, 2005 - Article released.