SubclassDlgItem函数

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/liubing8609/article/details/84928883

SubclassDlgItem函数

SubclassDlgItem函数用来子类化一个控件。Subclass(子类化)MFC中最常用的窗体技术之一。子类化完成两个工作:一是把窗体类对象attach到一个windows窗体实体中(即把一个窗体的hwnd赋给该类)。另外就是把该类对象的消息加入到消息路由中,使得该类可以捕获消息。

OnInitDialog中调用SubclassDlgItem将派生类的控件对象与对话框中的基类控件相连接,则这个基类控件对象变成了派生控件对象。

要在程序中创建新设计的控件,显然不能用自动创建的办法,因为对话框模板对新控件的特性一无所知。程序可以用手工方法创建控件,在调用派生类的Create函数时,派生类会调用基类的Create函数创建控件。用Create函数创建控件是一件比较麻烦的工作,程序需要为函数指定一大堆的控件风格以及控件的坐标和ID。特别是控件的坐标,没有经验的程序员很难确切地安排控件的位置和大小,往往需要反复调整。利用MFCCWnd::SubclassDlgItem提供的动态连接功能,可以避免Create函数的许多麻烦,该函数大大简化了在对话框中创建派生控件的过程。

大家知道,在用手工方法创建控件时,先要构建一个控件对象,然后再用Create函数在屏幕上创建控件窗口,也就是说,控件的创建工作是由控件对象完成的.动态连接的思路则不同,SubclassDlgItem可以把对话框中已有的控件与某个窗口对象动态连接起来,该窗口对象将接管控件的消息处理,从而使控件具有新的特性。SubclassDlgItem函数的声明为:

BOOL SubclassDlgItem( UINT nID, CWnd* pParent );

参数nID是控件的IDpParent是指向父窗口的指针。若连接成功则函数返回TRUE,否则返回FALSE

综上所述,要在程序中使用派生控件,应该按下面两步进行:

  1. 在对话框模板中放置好基类控件。
  2. 在对话框类中嵌入派生控件类的对象。

否则函数将会执行失败,一定要注意!

OnInitDialog中调用SubclassDlgItem将派生类的控件对象与对话框中的基类控件相连接,则这个基类控件变成了派生控件。例如,如果要在对话框中使用新设计的编辑框控件,应先在对话框模板中的合适位置放置一个普通的编辑框,然后,在OnInitDialog函数中按下面的方式调用SubclassDlgItem即可:

BOOL CMyDialog::OnInitDialog()

{

CDialog::OnInitDialog();

m_MyEdit.SubclassDlgItem(IDC_MYEDIT, this);

return TRUE;

}

下面的代码演示通过配置文件创建自定义按钮,使用“SubclassDlgItem”,并解决没有在对话框模板中放置好基类控件的方法:

CxSkinButton *pSkinBnt = NULL;

pSkinBnt = new CxSkinButton; // del at Clear();

//子类化控件

BOOL ret = pSkinBnt->SubclassDlgItem(id,parent);

//如果执行失败,则说明没有在对话框模板中放置好基类控件

if (!ret)

{

       //创建按钮

       UINT stype = WS_CHILD|WS_VISIBLE;

       pSkinBnt->Create(NULL, stype, rect, parent, id);

}

我调用“SubclassDlgItem”函数成功了,为什么按钮却没有显示呢?一共就两个参数,IDParent,检查一下参数传递的是否正确吧!

如果使用了重复的ID,就会出现这种情况。

另外,如果做了变量映射,调用此函数就会触发:Asert(m_hwnd== NULL);

问题缘起

通常如果在对话框中将一个控件映射到一个变量,有三种方法:

1. DDX的方法

2. GetDlgItem的方法,例如CEdit pEdt = (CEdit *)GetDlgItem(IDC_EDIT1);

3. SubclassWindow的方法(或者其扩展SubclassDlgItem),例如
CEdit m_editm_edit.SubclassDlgItem(IDC_EDIT1);

CWnd::SubclassWindow(HWND hWnd)中调用两个主要操作:AttachhWnd)和WNDPROC oldWndProc = (WNDPROC)::SetWindowLong(hWnd, GWL_WNDPROC, (DWORD)AfxGetAfxWndProc());

前者的作用是把CWnd中的m_hWnd设置为hWnd,后者的作用是改变该窗口的窗口函数为AfxGetAfxWndProc()的返回。

AfxGetAfxWndProc返回了AfxWndProc的函数指针,即窗口函数的指针,AfxWndProc包裹了AfxCallWndProc,后者又调用了pWnd->WindowProc(nMsg, wParam, lParam);

可见SubclassWindow完成了两项功能:

  1. 我们对该窗体实例调用成员函数将会直接改变相关窗体句柄对应的窗体(Attach
  2. 系统传给相关窗体句柄的消息会先经过该窗体实例的消息映射(SetWindowLong)。

SubclassDlgItem调用了SubclassWindow,但之前调用了::GetDlgItem获取一个控件ID对应的窗口句柄。

GetDlgItem只是调用::GetDlgItem获得控件ID对应的窗口句柄,然后使用FromHandle将句柄转换为CWnd指针。

SubclassDlgItemGetDlgItem二者的区别

如果只是想调用一个控件对应类的方法,差别不大。只是前者会生成一个类对象,而后者得到指向对象的指针。但如果跟消息有关,则前者会相应消息。例如:

比如我自己写了一个类叫CSuperEdit(父类为CEdit),在该类中我声明了void OnChar(UINT nChar, UINT nRepCnt, UINT nFlags);并在消息循环里添加了ON_WM_CHAR 一行。现在我只要在对话框CProg1Dlg 中声明CSuperEdit m_edit;然后在CProg1Dlg::OnInitDialog中,添加以下代码,就完成了超类化

HWND hWndControl = ::GetDlgItem(pParent->m_hWnd, IDC_EDIT1);

m_edit.SubclassWindow (hWndControl);

通过这种方式,可以动态改变一个控件的消息处理流程,使得CsuperEdit中重载的消息可以被执行。如果不使用子类型化,则无法执行。

猜你喜欢

转载自blog.csdn.net/liubing8609/article/details/84928883