一个用于保存和恢复窗口位置的插入式类






3.50/5 (6投票s)
2002年12月27日
4分钟阅读

61709

1029
本文介绍了如何保存和恢复基于对话框的应用程序的大小和位置。
引言
设计良好且行为良好的应用程序会记住上次执行时在屏幕上的位置。 我已经多次以至少三种不同的方式实现了这种行为。 我决定是时候将此功能形式化并提供给其他人使用了。
结果是一个单独的头文件 - WindowPosition.h,可以从下面的文章正文中剪切。
实现
CWindowPosition 是一个 Singleton 类,用于将窗口的位置保存和恢复到系统注册表中的已知位置。 它可以作为一个即插即用模块使用,并且只需要在正确的位置进行少量调用即可生效。 不幸的是,它不能用源代码中的单行完成,因为这需要依赖于在正确时间触发的析构函数,并且对于大多数应用程序,当为此类调用析构函数时,没有窗口可以查询其位置信息。 因此,要使用此类,请在启动应用程序时在方便的位置实例化它。
(如果您希望在注册表中具有更易于理解的窗口位置形式,请使用头文件顶部的 cs_RegFormat 变量以及同一头文件中下面的 WriteProfileString 和 GetProfileString 代码。 这显示了我过去使用过的两种注册表方法。)
以下是完整实现所需的步骤。
设置注册表项
首先,确保您已为您的应用程序设置了注册表项。 这通常在应用程序的 InitInstance() 方法中完成。 否则,注册表设置将在您的 Windows 文件夹中创建,通常是 C:\Windows\appname.INI,其中 appname 是您为应用程序指定的名称,在本例中是 MyDialog。
BOOL CMyDialog ::InitInstance() { // Standard initialization . . Other initialization code here . // Start WindowPlacement code block { // Set the registry key. Otherwise application settings will be
// placed // in an INI file in the Windows folder. SetRegistryKey("WindowPositionApp"); } // End WindowPlacement code block // For a dialog application the creation of the dialog is here CMyDialog dlg; dlg.DoModal(); . . Other code here . // Since the dialog has been closed, return FALSE so that we exit the // application, rather than start the application's message pump. return FALSE; }
创建 CWindowPosition 对象
接下来,创建窗口位置对象。 它必须与您的应用程序一样长久,因此将其作为对话框类的成员。 我把它设为私有的,这是一种风格问题。 您可以随意操作。
// Include the window position header file #include "WindowPosition.h" class CMyDialog : public CDialog { private: // Create the window position object and make it private CWindowPosition m_WindowPosition; // Construction public: CMyDialog(CWnd* pParent = NULL); // standard constructor . . Other code here . };
调用构造函数
作为一种形式,我使用构造函数初始化列表形式来构造类的成员。 将 NULL 或 'this' 作为参数传递给构造函数。 该参数是指向要保存和恢复的窗口的指针。 如果您传递 'this',您将收到警告 'warning C4355: 'this' : used in base member initializer list'。 Microsoft 的文档对此进行了说明,但我以这种方式使用它没有任何问题。
编译器警告(级别 1 和 4)C4355
'this' : 在基成员初始化列表中使用
this 指针仅在非静态成员函数中有效,但在基类的初始化列表中使用。
启用 Microsoft 扩展 (/Ze) 时,这是一个级别 1 警告,否则为级别 4 警告。
// turn off 'warning C4355: 'this' : used in base member initializer list' #pragma warning ( disable:4355 ) // Constructor CMyDialog::CMyDialog(CWnd* pParent /*=NULL*/) : CDialog(CMyDialog::IDD, pParent), m_WindowPosition(NULL) { . . Other constructor code here . }
添加恢复位置代码块
添加类似于下一个所示的代码,以恢复窗口的位置(如果之前已保存)。 如果尚未保存,则对 SetWindowPlacement 的调用不执行任何操作。
BOOL CMyDialog::OnInitDialog() { CDialog::OnInitDialog(); . . Other code here . // Start WindowPlacement code block { // Be sure to set the registry key in the main application module. // Otherwise application settings will be placed in an INI file in
// the Windows folder. // Set the pointer to the window whose position is to be saved/
// restored. You can also initialize it in the constructor if you
// wish. Microsoft issues a 'warning C4355: 'this' : used in base
// member initializer list' if you do so, but it works nontheless.
// We'll set it explicitly here. m_WindowPosition.SetPositionWnd(this); // Restore the window position, if previously saved. m_WindowPosition.SetWindowPlacement(); } // End WindowPlacement code block // TODO: Add extra initialization here return TRUE; // return TRUE unless you set the focus to a control }
添加保存位置代码块
现在为 DestroyWindow 添加一个重写,并添加显示的单行代码
// Override DestroyWindow() for a dialog application. BOOL CMyDialog::DestroyWindow() { m_WindowPosition.SaveWindowPlacement(); return CDialog::DestroyWindow(); }
总结
我总是很感激应用程序记住我把它们放在哪里。 该功能并不困难,并且可能应该默认包含在应用程序框架中。 就像 MFC 中的许多其他东西一样。 唉! 我希望这能帮助其他人让我和像我一样的人对他们的应用程序感到满意。
当然,还有代码
您可以从此处复制代码或使用嵌入式链接。 随意调用它 - 我使用了 WindowPosition.h,因为这是其中定义的类的名称。
// WindowPosition.h // // Copyright(C) 1999-2003 toShay Consulting // All rights reserved. // // toShay Consulting grants you ("Licensee") a non-exclusive, royalty free, // licence to use, modify and redistribute this software in source and binary // code form, provided that i) this copyright notice and licence appear on
// all copies of the software; and ii) Licensee does not utilize the software
// in a manner which is disparaging to toShay Consulting. // // This software is provided "as is" without a warranty of any kind. All // express or implied conditions, representations and warranties, including // any implied warranty of merchantability, fitness for a particular purpose // or non-infringement, are hereby excluded. toShay Consulting and its
// licensors shall not be liable for any damages suffered by licensee as a
// result of using, modifying or distributing the software or its derivatives.
// In no event will toShay Consulting be liable for any lost revenue, profit
// or data, or for direct, indirect, special, consequential, incidental or
// punitive damages, however caused and regardless of the theory of liability,
// arising out of the use of or inability to use software, even if toShay
// Consulting has been advised of the possibility of such damages. // // This software is not designed or intended for use in on-line control of // aircraft, air traffic, aircraft navigation or aircraft communications; or
// in the design, construction, operation or maintenance of any nuclear // facility. Licensee represents and warrants that it will not use or // redistribute the Software for such purposes. // ////////////////////////////////////////////////////////////////////////
// Strings used in saving and restoring window position information // in the Registry. const CString cs_RegEntry = "Settings"; const CString cs_RegKey = "WindowPlacement"; // const CString cs_RegFormat = "%ld,%ld,%ld,%d,%d,%d,%d,%d,%d,%d,%d"; // // 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 above and the
// WriteProfileString and GetProfileString code below. // // Here are the steps necessary for a complete implementation in a dialog
// application. First, make sure that you have set a registry key for your
// application. This is typically done in the InitInstance() method of
// the application. // // BOOL CWindowPositionApp::InitInstance() // { // // Standard initialization // . // . Other initialization code here // . // // Start WindowPlacement code block // { // // Set the registry key. Otherwise application settings will be
// // placed in an INI file in the Windows folder. // SetRegistryKey("WindowPositionApp"); // } // // End WindowPlacement code block // // // For a dialog application the creation of the dialog is here // CMyDialog dlg; // dlg.DoModal(); // . // . Other code here // . // // // Since the dialog has been closed, return FALSE so that we exit the // // application, rather than start the application's message pump. // return FALSE; // } // // // Include the window position header file // #include "WindowPosition.h" // // class CMyDialog : public CDialog // { // private: // // Create the window position object and make it private // CWindowPosition m_WindowPosition; // // // Construction // public: // CMyDialog(CWnd* pParent = NULL); // standard constructor // // . // . Other code here // . // }; // // // Constructor // CMyDialog::CMyDialog(CWnd* pParent /*=NULL*/) // : CDialog(CMyDialog::IDD, pParent), // m_WindowPosition(this) // { // . // . Other constructor code here // . // } // // BOOL CMyDialog::OnInitDialog() // { // CDialog::OnInitDialog(); // . // . // . // // Start WindowPlacement code block // { // // Be sure to set the registry key in the main application module. // // Otherwise application settings will be placed in an INI file
// //in the Windows folder. // // // Set the pointer to the window whose position is to be saved/
// // restored. You can also initialize it in the constructor if you
// // wish. Microsoft issues a 'warning C4355: 'this' : used in base
// // member initializer list' if you do so, but it works nontheless.
// // We'll set it explicitly here. // m_WindowPosition.SetPositionWnd(this); // // Restore the window position, if previously saved. // m_WindowPosition.SetWindowPlacement(); // } // // End WindowPlacement code block // // // TODO: Add extra initialization here // // return TRUE; // return TRUE unless you set the focus to a control // } // // // Override DestroyWindow() for a dialog application. // BOOL CMyDialog::DestroyWindow() // { // m_WindowPosition.SaveWindowPlacement(); // return CDialog::DestroyWindow(); // } // // class CWindowPosition { private: CWnd* m_pPositionWnd; public: CWindowPosition(CWnd* pPositionWnd = NULL) : m_pPositionWnd(pPositionWnd) { }; virtual ~CWindowPosition() { }; // The singleton static CWindowPosition& Instance() { static CWindowPosition theOnlyWindowPosition; return theOnlyWindowPosition; }; void SetPositionWnd(CWnd* pPositionWnd) { Instance().m_pPositionWnd = pPositionWnd; }; void SaveWindowPlacement() { // Save the last window size and position WINDOWPLACEMENT wndpl; if (Instance().m_pPositionWnd &&
Instance().m_pPositionWnd->GetWindowPlacement(&wndpl)) { CWinApp* pApp = AfxGetApp(); ASSERT(pApp); /* CString strWP; strWP.Format(cs_RegFormat, wndpl.flags, wndpl.length, wndpl.showCmd, wndpl.ptMaxPosition.x, wndpl.ptMaxPosition.y, wndpl.ptMinPosition.x, wndpl.ptMinPosition.y, wndpl.rcNormalPosition.left, wndpl.rcNormalPosition.top, wndpl.rcNormalPosition.right, wndpl.rcNormalPosition.bottom); pApp->WriteProfileString(cs_RegEntry, cs_RegKey, strWP); */ if (pApp) { pApp->WriteProfileBinary(cs_RegEntry, cs_RegKey,
(BYTE*)&wndpl, sizeof(wndpl)); } } }; void SetWindowPlacement() { CWinApp* pApp = AfxGetApp(); if (pApp) { /* WINDOWPLACEMENT wndpl; CString strWP; strWP = pApp->GetProfileString(cs_RegEntry, cs_RegKey, NULL); if (!strWP.IsEmpty()) { sscanf(strWP, cs_RegFormat, &wndpl.flags, &wndpl.length, &wndpl.showCmd, &wndpl.ptMaxPosition.x, &wndpl.ptMaxPosition.y, &wndpl.ptMinPosition.x, &wndpl.ptMinPosition.y, &wndpl.rcNormalPosition.left, &wndpl.rcNormalPosition.top, &wndpl.rcNormalPosition.right, &wndpl.rcNormalPosition.bottom); Instance().m_pPositionWnd->SetWindowPlacement(&wndpl); } */ 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); } } }; };
关于 Jan S. Yoder
自从 Jan 加入 Autodesk 时,他们还是个小公司,位于美丽的加利福尼亚州索萨利托,至今已有 15 年的编程经验。 在参加了 Autodesk University(我喜欢这样称呼它,相当于我的 BS/CS)近五年后,他离开了公司去从事 Pen 编程,并继续帮助创立和运营了两家咨询公司。 他现在住在坦帕湾地区。 可以通过 toShay@shay.to 联系他,这是他的笔名。