/*********************************************************************************************************************
* 发布日期:
* 更新日期:2017-11-16 09:22:05
* 进度:待完善
* 作者:骆天
* 备注:
*********************************************************************************************************************/
对话框的创建
一、MFC创建模态对话框
CMyDialog dlg;
dlg.DoModal();
考查模式对话框的创建过程。CDialog::DoModal用来创建模式对话框窗口并执行有关任务,和DoModal相关的是MFC内部使用的成员函数CDialog::PreModal和CDialog::PostModal。下面分别讨论它们的实现。
HWND CDialog::PreModal() { // cannot call DoModal on a dialog already constructed as modeless ASSERT(m_hWnd == NULL); // allow OLE servers to disable themselves AfxGetApp()->EnableModeless(FALSE); // 得到父窗口 CWnd* pWnd = CWnd::GetSafeOwner(m_pParentWnd, &m_hWndTop); // 如同CWnd处理其他窗口的创建,设置一个窗口创建HOOK AfxHookWindowCreate(this); //返回父窗口的句柄 return pWnd->GetSafeHwnd(); }
void CDialog::PostModal() { //取消窗口创建前链接的HOOK AfxUnhookWindowCreate(); // just in case //MFC对话框对象和对应的Windows对话框窗口分离 Detach(); // just in case // m_hWndTop是当前对话框的父窗口或所属窗口,则恢复它 if (::IsWindow(m_hWndTop)) ::EnableWindow(m_hWndTop, TRUE); m_hWndTop = NULL; AfxGetApp()->EnableModeless(TRUE); }
int CDialog::DoModal() { // can be constructed with a resource template or InitModalIndirect ASSERT(m_lpszTemplateName != NULL || m_hDialogTemplate != NULL || m_lpDialogTemplate != NULL); //加载对话框资源 LPCDLGTEMPLATE lpDialogTemplate = m_lpDialogTemplate; HGLOBAL hDialogTemplate = m_hDialogTemplate; HINSTANCE hInst = AfxGetResourceHandle(); //查找资源(见9.5.2节),找到了就加载它 if (m_lpszTemplateName != NULL) { hInst = AfxFindResourceHandle(m_lpszTemplateName, RT_DIALOG); HRSRC hResource = ::FindResource(hInst, m_lpszTemplateName, RT_DIALOG); hDialogTemplate = LoadResource(hInst, hResource); } //锁定加载的资源 if (hDialogTemplate != NULL) lpDialogTemplate = (LPCDLGTEMPLATE)LockResource(hDialogTemplate); // return -1 in case of failure to load the dialog template resource if (lpDialogTemplate == NULL) return -1; //创建对话框前禁止父窗口,为此要调用PreModal得到父窗口句柄 HWND hWndParent = PreModal(); AfxUnhookWindowCreate(); CWnd* pParentWnd = CWnd::FromHandle(hWndParent); BOOL bEnableParent = FALSE; if (hWndParent != NULL && ::IsWindowEnabled(hWndParent)) { ::EnableWindow(hWndParent, FALSE); bEnableParent = TRUE; } //创建对话框,注意是无模式对话框 TRY { //链接一个HOOK到HOOK链以处理窗口创建, //如同4.4.1节描述的CWnd类窗口创建一样 AfxHookWindowCreate(this); //CreateDlgIndirect间接调用::CreateDlgIndirect, //最终调用了::CreateWindowEX来创建对话框窗口。 //HOOK过程_AfxCbtFilterHook用子类化的方法 //取代原来的窗口过程为AfxWndProc。 if (CreateDlgIndirect(lpDialogTemplate, CWnd::FromHandle(hWndParent), hInst)) { if (m_nFlags & WF_CONTINUEMODAL) { // enter modal loop DWORD dwFlags = MLF_SHOWONIDLE; //RunModalLoop接管整个应用程序的消息处理 if (GetStyle() & DS_NOIDLEMSG) dwFlags |= MLF_NOIDLEMSG; VERIFY(RunModalLoop(dwFlags) == m_nModalResult); } // hide the window before enabling the parent, etc. if (m_hWnd != NULL) SetWindowPos(NULL, 0, 0, 0, 0, SWP_HIDEWINDOW | SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOZORDER); } } CATCH_ALL(e) { DELETE_EXCEPTION(e); m_nModalResult = -1; } END_CATCH_ALL //Enable并且激活父窗口 if (bEnableParent) ::EnableWindow(hWndParent, TRUE); if (hWndParent != NULL && ::GetActiveWindow() == m_hWnd) ::SetActiveWindow(hWndParent); //::EndDialog仅仅关闭了窗口,现在销毁窗口 DestroyWindow(); PostModal(); // 必要的话,解锁/释放资源 if (m_lpszTemplateName != NULL || m_hDialogTemplate != NULL) UnlockResource(hDialogTemplate); if (m_lpszTemplateName != NULL) FreeResource(hDialogTemplate); return m_nModalResult; }
从DoModal的实现可以看出:
它首先Disable对话框窗口的父窗口;然后使用::CreateIndrectDialog创建对话框窗口,使用子类化的方法用AfxWndProc(或者AfxBaseProc)替换了原来的窗口过程,并把原来的窗口过程保存在CWnd的成员变量m_pfnSuper中。原来的窗口过程就是::DialogBox等创建对话框窗口时指定的,是Windows内部提供的对话框“窗口类”的窗口过程。取代(Subclass)原来“窗口类”的窗口过程的方法如同 4.4.1节描述的CWnd::Create。
在::CreateIndirectDialog创建对话框窗口后,会发送WM_INITDIALOG消息给对话框的对话框过程(必要的话,还有WM_SETFONT消息)。但是MFC取代了原来的对话框窗口过程,这两个消息如何送给对话框过程呢?处理方法如下节所描述。
二、MFC创建非模态对话框
CMyDialog dlg;//此dlg应在类中作为成员变量; dlg.Create(IDD_DIALOG1,this); dlg.ShowWindow(SW_SHOW);//显示窗口
对话框的控制:
分为几种情况,第一种,直接调用对话框,前提是已经在资源管理器中创建了一个对话框,比如,在菜单中的某个选项中调出对话框,可以直接用CWnd::DialogBox()函数。一旦显示对话框并且操作完成之后,当需要关闭的时候,调用函数EndDialog()函数。调用之后,只关闭这个对话框,而不是是关闭整个窗口。
BOOL EndDialog( HWND hDlg; //该对话框所属的窗口句柄,比如hwnd int nResult //返回值 );
第二种情况,应用程序就只有一个对话框,只要关闭对话框就意味着整个程序关闭。可以在某个按钮上(比如取消按钮)添加一条消息,PostQuitMessage(0);
CDialog成员函数一览表
BOOL Create( LPCTSTR lpszTemplateName, CWnd* pParentWnd = NULL ); |
Create用来根据模板创建无模式对话框 |
BOOL CreateIndirect( LPCDLGTEMPLATE lpDialogTemplate, CWnd* pParentWnd = NULL ); |
CreateInDirect用来根据内存中的模板创建无模式对话框 |
BOOL InitModalIndirect( LPCDLGTEMPLATE lpDialogTemplate, CWnd* pParentWnd = NULL ); |
InitModalIndirect用来根据内存中的模板创建模式对话框。它们都提供了两个重载版本 |
/*构造函数*/ CDialog( LPCTSTR lpszTemplateName, CWnd* pParentWnd = NULL ); |
CDialog重载了三个构造函数。 其中,第三个是缺省构造函数; 第一个和第二个构造函数从指定的对话框模板资源创建,pParentWnd指定了父窗口或所属窗口 若空则设置父窗口为应用程序主窗口。 |