DDX_Control、SubclassWindow和SubclassDlgItem

文章参考地址:http://blog.sina.com.cn/s/blog_86fe5b440101au88.htmlhttp://www.cnblogs.com/riskyer/p/3424278.html

问题缘起

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

1. DDX的方法

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

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

SubclassWindow

CWnd::SubclassWindow(HWND hWnd)中调用两个主要操作:Attach(hWnd)和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指针。

SubclassDlgItem和GetDlgItem二者的区别

如果只是想调用一个控件对应类的方法,差别不大。只是前者会生成一个类对象,而后者得到指向对象的指针。

但如果跟消息有关,则前者会相应消息。例如:

比如我自己写了一个类叫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);
}

或者

BOOL CProg1Dlg::OnInitDialog()
{
CDialog::OnInitDialog();
m_edit.SubclassDlgItem(IDC_EDIT1, this);
return TRUE;
}

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



在自绘窗口的时候,子类化是MFC最常用的窗体技术之一。什么是子类化?窗口子类化就是创建一个新的窗口函数代替原来的窗口函数。

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

而通常我们会碰到DDX_Control、SubclassWindow、SubclassDlgItem等,不同的子类化方法。首先先看下面的代码:

void AFXAPI DDX_Control(CDataExchange* pDX, int nIDC, CWnd& rControl)
{
if ((rControl.m_hWnd == NULL) && (rControl.GetControlUnknown() == NULL))    // not subclassed yet
{
ASSERT(!pDX->m_bSaveAndValidate);

pDX->PrepareCtrl(nIDC);
  HWND hWndCtrl;
  pDX->m_pDlgWnd->GetDlgItem(nIDC, &hWndCtrl);
if ((hWndCtrl != NULL) && !rControl.SubclassWindow(hWndCtrl))
{
ASSERT(FALSE);      // possibly trying to subclass twice?
AfxThrowNotSupportedException();
}
#ifndef _AFX_NO_OCC_SUPPORT
else
{
 if (hWndCtrl == NULL)
 {
if (pDX->m_pDlgWnd->GetOleControlSite(nIDC) != NULL)
{
   rControl.AttachControlSite(pDX->m_pDlgWnd, nIDC);
}
 }
 else
 {
   // If the control has reparented itself (e.g., invisible control),
   // make sure that the CWnd gets properly wired to its control site.
   if (pDX->m_pDlgWnd->m_hWnd != ::GetParent(rControl.m_hWnd))
   rControl.AttachControlSite(pDX->m_pDlgWnd);
 }
}
#endif //!_AFX_NO_OCC_SUPPORT

}
}

我们发现 DDX_Control()函数中调用了SubclassWindow(),再看SubclassWindow()里写了什么:

// From VS Install PathVC98MFCSRCWINCORE.CPP
BOOL CWnd::SubclassWindow(HWND hWnd)
{
    if (!Attach(hWnd))
        return FALSE;  

    // allow any other subclassing to occur
    PreSubclassWindow();  

    // now hook into the AFX WndProc
    WNDPROC* lplpfn = GetSuperWndProcAddr();
    WNDPROC oldWndProc = (WNDPROC)::SetWindowLong(hWnd, GWL_WNDPROC,
        (DWORD)AfxGetAfxWndProc());
    ASSERT(oldWndProc != (WNDPROC)AfxGetAfxWndProc());  

    if (*lplpfn == NULL)
        *lplpfn = oldWndProc;   // the first control of that type created
#ifdef _DEBUG
    else if (*lplpfn != oldWndProc)
        {  

            ::SetWindowLong(hWnd, GWL_WNDPROC, (DWORD)oldWndProc);
    }
#endif  

    return TRUE;
}

很显然,SubclassWindow()中调用了Attach()函数和PreSubclassWindow()函数,由于SubclassWindow()函数是不可重载的,而PreSubclassWindow()函数是可重载的,所以我们经常重载PreSubclassWindow()函数,以致于 窗口被子类化之前进行其它的必要的子类化,看下它原来的声明:

CWnd::PreSubclassWindow

virtual void PreSubclassWindow( );

而SubclassWindow又与SubclassDlgItem有什么区别?前者用于一切具有HWND的窗体,后者只限定于对话框控件

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

时间: 2024-08-29 09:08:57

DDX_Control、SubclassWindow和SubclassDlgItem的相关文章

SubclassWindow和SubclassDlgItem

