65.9K
CodeProject 正在变化。 阅读更多。
Home

在 ATL 对话框中使用 WTL 进行控件子类化

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.56/5 (9投票s)

2002年3月28日

4分钟阅读

viewsIcon

109804

downloadIcon

1488

一篇关于如何使用 WTL 和 ATL 进行控件子类的文章

Sample Image - compositeCtrl.jpg

引言

在使用 ATL 创建对话框(例如,复合 ActiveX 控件)时,有时需要更改嵌入的 Windows 控件的行为。例如,可以将 RETURN 键的行为更改为 TAB 键。也可以只允许特定的按键。使用 MFC 时,这是一项相当容易的任务。接下来的内容将展示如何使用 WTL 类的一个子类来完成此任务。使用 WTL 完成此任务的优势在于可以重用 Microsoft 在将所有消息实现为方法方面所做的工作。下面的示例对一个编辑控件进行了子类化,但同样的技术可用于许多其他控件,例如按钮控件。

该任务可以分为四个子任务

  • 创建一个 WTL 控件(此处为编辑控件)的子类(从面向对象的角度),该子类可以完成任务
  • 将控件添加到项目中
  • 在运行时实例化它并将其附加到控件("Windows 子类化"它)
  • 测试,调试和测试

重要提示:您需要安装平台 SDK,并将 include 路径设置为 "Microsoft Platform SDK\Src\WTL\Include" 目录,才能使用 WTL 类并运行演示。

任务 1:创建子类

仅仅从 WTL 类 CEditTCEdit)创建子类并不是我们想要的,因为这只会使我们将发送给控件的 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_KEYWM_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 键。
© . All rights reserved.