科技行者

行者学院 转型私董会 科技行者专题报道 网红大战科技行者

知识库

知识库 安全导航

至顶网软件频道基础软件MFC程序员的WTL指南之对话框与控件

MFC程序员的WTL指南之对话框与控件

  • 扫一扫
    分享文章到微信

  • 扫一扫
    关注官方公众号
    至顶头条

MFC 的对话框和控件的封装真得可以节省你很多时间和功夫

作者:lithe 来源:BLOG 2007年10月19日

关键字:

  • 评论
  • 分享微博
  • 分享邮件
使用控件的封装类

  有几种方法将一个变量和控件建立关联,可以使用CWindows(或其它Window接口类,如CListViewCtrl),也可以使用CWindowImpl的派生类。如果只是需要一个临时变量就用CWindow,如果需要子类化一个控件并处理发送给该控件的消息就需要使用CWindowImpl。

  ·ATL 方式 1 - 连接一个CWindow对象最简单的方法是声明一个CWindow或其它window接口类,然后调用Attach()方法,还可以使用CWindow的构造函数直接将变量与控件的HWND关联起来。

  下面的代码三种方法将变量和一个list控件联系起来:

HWND hwndList = GetDlgItem(IDC_LIST);
CListViewCtrl wndList1 (hwndList); // use constructor
CListViewCtrl wndList2, wndList3;

wndList2.Attach ( hwndList ); // use Attach method
wndList3 = hwndList; // use assignment operator

  记住CWindow的析构函数并不销毁控件窗口,所以在变量超出作用域时不需要将其脱离控件,如果你愿意的话还可以将其作为成员变量使用:你可以在OnInitDialog()处理函数中建立变量与控件的联系。

  ·ATL 方式 2 - 包容器窗口(CContainedWindow)
 
  CContainedWindow是介于CWindow和CWindowImpl之间的类,它可以子类化控件,在控件的父窗口中处理控件的消息,这使得所有的消息处理都放在对话框类中,不需要为为每个控件生成一个单独的CWindowImpl派生类对象。需要注意的是不能用CContainedWindow 处理WM_COMMAND, WM_NOTIFY和其他通知消息,因为这些消息是发给控件的父窗口的。

  CContainedWindow只是CContainedWindowT定义的一个数据类型,CContainedWindowT才是真正的类,它是一个模板类,使用window接口类的类名作为模板参数。这个特殊的CContainedWindowT<CWindow>和CWindow功能一样,
CContainedWindow只是它定义的一个简写名称,要使用不同的window接口类只需将该类的类名作为模板参数就行了,例如CContainedWindowT<CListViewCtrl>。

  钩住一个CContainedWindow对象需要做四件事:

  ·在对话框中创建一个CContainedWindowT 成员变量。

  ·将消息处理添加到对话框消息映射的ALT_MSG_MAP小节。

  ·在对话框的构造函数中调用CContainedWindowT 构造函数并告诉它哪个ALT_MSG_MAP小节的消息需要处理。

  ·在OnInitDialog()中调用CContainedWindowT::SubclassWindow() 方法与控件建立关联。

  在ControlMania1中,我对三个按钮分别使用了一个CContainedWindow,对话框处理发送到每一个按钮的WM_SETCURSOR消息,并改变鼠标指针形状。

  现在仔细看看这一步,首先,我们在CMainDlg中添加了CContainedWindow成员。

class CMainDlg : public CDialogImpl<CMainDlg>
{
 // ...
 protected:
 CContainedWindow m_wndOKBtn, m_wndExitBtn;
};

  其次,我们添加了ALT_MSG_MAP小节,OK按钮使用1小节,Exit按钮使用2小节。这意味着所有发送给OK按钮的消息将由ALT_MSG_MAP(1)小节处理,所有发给Exit按钮的消息将由ALT_MSG_MAP(2)小节处理。

class CMainDlg : public CDialogImpl<CMainDlg>
{
 public:
  BEGIN_MSG_MAP_EX(CMainDlg)
   MESSAGE_HANDLER(WM_INITDIALOG, OnInitDialog)
   COMMAND_ID_HANDLER(ID_APP_ABOUT, OnAppAbout)
   COMMAND_ID_HANDLER(IDOK, OnOK)
   COMMAND_ID_HANDLER(IDCANCEL, OnCancel)
   ALT_MSG_MAP(1)
    MSG_WM_SETCURSOR(OnSetCursor_OK)
   ALT_MSG_MAP(2)
    MSG_WM_SETCURSOR(OnSetCursor_Exit)
  END_MSG_MAP()

  LRESULT OnSetCursor_OK(HWND hwndCtrl, UINT uHitTest, UINT uMouseMsg);
  LRESULT OnSetCursor_Exit(HWND hwndCtrl, UINT uHitTest, UINT uMouseMsg);
};

  接着,我们调用每个CContainedWindow的构造函数,告诉它使用ALT_MSG_MAP的哪个小节。

CMainDlg::CMainDlg() : m_wndOKBtn(this, 1),
m_wndExitBtn(this, 2)
{
}

  构造函数的参数是消息映射链的地址和ALT_MSG_MAP的小节号码,第一个参数通常使用this,就是使用对话框自己的消息映射链,第二个参数告诉对象将消息发给ALT_MSG_MAP的哪个小节。

  最后,我们将每个CContainedWindow对象与控件关联起来。

LRESULT CMainDlg::OnInitDialog(...)
{
 // ...
 // Attach CContainedWindows to OK and Exit buttons
 m_wndOKBtn.SubclassWindow ( GetDlgItem(IDOK) );
 m_wndExitBtn.SubclassWindow ( GetDlgItem(IDCANCEL) );

 return TRUE;
}


  下面是新的WM_SETCURSOR消息处理函数:

LRESULT CMainDlg::OnSetCursor_OK (HWND hwndCtrl, UINT uHitTest, UINT uMouseMsg )
{
 static HCURSOR hcur = LoadCursor ( NULL, IDC_HAND );

 if ( NULL != hcur )
 {
  SetCursor ( hcur );
  return TRUE;
 }
 else
 {
  SetMsgHandled(false);
  return FALSE;
 }
}

LRESULT CMainDlg::OnSetCursor_Exit ( HWND hwndCtrl, UINT uHitTest, UINT uMouseMsg )
{
 static HCURSOR hcur = LoadCursor ( NULL, IDC_NO );

 if ( NULL != hcur )
 {
  SetCursor ( hcur );
  return TRUE;
 }
 else
 {
  SetMsgHandled(false);
  return FALSE;
 }
}

  如果你还想使用按钮类的特性,你需要这样声明变量:

CContainedWindowT<CButton> m_wndOKBtn;

  这样就可以使用CButton类的方法。

  当你把鼠标光标移到这些按钮上就可以看到WM_SETCURSOR消息处理函数的作用结果:


 
    • 评论
    • 分享微博
    • 分享邮件
    邮件订阅

    如果您非常迫切的想了解IT领域最新产品与技术信息,那么订阅至顶网技术邮件将是您的最佳途径之一。

    重磅专题
    往期文章
    最新文章