双重子类化(运行时窗体编辑器)






4.83/5 (17投票s)
2003年5月22日
4分钟阅读

92375

1451
本文介绍了一种对已子类化的控件进行子类化(双重子类化)的方法。示例包含一个运行时窗体编辑器。
引言
本文解释了如何对已子类化的控件进行子类化。其中包含一个简单的运行时窗体编辑器。
引言
我在公司工作时有一个项目,需要为不同的客户提供不同的窗体,所以团队决定创建一个运行时窗体编辑器,以便为每个客户编辑窗体。为了实现这一点,我需要一种方法来控制窗体视图或对话框上的所有控件;使我能够调整控件大小、移动它们或隐藏它们。我想对对话框/窗体视图上的控件进行子类化,以便控制它们,然而主要问题是大多数控件已经被子类化了。我编写了一个类(CDblSubclassWnd
),即使控件已被子类化,它也能对其进行子类化。
解释
其工作原理是,消息首先进入一个名为 PreWndProc
的函数,然后继续进入控件的原始 WndProc
,在它完成后,再进入一个名为 PostWndProc
的函数。
该类易于使用。文章中的演示包含一个非常简单的窗体编辑器,允许您在编辑模式下简单地移动窗体上的控件。使用该类非常简单。您需要与 CDblSubclassWnd
类中的三个方法进行交互。所有方法都是静态的。
- 第一步是向该类提供两个函数指针,
CDblSubclassWnd
类将调用它们来传递子类化控件的消息。方法签名如下:
static void SetDblSubclassWndProc( DBLSUBCLASS_WNDPROC *pPREPointer = NULL, DBLSUBCLASS_WNDPROC *pPOSTPointer = NULL );
第一个参数是指向函数的指针(PreWndProc
),该函数将在消息传递给控件的主WndProc
(或另一个 Proc,如果它已被子类化)之前被调用。此函数将为您提供在消息到达控件之前修改消息的选项。第二个参数是指向将在控件处理完消息后调用的函数的指针(PostWndProc
)。您可以使用此指针来检查控件已更改(或返回)的值。DBLSUBCLASS_WNDPROC
函数指针定义如下:typedef LRESULT CALLBACK DBLSUBCLASS_WNDPROC( CWnd*, HWND, UINT, WPARAM, LPARAM, bool & );
第一个参数是指向当前消息所属的控件窗口类的指针。第二个参数是窗口的句柄。第三个参数(
UINT
)是消息编号本身,接下来的两个参数(WPARAM, LPARAM
)是传递给每个窗口消息的两个参数,它们的含义取决于实际的消息。最后一个参数(对 bool 的引用)用于指示您是否希望 DblSubclassed 引擎停止路由消息并返回您的两个函数之一(pPREPointer, pPOSTPointer
)返回的值。为了更清晰地说明这一点,举个例子,如果您有一个按钮并且点击了它,点击消息BN_CLICKED
将首先转到您指定的 PRE 函数指针,如果您不希望此消息继续传递到控件的WndProc
,您可以通过将布尔值(传递给DBLSUBCLASS_WNDPROC
函数的最后一个参数)设置为 false 来阻止此消息,这样消息将被丢弃,不会到达控件。 - 设置好
PreWndProc
和PostWndProc
后,就可以实际调用方法来子类化控件了。void CDblSubclassWnd::SubclassChildsRecurs( HWND hWnd, DBLSUBCLASS_RECURSIVECALLBACKPROC *pFunc )
您调用此方法,例如,提供您正在工作的对话框的句柄。该函数将遍历对话框上的所有控件,并为每个控件调用函数参数DBLSUBCLASS_RECURSIVECALLBACKPROC
所指向的函数。引擎将为将要子类化的每个控件调用此函数,其任务是决定是否要子类化该控件。例如,如果您想子类化对话框上的所有按钮而不是所有控件,在您的
RecursiveCallBack
函数中,您将测试控件是否为按钮(控件的类),并且只为按钮控件返回 true。RecursiveCallBack
仅接受一个HWND
参数(用于对控件进行测试),并返回一个布尔值以指示是否要子类化指定的控件。 - 在子类化控件后,消息将开始流向您在第一步调用
SetDblSubclassWndProc
时指定的PRE
和POST WindowProc
。当您想结束此效果时,可以使用UnSubclassChildsRecurs
来取消双重子类化控件。它的使用方法很简单,您提供主窗口(例如示例中的对话框)的句柄,它将遍历属于它的控件,并取消所有之前被子类化的控件的子类化。
希望您觉得这篇文章对您有所帮助。
历史
发布日期:2003 年 5 月 22 日