WTLATLVisual Studio 6Visual C++ 7.0Windows 2000Visual C++ 6.0Windows XPMFCIntermediateDevVisual StudioWindowsC++
在 ATL 对话框中使用 WTL 进行控件子类化






4.56/5 (9投票s)
2002年3月28日
4分钟阅读

109804

1488
一篇关于如何使用 WTL 和 ATL 进行控件子类的文章
引言
在使用 ATL 创建对话框(例如,复合 ActiveX 控件)时,有时需要更改嵌入的 Windows 控件的行为。例如,可以将 RETURN
键的行为更改为 TAB
键。也可以只允许特定的按键。使用 MFC 时,这是一项相当容易的任务。接下来的内容将展示如何使用 WTL 类的一个子类来完成此任务。使用 WTL 完成此任务的优势在于可以重用 Microsoft 在将所有消息实现为方法方面所做的工作。下面的示例对一个编辑控件进行了子类化,但同样的技术可用于许多其他控件,例如按钮控件。
该任务可以分为四个子任务
- 创建一个 WTL 控件(此处为编辑控件)的子类(从面向对象的角度),该子类可以完成任务
- 将控件添加到项目中
- 在运行时实例化它并将其附加到控件("Windows 子类化"它)
- 测试,调试和测试
重要提示:您需要安装平台 SDK,并将 include 路径设置为 "Microsoft Platform SDK\Src\WTL\Include" 目录,才能使用 WTL 类并运行演示。
任务 1:创建子类
仅仅从 WTL 类CEditT
(CEdit
)创建子类并不是我们想要的,因为这只会使我们将发送给控件的 Windows 消息转换为 C++ 方法调用。这还不够。我们还希望响应 Windows 系统发送给控件的消息。通过从 CWindowImpl
(来自 atlwin.h)进行子类化,我们将获得一个消息映射,这是工作的一半。类的定义可能如下所示template < class T> class CEditEnterAsTabT : public CWindowImpl< CEditEnterAsTabT< T > ,CEdit> { public: BEGIN_MSG_MAP(CEditEnterAsTabT< T >) MESSAGE_HANDLER(WM_CHAR, OnChar) MESSAGE_HANDLER(WM_KEYDOWN, OnKeyDown) END_MSG_MAP() CEditEnterAsTabT(HWND hWnd = NULL){ } CEditEnterAsTabT< T >& operator=(HWND hWnd); LRESULT OnChar(UINT uMsg, WPARAM wParam,LPARAM lParam, BOOL& bHandled); LRESULT OnKeyDown(UINT uMsg, WPARAM wParam,LPARAM lParam, BOOL& bHandled) BOOL AttachToDlgItem(HWND parent, UINT dlgID) } // end class
难点在于为 CWindowImpl
模板提供正确的参数。第一个是类本身(此处为 CEditEnterAsTabT
),第二个是类似于 CWindow
的类(此处为 CEdit
,它与 CEditT< CWindow >
相同)。如您所见,消息映射包含两个消息。我们未处理的消息可能由该控件的超类处理。我们像在任何 MFC 程序中一样为它们添加消息处理程序,只不过我们需要自己进行消息解析。
此类定义给出了以下继承图
如您所见,新类 CEditEnterAsTabT
既是 CEdit
类,也是 CWindow
类,还是 CMessageMap
类。
WM_KEY
和 WM_CHAR
的处理程序。实际上,WM_KEY
应该足够了,但如果我们不实现 WM_CHAR
的处理程序并忽略回车符,我们会听到一声蜂鸣声。代码如下所示LRESULT OnChar(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) { //ignore without a beep switch (wParam) { case '\r': //Carriage return return 0; break; } return DefWindowProc(uMsg, wParam, lParam); } LRESULT OnKeyDown(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) { switch (wParam) { case VK_RETURN: case VK_TAB: ::PostMessage (m_parent, WM_NEXTDLGCTL, 0, 0L); return FALSE; } return DefWindowProc(uMsg, wParam, lParam); }如您所见,如果控件不想要捕获按键或字符,它将调用
DefWindowProc
。任务 2:添加控件
将新类包含在项目中,并在复合控件中创建所需数量的实例。我建议将它们作为复合控件类的私有成员,例如private:
WTL::CEditEnterAsTab m_editEnterAsTab1;
WTL::CEditEnterAsTab m_editEnterAsTab2;
任务 3:实例化和附加
窗口控件的动态附加到我们的类通常在WM_INITDIALOG
(即 OnInitDialog
)的处理程序中完成。通过右键单击复合控件类,选择 "Add Windows Message Handler",然后选择 WM_INITDIALOG
,可以创建此处理程序。当我们希望在控件自己的消息处理程序之前使用我们的消息映射时,我们需要附加自己的窗口过程。我们不需要自己实现过程。它已经完成。我们只需要附加它。这可以通过调用成员方法 SubclassWindow
来完成。我创建了一个方法(AttachToDlgItem
),它将从对话框项 ID 为我们完成此任务。它如下所示BOOL AttachToDlgItem(HWND parent, UINT dlgID)
{
m_dlgItem = dlgID;
m_parent = parent;
m_hWnd = ::GetDlgItem(parent,dlgID);
return SubclassWindow(m_hWnd);
}
现在, OnInitDialog
处理程序可能如下所示LRESULT OnInitDialog(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) { // TODO : Add Code for message handler. // Call DefWindowProc if necessary. m_editEnterAsTab1.AttachToDlgItem(this->m_hWnd,IDC_EDITSUBCLASSED1); m_editEnterAsTab2.AttachToDlgItem(this->m_hWnd,IDC_EDITSUBCLASSED2); return 0; }您的新编辑控件的消息映射现在将在 Windows 控件获得机会之前执行。
任务 4:测试,调试和测试
您现在可以在代码中插入断点并查看发生了什么。要测试演示项目,您必须编译演示项目,使用 ActiveX 控件测试容器并插入SubClassEditCtrlContainer
ActiveX 控件。行为应该是:对于第一个和第三个编辑控件,ENTER
键将用作 TAB
键。