模态/非模态对话框的生&死 VC++中subclassdlgitem函数的功能及用法

以下为调试过程中的代码片段:
----------------------------------------------------------->>>>
// 模态
CDialog::DoModal()->
{
::FindResource
CWnd::CreateDlgIndirect->::CreateDialogIndirect(看不到源码了,win32 API内部会创建窗口句柄,且触发以下代码)
{{{{{
  virtual CWnd::PreSubclassWindow(窗口子类化)
  [afx_msg int OnCreate(消息响应函数)]
  virtual CDialog::OnInitDialog(->CWnd::UpdateData(FALSE)->CWnd::DoDataExchange->)
  {//一下是控件子类化的过程
   //if 模板创建,比如:DDX_Control(pDX, IDC_BUTTON1, m_btn)存在
   DDX_Control
   {
    CWnd::SubclassWindow->...
    virtual CWnd::PreSubclassWindow
   }

   //if 手动子类化(与if 模板创建相仿),比如:SubclassWindow/SubclassDlgItem;
   SubclassWindow/SubclassDlgItem
   {
    CWnd::Attach(hWnd) (绑定句柄)
    virtual CWnd::PreSubclassWindow
   }

   //if 动态创建,比如:m_btn.Create(..)
   CWnd::Create->
   CWnd::CreateEx->
   {
    virtual BOOL PreCreateWindow(CREATESTRUCT& cs)

    ::CreateWindowEx(看不到源码了,会触发以下代码)->
    virtual CWnd::PreSubclassWindow
    [afx_msg int OnCreate(消息响应函数)]
   }
  }
}}}}}

CWnd::RunModalLoop(CDialog::EndDialog->CWnd::EndModalLoop,退出消息循环)
CWnd::DestroyWindow
}

// 非模态
CDialog::Create(..)->
{
::FindResource
CDialog::Create->CWnd::CreateDlgIndirect->::CreateDialogIndirect(看不到源码了,win32 API内部会创建窗口句柄,且触发以下代码)
{
  ...(与模态红色部分相同,不写了)
}

[CWnd::DestroyWindow(需我们自己在适当时候调用)]
}
关于对话框的"死",详细请看另一篇:http://hi.baidu.com/pop1210/blog/item/1b8ef8ad2205c2104a36d61b.html

从上面看出,模态/非模态对话框的生&死"基本没什么差别"(当然咯!)。
1. 对于子控件言,其创建方法有2种:模板方式或动态创建方式。二者差别在与,
   动态创建方式(Create): MFC来创建控件,MFC可以在控件的创建过程中完成子类化操作,从而实现响应WM_CREATE等消息;
   模板方式: 对话框资源上放置的控件是由对话框创建的,MFC对控件的子类化操作是在响应WM_INITDIALOG消息时进行的;
2. 无论模板创建方式或动态创建方式,虚函数PreSubclassWindow都将被调用;
   虚函数PreSubclassWindow执行前窗口句柄(HWND)已经创建完成;
3. 根据创建方式的不同,虚函数PreCreateWindow可能不会调用到,(目前看,只有掉CWnd::Create/CreateEX时才会调用到);
   虚函数PreCreateWindow执行时窗口句柄(HWND)还未开始创建;
4. CWnd::OnCreate消息响应函数同样可能不会调用到(一是看你映射ON_WM_CREATE()了没,二是看你的创建方式(对子控件言));

--简单一句话总结大概是:
只有是模板(资源)创建方式,PreCreateWindow或OnCreate很可能不会被调用到,动态创建方式(Create)则相反;
无论何种创建方式,PreSubclassWindow总能被调用到;

其他人的总结看了下,基本无异:
http://www.cnblogs.com/zqrferrari/archive/2010/07/06/1772437.html
http://xiaop.me/archives/159
http://www.vckbase.com/bbs/prime/viewprime.asp?id=377
http://www.cnblogs.com/mqt-2003/articles/540091.html#_Toc146167830
http://topic.csdn.net/u/20100129/17/7e211e1c-0f79-4d5c-bbb2-8976faaee9bf.html

以下是部分源码截取:
----------------------------------------------------------->>>>
BOOL CWnd::CreateDlgIndirect(LPCDLGTEMPLATE lpDialogTemplate,
CWnd* pParentWnd, HINSTANCE hInst)
{
...
[Note: AfxDlgProc调用前会先调用virtual CWnd::PreSubclassWindow]
hWnd = ::CreateDialogIndirect(hInst, lpDialogTemplate, pParentWnd->GetSafeHwnd(), AfxDlgProc); //创建窗口句柄,发送WS_Create消息
...
}

BOOL CALLBACK AfxDlgProc(HWND hWnd, UINT message, WPARAM, LPARAM)
{
if (message == WM_INITDIALOG)
{
  // special case for WM_INITDIALOG
  CDialog* pDlg = DYNAMIC_DOWNCAST(CDialog, CWnd::FromHandlePermanent(hWnd));
  if (pDlg != NULL)
   return pDlg->OnInitDialog();
  else
   return 1;
}
return 0;
}

BOOL CWnd::PreCreateWindow(CREATESTRUCT& cs)
{//窗口句柄创建前被调用
...
}