通常如果在对话框中将一个控件映射到一个变量,有三种方法: DDX的方法 GetDlgItem的方法,例如CEdit pEdt = (CEdit *)GetDlgItem(IDC_EDIT1); SubclassWindow的方法(或者其扩展SubclassDlgItem),例如CEdit m_edit:m_edit.SubclassDlgItem(IDC_EDIT1); SubclassWindow CWnd::SubclassWindow(HWND hWnd)中调用两个主要操作:Attach(

日志文件目录

一.Visio分类 以编程方式使用 Microsoft Office Visio 2003 ActiveX 控件 visio应用程序相关设置-选项-视图 visio应用程序相关设置-选项-常规 visio中相关设置-菜单视图 Visio中设置自定义属性的值 Visio中的Undo和Redo Visio中ShapeAdded和SelectionAdded 二.位分类 补码.原码.反码 不同进制之间的转换 位操作基础 常用位操作小技巧 位操作的趣味应用 位操作与空间压缩 位操作基础篇之位操作全面总结

CWnd中PreCreateWindow、PreSubclassWindow、SubclassWindow的区别

http://blog.csdn.net/swimmer2000/archive/2007/10/30/1856213.aspx MFC(VC6.0)的CWnd及其子类中,有如下三个函数:     // From VS Install PathVC98MFCIncludeAFXWIN.H    class CWnd : public CCmdTarget    {        ...    public:        ...        virtual BOOL PreCreateWind

error LNK2005: DDX_Control 已经在 uafxcwd.lib(wincore2.obj) 中定义

编译错误提示: 1>afxnmcdd.lib(wincore2.obj) : error LNK2005: "void __stdcall DDX_Control(classCDataExchange *,int,class CWnd &)" ([email protected]@[email protected]@[email protected]@@Z)already defined in nafxcwd.lib(wincore2.obj) 1>afxnmcdd

关于SubclassWindow

#define SubclassWindow(hwnd, lpfn) \ ((WNDPROC)SetWindowLongPtr((hwnd), GWLP_WNDPROC, (LPARAM)(WNDPROC)(lpfn))) 从该宏可以看出是调用SetWindowLongPtr 具体的详解:http://blog.csdn.net/sunliangyuan/article/details/6028425 由于照着上面的链接敲的时候遇到了两个问题: 1.OnChar无法响应 2.当按回车键的时候整个

MFC DDX_Control 与 DDX_Text

DDX_TEXT()的作用可以理解为把字符串变量和控件的文本(WindowText)关联起来, DDX_Control()的作用可以理解为把变量和控件本身关联起来, DoDataExchange(pDX)就是处理所有变量与其关联控件交换数据的函数. void CView::DoDataExchange(CDataExchange* pDX){CFormView::DoDataExchange(pDX);DDX_Text(pDX, IDC_ID_VALUE, m_strID);DDX_Contro

WTL自定义控件:SubclassWindow的实现

自定义了一个edit类如下: 1 class CCheckEditEx : public CWindowImpl< CCheckEditEx, CEdit > 其SubclassWindow函数实现如下: 1 BOOL CCheckEditEx::SubclassWindow(HWND hwnd) 2 { 3 ATLASSERT(NULL == ::GetWindow(hwnd, GW_CHILD)); 4 if (CWindowImpl< CCheckEditEx, CEdit >

C++控件关联对象

1.C++控件关联对象 方法1:用向导关联变量 void DrawPoint::DoDataExchange(CDataExchange* pDX) { CDialogEx::DoDataExchange(pDX); DDX_Control(pDX, IDC_EDIT1, m_edit); } 方法2:GetDlgItem(IDC_EDIT1)获取句柄赋值 CEdit× m_edit; m_edit = (CEdit*)GetDlgItem(IDC_EDIT1); // GetDlgItem只能

眼见为实(2):介绍Windows的窗口、消息、子类化和超类化

眼见为实(2):介绍Windows的窗口.消息.子类化和超类化 这篇文章本来只是想介绍一下子类化和超类化这两个比较"生僻"的名词.为了叙述的完整性而讨论了Windows的窗口和消息,也简要讨论了进程和线程.子类化(Subclassing)和超类化(Superclassing)是伴随Windows窗口机制而产生的两个复用代码的方法.不要把"子类化.超类化"与面向对象语言中的派生类.基类混淆起来."子类化.超类化"中的"类"是指W