CWnd 辅助类






4.98/5 (16投票s)
2002年4月9日
6分钟阅读

86727

1183
包含静态函数和嵌套类的类,
类定义
成员函数
嵌套类
用法
多年来,我一直在处理许多基于对话框的应用程序,并且需要执行相同类型的任务。
- 当对话框关闭时保存其位置和/或大小,以便下次可以以相同的方式显示。
- 通过像素数更改控件的位置和/或大小。
- 使多个控件相对于一个控件对齐。
- 检索给定文本在窗口上显示时将占用的像素数。
- 确保函数退出时控件获得焦点。
- 在执行某些任务时暂时禁用控件/窗口。
- 挂钩一个或多个消息以执行特定任务。
这些都是非常有用的任务, CWnd
类不幸地缺少这些功能,所以最初我会反复编写成员函数和类来完成这些任务——大量地复制粘贴。过了一段时间,我决定是时候为它们创建一个类了。
理想情况下,这些任务应该属于基类,例如CWnd
,但更改 MFC 的代码根本不是一个选项。因此,次优选择是:创建一个辅助类,并将这些任务作为静态成员或嵌套类添加到其中。 CAMSWnd
就是这样的一个类——一个简单的命名空间风格的类,其中包含静态函数和嵌套类,它们与单独传递给它们的 CWnd
对象一起工作。
类定义
以下是CAMSWnd
的完整定义
class CAMSWnd { public: enum Flags // Bit values passed to the uFlags parameter of the functions below. { Left = 0x0001, // left coordinate Right = 0x0002, // right coordinate Top = 0x0004, // top coordinate Bottom = 0x0008, // bottom coordinate X = 0x0003, // horizontal coordinates (left and right) Y = 0x000C, // vertical coordinates (top and bottom) Position = 0x000F, // both the horizontal and vertical coordinates Width = 0x0010, // the window's width Height = 0x0020, // the window's height Size = 0x0030, // the window's width and height Both = 0x003F, // the window's position and size State = 0x0040, // the window's state (minimized, maximized, etc.) All = 0x007F, // the window's position, size, and state NoRedraw = 0x1000 // the window is not repainted }; static void Save(CWnd* pWnd, LPCTSTR szWindowName = NULL); static void Restore(CWnd* pWnd, LPCTSTR szWindowName = NULL, unsigned uFlags = Position); static void ChangeBy(CWnd* pWnd, int nPixels, unsigned uFlags); static void AlignControl(CWnd* pWnd, unsigned uCtrlToAlign, unsigned uCtrlToAlignAgainst, unsigned uFlags, int nOffset = 0); static void OrganizeSequentialControls(CWnd* pWnd, unsigned uFirstCtrl, unsigned uLastCtrl, int nDistance, unsigned uFlags = CAMSWnd::Y); static int GetTextExtent(CWnd* pWnd, const CString& strText, CFont* pFont = NULL); // Class FocusHolder allows you to save a window object in construction // whose focus will be set on destruction. // class FocusHolder { public: FocusHolder(CWnd* pWnd); FocusHolder(CWnd& wnd); ~FocusHolder(); private: CWnd* m_pWnd; }; // Class Disabler allows you to disable a window in construction and // reenable it on destruction. // class Disabler { public: Disabler(CWnd* pWnd); Disabler(CWnd& wnd); ~Disabler(); private: CWnd* m_pWnd; }; // Class Hook is an abstract class used for intercepting a given window's messages // before they're processed. // class Hook { public: Hook(CWnd* pWnd = NULL); virtual ~Hook(); virtual BOOL Open(CWnd* pWnd); virtual void Close(); BOOL IsOpen() const; CWnd* GetWindow() const; HWND GetHwnd() const; static LRESULT CALLBACK HookedWindowProc(HWND, UINT, WPARAM, LPARAM); protected: virtual LRESULT WindowProc(UINT message, WPARAM wParam, LPARAM lParam) = 0; LRESULT Default(); // call this at the end of handler fns private: HWND m_hWnd; // the window being hooked WNDPROC m_pWndProcOld; // the original window proc private: // map of windows to hooks static CMap<HWND, HWND&, Hook*, Hook*&> m_mapHookedWindows; }; // Class PlacementHook is used to easily save and restore given window's position // and size. Simply derive from it and call one of the Set functions inside // OnCreate or OnInitDialog. // class PlacementHook : private Hook { public: PlacementHook(); void SetLastPositionAndSize(CWnd* pWnd, LPCTSTR szWindowName = NULL); void SetLastPosition(CWnd* pWnd, LPCTSTR szWindowName = NULL); void SetLastSize(CWnd* pWnd, LPCTSTR szWindowName = NULL); protected: void Open(CWnd* pWnd, LPCTSTR szWindowName); virtual LRESULT WindowProc(UINT message, WPARAM wParam, LPARAM lParam); private: CString m_strWindowName; // the name of the window (to be saved // in the registry) unsigned m_uFlags; // how the window should be restored }; };
成员函数
下面是CAMSWnd
中每个静态成员函数的描述,尽管它们应该是不言自明的。
保存
void Save(CWnd* pWnd, LPCTSTR szWindowName = NULL)
将pWnd
的位置、大小和状态(最小化、最大化等)写入应用程序配置文件(INI文件或注册表)。如果szWindowName
为NULL(推荐),则窗口标题文本用作在INI文件或注册表中保存信息的键。因此,如果窗口文本不总是一致或唯一的,您应该在szWindowName
参数中传递它。
我主要将此函数用于我的对话框。我只是为WM_DESTROY
消息添加了一个处理程序,并在那里调用了此函数。
void CMyDialog::OnDestroy() { CAMSWnd::Save(this); CDialog::OnDestroy(); }
请查看下面的PlacementHook类以获取更好的替代方案。
注意:请记住在您的 CWinApp 的 InitInstance
中调用 SetRegistryKey
,以便使用注册表而不是 INI 文件。
恢复
void Restore(CWnd* pWnd, LPCTSTR szWindowName = NULL, unsigned uFlags = Position)
根据uFlags的值,通过从应用程序配置文件(INI文件或注册表)中检索以前由Save函数(上文)写入的信息,恢复pWnd的一个或多个方面。
如果szWindowName
为NULL(推荐),则窗口标题文本用作从INI文件或注册表检索信息的键。因此,如果窗口文本不总是始终一致或唯一的,您应该在szWindowName
参数中传递它。本质上,此参数应与传递给Save函数的值相同。
我曾在WM_INITDIALOG
处理程序中调用此函数,以从注册表中恢复我的对话框位置。
BOOL CMyDialog::OnInitDialog() { CDialog::OnInitDialog(); CAMSWnd::Restore(this); return TRUE; }
然后,我发现了一种更好的方法:下面的PlacementHook类。
注意:请记住在您的 CWinApp 的 InitInstance
中调用 SetRegistryKey
,以便使用注册表而不是 INI 文件。
改变通过
void ChangeBy(CWnd* pWnd, int nPixels, unsigned uFlags)
根据uFlags的值,通过给定的nPixels
数改变pWnd
的一个或多个方面。CAMSWnd::State
标志不适用于此处,但CAMSWnd::NoRedraw
标志可用于防止窗口不必要的重绘。
这个函数在作为一个组移动一组控件时非常有用。
// Move these two buttons down 100 pixels from their current position CAMSWnd::ChangeBy(GetDlgItem(IDOK), 100, CAMSWnd::Y); CAMSWnd::ChangeBy(GetDlgItem(IDCANCEL), 100, CAMSWnd::Y); // Make the following combo box 20 pixels wider CAMSWnd::ChangeBy(GetDlgItem(IDC_COMBO1), 20, CAMSWnd::Width);
对齐控件
void AlignControl(CWnd* pWnd, unsigned uCtrlToAlign, unsigned uCtrlToAlignAgainst, unsigned uFlags, int nOffset = 0)
根据uCtrlToAlignAgainst
的当前位置和/或大小对齐给定的uCtrlToAlign
。此外,对齐的控件可以放置在距离uCtrlToAlignAgainst
nOffset
像素的位置。uFlags可以是CAMSWnd::Left
、CAMSWnd::Right
、CAMSWnd::Top
、CAMSWnd::Bottom
、CAMSWnd::Width
或CAMSWnd::Height
的任意组合。
我曾使用此函数作为ChangeBy
(上文)的替代方案,尽管它还有其他用途。
// Align both buttons on the same Y position CAMSWnd::ChangeBy(GetDlgItem(IDOK), 100, CAMSWnd::Y); CAMSWnd::AlignControl(this, IDCANCEL, IDOK, CAMSWnd::Y);
组织顺序控件
void OrganizeSequentialControls(CWnd* pWnd, unsigned uFirstCtrl, unsigned uLastCtrl, int nDistance, unsigned uFlags = CAMSWnd::Y)
将给定的一组顺序编号的控件按给定的nDistance
(像素)水平(CAMSWnd::X)或垂直(CAMSWnd::Y)排列。结果是,每个控件看起来都与第一个完全相同,但与其水平或垂直位置相隔nDistance像素。
我在几个包含多行多列编辑控件的大型对话框中使用了这个函数,所有这些控件都需要统一大小和间距。不幸的是,由于DevStudio的编辑器使用愚蠢的对话框单位,我不得不通过编程方式来实现。
CAMSWnd::OrganizeSequentialControls(this, IDC_EDIT1, IDC_EDIT5, 13);
获取文本范围
int GetTextExtent(CWnd* pWnd, const CString& strText, CFont* pFont = NULL)
确定在给定pWnd
的设备上下文中显示给定strText
所需的像素数。如果pFont
不为NULL,则将其临时选入窗口的设备上下文并用于计算。
这个函数在我之前需要动态创建一个窗口来显示一小段文本时派上了用场。
嵌套类
下面是CAMSWnd
内部每个嵌套类的描述。
焦点保持器
FocusHolder 类确保在一段代码执行完毕后,焦点被设置到特定的窗口(例如控件)。您只需通过传递需要设置焦点的窗口来构造它,然后“忘记它”——FocusHolder 的析构函数会完成其余的工作。您可以使用 CWnd
指针或引用来构造它。
我主要在两种情况下使用这个类:(1)作为带有旁边浏览按钮的编辑控件,(2)作为带有一个或多个相关按钮(添加、编辑、删除等)的项目列表。这是一个删除按钮处理程序的示例:
void CMyDialog::OnButtonDeleteFile() { // sets the window to hold focus CAMSWnd::FocusHolder focus = m_ctlListFiles; int nSelectedCount = m_ctlListFiles.GetSelectedCount(); if (nSelectedCount == 0) return; m_ctlListFiles.DeleteItems(); UpdateControls(); }
禁用器
这个类的工作方式与FocusHolder非常相似,不同之处在于它在构造时临时禁用一个窗口,并在销毁时重新启用它。您可以使用CWnd
指针或引用来构造它。
我发现它在特殊情况下非常方便,即当前窗口需要暂时禁用,以给单独的弹出窗口一种模态的错觉。
void CMyDialog::OnDoSomethingSpecial() { // disable the dialog box while the pop up is shown CAMSWnd::Disabler disable = this; PopupSomeWindow(); }
Hook
这是一个抽象类,允许您在拦截一个或多个窗口消息时将特定操作对象化。一个 Hook 派生类的实例基本上通过其 Open 函数将自身附加到特定窗口,然后通过其 WindowProc
虚拟函数开始接收所有窗口消息。“挂钩”是通过 SetWindowLong
API 完成的,因此不需要特殊的挂钩 DLL。如何使用此类的一个很好的例子是接下来的 PlacementHook 类。
放置挂钩
这个类实现了Hook类,为保存和恢复窗口的位置和/或大小提供了一个很好的替代方案。你只需从它派生你的对话框或窗口类,然后在WM_INITDIALOG
或WM_CREATE
处理程序中调用它的三个成员函数之一(SetLastPositionAndSize
、SetLastPosition
或SetLastSize
)。就是这样!
我就是这样使用它来保存/恢复我的对话框位置的
// Inside MyDialog.h class CMyDialog : public CDialog, CAMSWnd::PlacementHook { ... };
// Inside MyDialog.cpp BOOL CTestTextWriterDlg::OnInitDialog() { CDialog::OnInitDialog(); // Note: window must exist before calling this SetLastPosition(this); return TRUE; }
用法
要在您的项目中使用 CAMSWnd
类
- 将 amsWnd.cpp 和 amsWnd.h 添加到您的项目中。
- 在您的源文件中包含 amsWnd.h。我建议将其包含在 stdafx.h 中,这样只需在一个地方完成。
- 在任何需要它们的地方使用它的静态成员和类。
- 尽情享用!