Saving a windows size position and state in MFC






4.72/5 (23 votes)
May 31, 2001
3 min read

198871
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:
- If your application hides the window and shows it during program execution, it will be displayed at the saved coordinates each time.
SetWindowPlacement
causes aWM_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!