Introduction
Well-designed and well-behaved applications remember where they were on the screen the last time they were executed. I've implemented this behavior several times and in at least three different ways. I decided it was time to formalize this functionality and present it for others to use.
The result is a single header file - WindowPosition.h which can be cut from the body of the article below.
Implementation
CWindowPosition is a Singleton class for saving and restoring the position of a window to a well-known location in the system registry. It can be used as a drop-in module and requires only a few calls in the correct location to be effective. Unfortunately it cannot be done with a single line in the source since this would require depending on destructors firing at the right time, and for most applications, by the time the destructor is called for this class, there is no window left to query for its position information. So, to use this class, instantiate it somewhere convenient while starting your application.
(If you prefer to have a more human readable form of the window position in the registry, use the cs_RegFormat variable at the top of the header file and the WriteProfileString and GetProfileString code below it in the same header file. This shows the two registry methods I have used in the past.)
Here are the steps necessary for a complete implementation.
Set a Registry Key
First, make sure that you have set a registry key for your application. This is typically done in the InitInstance() method of the application. Otherwise the registry settings will be created in your Windows folder, typically C:\Windows\appname.INI where appname is the name you gave your application, in this case it is MyDialog.
BOOL CMyDialog ::InitInstance()
{
.
. Other initialization code here
.
{
SetRegistryKey("WindowPositionApp");
}
CMyDialog dlg;
dlg.DoModal();
.
. Other code here
.
return FALSE;
}
Create the CWindowPosition object
Next, create the window position object. It has to live as long as your application, so make it a member of your dialog class. I made it private as a matter of style. Do as you like.
#include "WindowPosition.h"
class CMyDialog : public CDialog
{
private:
CWindowPosition m_WindowPosition;
public:
CMyDialog(CWnd* pParent = NULL);
.
. Other code here
.
};
Call the constructor
As a matter of form I use the constructor initializer list form for constructing members of my classes. Pass NULL or 'this' as the argument to the constructor. The argument is a pointer to the window that is to be saved and restored. If you pass 'this' you will get a warning 'warning C4355: 'this' : used in base member initializer list'. Microsoft's documentation had this to say, though I had no problems using it this way.
Compiler Warning (levels 1 and 4) C4355
'this' : used in base member initializer list
The this pointer is only valid within nonstatic member functions, but was used in the initializer list for a base class.
This is a level-1 warning when Microsoft extensions are enabled (/Ze) and a level-4 warning otherwise.
#pragma warning ( disable:4355 )
CMyDialog::CMyDialog(CWnd* pParent )
: CDialog(CMyDialog::IDD, pParent),
m_WindowPosition(NULL)
{
.
. Other constructor code here
.
}
Add the Restore position code block
Add code similar to that shown next to restore the window's position, if it has been saved previously. If it has not been saved, then the call to SetWindowPlacement does nothing.
BOOL CMyDialog::OnInitDialog()
{
CDialog::OnInitDialog();
.
. Other code here
.
{
m_WindowPosition.SetPositionWnd(this);
m_WindowPosition.SetWindowPlacement();
}
return TRUE;
}
Add the Save position code block
Now add an override for DestroyWindow and add the single line of code shown
BOOL CMyDialog::DestroyWindow()
{
m_WindowPosition.SaveWindowPlacement();
return CDialog::DestroyWindow();
}
Wrapping it up
I always appreciate it when applications remember where I put them. The functionality is not difficult, and probably should have been included by default in the application framework. Like so many other things in MFC. Sigh! I hope this helps others make me and others like me happier with their applications.
And of course, the code
You can copy the code from here or use the embedded links. Call it whatever you like - I used WindowPosition.h since that is what the class defined in it is called.
const CString cs_RegEntry = "Settings";
const CString cs_RegKey = "WindowPlacement";
class CWindowPosition
{
private:
CWnd* m_pPositionWnd;
public:
CWindowPosition(CWnd* pPositionWnd = NULL) :
m_pPositionWnd(pPositionWnd)
{
};
virtual ~CWindowPosition()
{
};
static CWindowPosition& Instance()
{
static CWindowPosition theOnlyWindowPosition;
return theOnlyWindowPosition;
};
void SetPositionWnd(CWnd* pPositionWnd)
{
Instance().m_pPositionWnd = pPositionWnd;
};
void SaveWindowPlacement()
{
WINDOWPLACEMENT wndpl;
if (Instance().m_pPositionWnd &&
Instance().m_pPositionWnd->GetWindowPlacement(&wndpl))
{
CWinApp* pApp = AfxGetApp();
ASSERT(pApp);
if (pApp)
{
pApp->WriteProfileBinary(cs_RegEntry, cs_RegKey,
(BYTE*)&wndpl, sizeof(wndpl));
}
}
};
void SetWindowPlacement()
{
CWinApp* pApp = AfxGetApp();
if (pApp)
{
WINDOWPLACEMENT* pwndpl;
BYTE* pb = NULL;
UINT nLen = 0;
if (pApp && pApp->GetProfileBinary(cs_RegEntry, cs_RegKey,
&pb, &nLen))
{
pwndpl = reinterpret_cast<WINDOWPLACEMENT*>(pb);
Instance().m_pPositionWnd->SetWindowPlacement(pwndpl);
}
}
};
};
About Jan S. Yoder
Jan has been programming for 15 years since joining Autodesk when they were smallish and located in lovely Sausalito California. After attending Autodesk University (as I like to call it, my equivalent of a BS/CS) for almost five years he left to pursue Pen programming, going on to help found and run two consulting companies. He is now living in the Tampa Bay area. He can be reached at toShay@shay.to , his nom-de-plume.