CWnd::Create()
{
...
CWnd::CreateEx
...
}

CWnd::CreateEx
{
...
PreCreateWindow(cs);
HWND hWnd = ::CreateWindowEx(cs.dwExStyle, ..);
...
}

void CModalDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CModalDlg)
DDX_Control(pDX, IDC_BUTTON1, m_btn);
//}}AFX_DATA_MAP
}

void AFXAPI DDX_Control(CDataExchange* pDX, int nIDC, CWnd& rControl)
{
...
HWND hWndCtrl = pDX->PrepareCtrl(nIDC);
rControl.SubclassWindow(hWndCtrl)
...
}

BOOL CWnd::SubclassWindow(HWND hWnd)
{
if (!Attach(hWnd))
  return FALSE;

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

void CWnd::PreSubclassWindow()
{
// no default processing
}

BOOL CWnd::SubclassDlgItem(UINT nID, CWnd* pParent)
{ //通常在CXXDialog::OnInitDialog中调用
...
// check for normal dialog control first
HWND hWndControl = ::GetDlgItem(pParent->m_hWnd, nID);
if (hWndControl != NULL)
  return SubclassWindow(hWndControl);
...
}

VC++中subclassdlgitem函数的功能及用法

subclassdlgitem
  该函数用来子类化一个控件.
  Subclass(子类化)是MFC中最常用的窗体技术之一。子类化完成两个工作:一是把窗体类对象attach到一个windows窗体实体中(即把一个窗体的hwnd赋给该类)。另外就是把该类对象的消息加入到消息路由中,使得该类可以捕获消息。
  SubclassDlgItem可以把对话框中已有的控件与某个窗口对象动态连接起来,该窗口对象将接管控件的消息处理,从而使控件具有新的特性.SubclassDlgItem函数的声明为
  BOOL SubclassDlgItem( UINT nID, CWnd* pParent );
  参数nID是控件的ID,pParent是指向父窗口的指针.若连接成功则函数返回TRUE,否则返回FALSE.
  综上所述,要在程序中使用派生控件,应该按下面两步进行:
  在对话框模板中放置好基类控件.
  在对话框类中嵌入派生控件类的对象.
  在OnInitDialog中调用SubclassDlgItem将派生类的控件对象与对话框中的基类控件相连接,则这个基类控件变成了派生控件

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

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

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

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

  在对话框模板中放置好基类控件.

  在对话框类中嵌入派生控件类的对象.

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

SubClass Dialog 上面的一个控件呀!
SubClass 某个控件后,其原来的 WindowProc 替换成你自己写的 CYourCWndClass 的 WindowProc,这样你就可以处理这个控件的所有消息了;用 ClassWizard 维护自己的 CYourCWndClass 就可以了。

在MFC中除了SubClassDlgItem外还有SubclassWindow函数用来执行同样的功能。在与某个控件ID关联起来之后,所有该控件的消息将会有该类来响应。通常使用过程如下
一、从希望Subclass的控件派生一个类例如从CEdit->CMyEdit;
二、在CMyEdit中完成希望处理的消息,例如OnChar等;
三、在对话框类中定义一个CMyEdit类的成员变量m_myEdit;
四、在Dialog的OnInitDialog中加入m_myEdit(IDC_EDIT, this);
这样当在IDC_EDIT上发生OnChar时,将会调用CMyEdit中的OnChar

补充一下,用一个窗口过程替换另一个过程的行为叫SUBCLASSING,这是WINDOWS意义上的派生子类,与面向对象语言的派生子类是完全不同的概念。SubClassDlgItem让对话框内指定子控制自己的窗口过程替代默认窗口过程,以拦截消息。

在VC 中,DoDataExchange函数中的DDX_Control之类的函数会调用SubClassDlgItem完成窗口子类化,这样控件自己就能处理消息了。

BOOL SubclassDlgItem( UINT nID, CWnd* pParent );
参数nID是控件的ID,pParent是指向父窗口的指针.若连接成功则函数返回TRUE,否则返回FALSE.

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

1、在对话框模板中放置好基类控件.
    2、在对话框类中嵌入派生控件类的对象.
    3、在OnInitDialog中调用SubclassDlgItem将派生类的控件对象与对话框中的基类控件相连接,则这个基类控件变成了派生控件.

例如,如果要在对话框中使用新设计的编辑框控件,应先在对话框模板中的合适位置放置一个普通的编辑框,然后,在OnInitDialog函数中按下面的方式调用SubclassDlgItem即可:

BOOL CMyDialog::OnInitDialog()
{
CDialog::OnInitDialog();
m_MyEdit.SubclassDlgItem(IDC_MYEDIT, this);
return TRUE;
}

转自http://hi.baidu.com/pop1210/blog/item/907b688b3748a8659f2fb450.html

http://www.cnblogs.com/wuqi924/archive/2011/08/03/2126245.html

http://hi.baidu.com/%B7%B6%D5%F1%BB%AA/blog/item/7ba5d67bb02443f20ad18700.html

你可能感兴趣的:(模态/非模态对话框的生&死 VC++中subclassdlgitem函数的功能及用法)