MFC对话框 1分类 模式和非模式 2相关类 CDialog --父类 CWnd CCommonDialog 通用对话框--父类CDialog CPropertypage 属性页对话框 --父类CDialog CpropertySheet类,与CPropertypage一起完成属性页的创建. 3创建基于对话框的应用程序。 1.模式对话框 添加对话框资源,定义相关联的对话框类。 调用CDialog::DoModal函数创建和显示对话框。 通过调用CDialog::OnOk和CDialog::OnCancel关闭对话框。 virtual BOOL OnInitDialog( ); //初始化 2非模式对话框 添加资源,定义相关联的对话框类 创建和显示与一般框架窗口类似。 处理对话框的关闭。 点击关闭按钮与cancel处理函数相同 virtual void OnOK( ); virtual void OnCancel( ); 先调用父类的函数再销毁。 DestroyWindow virtual void PostNcDestroy( ); //最后执行的函数 CWnd::PostNcDestroy 先删除子类再删除父类。 DoModal函数调用 查找对话框资源。 hInst = AfxFindResourceHandle(m_lpszTemplateName, RT_DIALOG); HRSRC hResource = ::FindResource(hInst, m_lpszTemplateName, RT_DIALOG); hDialogTemplate = LoadResource(hInst, hResource); 将父窗口设置为不可用状态。 HWND hWndParent = PreModal(); AfxUnhookWindowCreate(); BOOL bEnableParent = FALSE; if (hWndParent != NULL && ::IsWindowEnabled(hWndParent)) { ::EnableWindow(hWndParent, FALSE); bEnableParent = TRUE; } 创建非模式对话框,进入对话框消息循环。(父窗口不可用) AfxHookWindowCreate(this); if (CreateDlgIndirect(lpDialogTemplate, CWnd::FromHandle(hWndParent), hInst)) { if (m_nFlags & WF_CONTINUEMODAL) { // enter modal loop DWORD dwFlags = MLF_SHOWONIDLE; 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); } 消息循环 int CWnd::RunModalLoop(DWORD dwFlags) { ASSERT(::IsWindow(m_hWnd)); // window must be created ASSERT(!(m_nFlags & WF_MODALLOOP)); // window must not already be in modal state // for tracking the idle time state BOOL bIdle = TRUE; LONG lIdleCount = 0; BOOL bShowIdle = (dwFlags & MLF_SHOWONIDLE) && !(GetStyle() & WS_VISIBLE); HWND hWndParent = ::GetParent(m_hWnd); m_nFlags |= (WF_MODALLOOP|WF_CONTINUEMODAL); MSG* pMsg = &AfxGetThread()->m_msgCur; // acquire and dispatch messages until the modal state is done for (;;) { ASSERT(ContinueModal()); // phase1: check to see if we can do idle work while (bIdle && !::PeekMessage(pMsg, NULL, NULL, NULL, PM_NOREMOVE)) { ASSERT(ContinueModal()); // show the dialog when the message queue goes idle if (bShowIdle) { ShowWindow(SW_SHOWNORMAL); UpdateWindow(); bShowIdle = FALSE; } // call OnIdle while in bIdle state if (!(dwFlags & MLF_NOIDLEMSG) && hWndParent != NULL && lIdleCount == 0) { // send WM_ENTERIDLE to the parent ::SendMessage(hWndParent, WM_ENTERIDLE, MSGF_DIALOGBOX, (LPARAM)m_hWnd); } if ((dwFlags & MLF_NOKICKIDLE) || !SendMessage(WM_KICKIDLE, MSGF_DIALOGBOX, lIdleCount++)) { // stop idle processing next time bIdle = FALSE; } } // phase2: pump messages while available do { ASSERT(ContinueModal()); // pump message, but quit on WM_QUIT if (!AfxGetThread()->PumpMessage()) { AfxPostQuitMessage(0); return -1; } // show the window when certain special messages rec'd if (bShowIdle && (pMsg->message == 0x118 || pMsg->message == WM_SYSKEYDOWN)) { ShowWindow(SW_SHOWNORMAL); UpdateWindow(); bShowIdle = FALSE; } if (!ContinueModal()) goto ExitModal; // reset "no idle" state after pumping "normal" message if (AfxGetThread()->IsIdleMessage(pMsg)) { bIdle = TRUE; lIdleCount = 0; } } while (::PeekMessage(pMsg, NULL, NULL, NULL, PM_NOREMOVE)); } ExitModal: m_nFlags &= ~(WF_MODALLOOP|WF_CONTINUEMODAL); return m_nModalResult; } 点击ok和cancel按钮时先隐藏对话框窗口,然后将父窗口设置为可用和激活状态 BOOL CWnd::SetWindowPos(const CWnd* pWndInsertAfter, int x, int y, int cx, int cy, UINT nFlags) { ASSERT(::IsWindow(m_hWnd)); if (m_pCtrlSite == NULL) return ::SetWindowPos(m_hWnd, pWndInsertAfter->GetSafeHwnd(), x, y, cx, cy, nFlags); else return m_pCtrlSite->SetWindowPos(pWndInsertAfter, x, y, cx, cy, nFlags); } 调用DestroyWindow函数销毁对话框窗口。 //DoModal函数中调用。 返回DoModal函数的执行结果。IDOK:1 IDCANCEL:0 释放资源 CMainFrame::OnDlgModal() line 116 _AfxDispatchCmdMsg(CCmdTarget * 0x007f4008 {CMainFrame hWnd=0x00040696}, unsigned int 32771, int 0, void (void)* 0x0040117c CMainFrame::OnDlgModal(void), void * 0x00000000, unsigned int 12, AFX_CMDHANDLERINFO * 0x00000000) line 88 CCmdTarget::OnCmdMsg(unsigned int 32771, int 0, void * 0x00000000, AFX_CMDHANDLERINFO * 0x00000000) line 302 + 39 bytes CFrameWnd::OnCmdMsg(unsigned int 32771, int 0, void * 0x00000000, AFX_CMDHANDLERINFO * 0x00000000) line 898 + 24 bytes CWnd::OnCommand(unsigned int 32771, long 0) line 2099 CFrameWnd::OnCommand(unsigned int 32771, long 0) line 321 CWnd::OnWndMsg(unsigned int 273, unsigned int 32771, long 0, long * 0x0018fc7c) line 1608 + 28 bytes CWnd::WindowProc(unsigned int 273, unsigned int 32771, long 0) line 1596 + 30 bytes AfxCallWndProc(CWnd * 0x007f4008 {CMainFrame hWnd=0x00040696}, HWND__ * 0x00040696, unsigned int 273, unsigned int 32771, long 0) line 215 + 26 bytes AfxWndProc(HWND__ * 0x00040696, unsigned int 273, unsigned int 32771, long 0) line 379 AfxWndProcBase(HWND__ * 0x00040696, unsigned int 273, unsigned int 32771, long 0) line 220 + 21 bytes USER32! 765762fa() USER32! 76576d3a() USER32! 765777c4() USER32! 76577bca() CWinThread::PumpMessage() line 853 CWinThread::Run() line 487 + 11 bytes CWinApp::Run() line 400 AfxWinMain(HINSTANCE__ * 0x00400000, HINSTANCE__ * 0x00000000, char * 0x005458ba, int 1) line 49 + 11 bytes WinMain(HINSTANCE__ * 0x00400000, HINSTANCE__ * 0x00000000, char * 0x005458ba, int 1) line 30 WinMainCRTStartup() line 330 + 54 bytes KERNEL32! 763633ca() NTDLL! 77149ed2() NTDLL! 77149ea5() int CDialog::DoModal() { // can be constructed with a resource template or InitModalIndirect ASSERT(m_lpszTemplateName != NULL || m_hDialogTemplate != NULL || m_lpDialogTemplate != NULL); // load resource as necessary LPCDLGTEMPLATE lpDialogTemplate = m_lpDialogTemplate; HGLOBAL hDialogTemplate = m_hDialogTemplate; HINSTANCE hInst = AfxGetResourceHandle(); 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; // disable parent (before creating dialog) HWND hWndParent = PreModal(); AfxUnhookWindowCreate(); BOOL bEnableParent = FALSE; if (hWndParent != NULL && ::IsWindowEnabled(hWndParent)) { ::EnableWindow(hWndParent, FALSE); bEnableParent = TRUE; } TRY { // create modeless dialog AfxHookWindowCreate(this); if (CreateDlgIndirect(lpDialogTemplate, CWnd::FromHandle(hWndParent), hInst)) { if (m_nFlags & WF_CONTINUEMODAL) { // enter modal loop DWORD dwFlags = MLF_SHOWONIDLE; 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 if (bEnableParent) ::EnableWindow(hWndParent, TRUE); if (hWndParent != NULL && ::GetActiveWindow() == m_hWnd) ::SetActiveWindow(hWndParent); // destroy modal window DestroyWindow(); PostModal(); // unlock/free resources as necessary if (m_lpszTemplateName != NULL || m_hDialogTemplate != NULL) UnlockResource(hDialogTemplate); if (m_lpszTemplateName != NULL) FreeResource(hDialogTemplate); return m_nModalResult; } ********************************************************** 对话框数据交换技术 DDX 将控件与类的成员变量绑定,通过操作成员变量来达到操作控件的目的。 相关的几个函数。 DDX_Control 将控件与控件类型的变量绑定 DDX_Text 将控件与值类型的变量绑定。 2包含一些绑定函数的函数 DoDataExchange 数据交换函数。 3 UpdateData 数据更新函数。 通常需要程序员调用。 UpdateData(TRUE) 将用户在控件上输入或者选择的值传递给它绑定的变量 UpdateData(FALSE) 变量的值传递给预支绑定的控件。 DDX 使用 virtual void DoDataExchange( CDataExchange* pDX ); void AFXAPI DDX_Control(CDataExchange* pDX, int nIDC, CWnd& rControl) { if (rControl.m_hWnd == NULL) // not subclassed yet { ASSERT(!pDX->m_bSaveAndValidate); HWND hWndCtrl = pDX->PrepareCtrl(nIDC); if (!rControl.SubclassWindow(hWndCtrl)) { ASSERT(FALSE); // possibly trying to subclass twice? AfxThrowNotSupportedException(); } #ifndef _AFX_NO_OCC_SUPPORT 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(CDataExchange * 0x0018f590, int 1, CWnd & {CWnd hWnd=0x00000000}) line 621 CMyDlg::DoDataExchange(CDataExchange * 0x0018f590) line 18 CWnd::UpdateData(int 0) line 3120 CDialog::OnInitDialog() line 680 + 10 bytes CMyDlg::OnInitDialog() line 27 + 8 bytes AfxDlgProc(HWND__ * 0x005d0746, unsigned int 272, unsigned int 591518, unsigned int 591518) line 35 + 14 bytes USER32! 765762fa() USER32! 7659f9df() USER32! 7659f784() USER32! 7658afac() USER32! 765762fa() USER32! 76576d3a() USER32! 76580d27() USER32! 7658794a() CWnd::DefWindowProcA(unsigned int 272, unsigned int 591518, long 0) line 1011 + 32 bytes CWnd::Default() line 258 CDialog::HandleInitDialog(unsigned int 591518, unsigned int 591518) line 624 + 8 bytes CWnd::OnWndMsg(unsigned int 272, unsigned int 591518, long 0, long * 0x0018f9c4) line 1826 + 17 bytes CWnd::WindowProc(unsigned int 272, unsigned int 591518, long 0) line 1596 + 30 bytes AfxCallWndProc(CWnd * 0x0018fdfc {CMyDlg hWnd=0x005d0746}, HWND__ * 0x005d0746, unsigned int 272, unsigned int 591518, long 0) line 215 + 26 bytes AfxWndProc(HWND__ * 0x005d0746, unsigned int 272, unsigned int 591518, long 0) line 379 AfxWndProcBase(HWND__ * 0x005d0746, unsigned int 272, unsigned int 591518, long 0) line 220 + 21 bytes USER32! 765762fa() USER32! 76576d3a() USER32! 7657965e() USER32! 765a206f() USER32! 765a10d3() USER32! 7658b044() CWnd::CreateDlgIndirect(const DLGTEMPLATE * 0x00417170, CWnd * 0x00000000 {CWnd hWnd=???}, HINSTANCE__ * 0x00400000) line 327 + 36 bytes CDialog::DoModal() line 531 + 32 bytes CDlgDDXApp::InitInstance() line 47 AfxWinMain(HINSTANCE__ * 0x00400000, HINSTANCE__ * 0x00000000, char * 0x005d589c, int 1) line 39 + 11 bytes WinMain(HINSTANCE__ * 0x00400000, HINSTANCE__ * 0x00000000, char * 0x005d589c, int 1) line 30 WinMainCRTStartup() line 330 + 54 bytes KERNEL32! 763633ca() NTDLL! 77149ed2() NTDLL! 77149ea5() DDX实现原理 DDX_Control,控件 类型的绑定 调用PrepareCtrl,通过调用资源ID得到控件的句柄 调用SubclassWindow,在函数中,调用Attach函数, 将控件类型的变量与控件句柄绑定。 DDX_Text,值类型的绑定 调用PrepareEditCtrl, 通过资源ID获取控件的句柄。 根据UpdateData()函数的参数的值,调用AfxGetWindowText/AfxSetWindowText 获取控件的值赋值给变量/吧变量的值传递给控件。 CMyDlg::OnInitDialog ->CDialog::OnInitDialog ->UpdateData ->DoDataExchange ->DDX_Control/DDX_Text BOOL CDialog::OnInitDialog() { // execute dialog RT_DLGINIT resource BOOL bDlgInit; if (m_lpDialogInit != NULL) bDlgInit = ExecuteDlgInit(m_lpDialogInit); else bDlgInit = ExecuteDlgInit(m_lpszTemplateName); if (!bDlgInit) { TRACE0("Warning: ExecuteDlgInit failed during dialog init.\n"); EndDialog(-1); return FALSE; } // transfer data into the dialog from member variables if (!UpdateData(FALSE)) { TRACE0("Warning: UpdateData failed during dialog init.\n"); EndDialog(-1); return FALSE; } // enable/disable help button automatically CWnd* pHelpButton = GetDlgItem(ID_HELP); if (pHelpButton != NULL) pHelpButton->ShowWindow(AfxHelpEnabled() ? SW_SHOW : SW_HIDE); return TRUE; // set focus to first one } BOOL CWnd::UpdateData(BOOL bSaveAndValidate) { ASSERT(::IsWindow(m_hWnd)); // calling UpdateData before DoModal? CDataExchange dx(this, bSaveAndValidate); // prevent control notifications from being dispatched during UpdateData _AFX_THREAD_STATE* pThreadState = AfxGetThreadState(); HWND hWndOldLockout = pThreadState->m_hLockoutNotifyWindow; ASSERT(hWndOldLockout != m_hWnd); // must not recurse pThreadState->m_hLockoutNotifyWindow = m_hWnd; BOOL bOK = FALSE; // assume failure TRY { DoDataExchange(&dx); ------------------------------->调用子类 bOK = TRUE; // it worked } CATCH(CUserException, e) { // validation failed - user already alerted, fall through ASSERT(!bOK); // Note: DELETE_EXCEPTION_(e) not required } AND_CATCH_ALL(e) { // validation failed due to OOM or other resource failure e->ReportError(MB_ICONEXCLAMATION, AFX_IDP_INTERNAL_FAILURE); ASSERT(!bOK); DELETE_EXCEPTION(e); } END_CATCH_ALL pThreadState->m_hLockoutNotifyWindow = hWndOldLockout; return bOK; } HWND CDataExchange::PrepareCtrl(int nIDC) { ASSERT(nIDC != 0); ASSERT(nIDC != -1); // not allowed HWND hWndCtrl; m_pDlgWnd->GetDlgItem(nIDC, &hWndCtrl); if (hWndCtrl == NULL) { TRACE1("Error: no data exchange control with ID 0x%04X.\n", nIDC); ASSERT(FALSE); AfxThrowNotSupportedException(); } m_hWndLastControl = hWndCtrl; m_bEditLastControl = FALSE; // not an edit item by default ASSERT(hWndCtrl != NULL); // never return NULL handle return hWndCtrl; } void CWnd::GetDlgItem(int nID, HWND* phWnd) const { ASSERT(::IsWindow(m_hWnd)); ASSERT(phWnd != NULL); if (m_pCtrlCont == NULL) *phWnd = ::GetDlgItem(m_hWnd, nID); else m_pCtrlCont->GetDlgItem(nID, phWnd); } 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) { TRACE0("Error: Trying to use SubclassWindow with incorrect CWnd\n"); TRACE0("\tderived class.\n"); TRACE3("\thWnd = $%04X (nIDC=$%04X) is not a %hs.\n", (UINT)hWnd, _AfxGetDlgCtrlID(hWnd), GetRuntimeClass()->m_lpszClassName); ASSERT(FALSE); // undo the subclassing if continuing after assert ::SetWindowLong(hWnd, GWL_WNDPROC, (DWORD)oldWndProc); } #endif return TRUE; } ************************ void AFXAPI DDX_Text(CDataExchange* pDX, int nIDC, CString& value) { HWND hWndCtrl = pDX->PrepareEditCtrl(nIDC); if (pDX->m_bSaveAndValidate) { int nLen = ::GetWindowTextLength(hWndCtrl); ::GetWindowText(hWndCtrl, value.GetBufferSetLength(nLen), nLen+1); value.ReleaseBuffer(); } else { AfxSetWindowText(hWndCtrl, value); } } HWND CDataExchange::PrepareEditCtrl(int nIDC) { HWND hWndCtrl = PrepareCtrl(nIDC); ASSERT(hWndCtrl != NULL); m_bEditLastControl = TRUE; return hWndCtrl; } _AFX_INLINE CString::operator LPCTSTR() const { return m_pchData; } void AFXAPI AfxSetWindowText(HWND hWndCtrl, LPCTSTR lpszNew) { int nNewLen = lstrlen(lpszNew); TCHAR szOld[256]; // fast check to see if text really changes (reduces flash in controls) if (nNewLen > _countof(szOld) || ::GetWindowText(hWndCtrl, szOld, _countof(szOld)) != nNewLen || lstrcmp(szOld, lpszNew) != 0) { // change it ::SetWindowText(hWndCtrl, lpszNew); } }
原文地址:http://blog.csdn.net/u011185633/article/details/44907393