2009-9-2
===========================
《深入解析MFC》筆記 5.對(duì)話框和控件類
===========================
CDialog: 模態(tài)MFC對(duì)話框 和 非模態(tài)MFC對(duì)話框
--------------------------------------------------------------------------
模態(tài)對(duì)話框
從CDialog派生一個(gè)本地對(duì)象,調(diào)用 DoModal() 初始化、創(chuàng)建、顯示并銷(xiāo)毀對(duì)話框
非模態(tài)對(duì)話框
從堆上利用new 操作符創(chuàng)建,顯示調(diào)用Create() 成員函數(shù)來(lái)顯示對(duì)話框, 銷(xiāo)毀 DestroyWindow()
對(duì)話框操作
NextDlgCtrl() 、PrevDlgCtrl() 或 GotoDlgCtrl() ,遍歷對(duì)話框中的控件
--------------------------------------------------------------------------
Win32 API
· CreateDialog() —— 從模板資源創(chuàng)建一個(gè)非模態(tài)的對(duì)話框。
· CreateDialogIndirect() —— 從模板指針創(chuàng)建一個(gè)非模態(tài)的對(duì)話框。
· DialogBox() —— 從模板資源創(chuàng)建一個(gè)模態(tài)的對(duì)話框。
· DialogBoxIndirect() —— 從模板指針創(chuàng)建一個(gè)模態(tài)的對(duì)話框。
CDialog只抵用CreateDialogIndirect(),自己實(shí)現(xiàn)了模態(tài)化。
--------------------------------------------------------------------------
CDialog的一些數(shù)據(jù)成員
· m_nIDHelp —— 按鈕的輔助ID,由模板ID確定
· m_lpszTemplateName —— 資源模板的名字。若指定了ID,m_lpszTemplateName就是MAKELONG(0,ID).
· m_hDialogTemplate —— 一旦裝載之后,是資源模板句柄。
· m_lpDialogInit —— 一個(gè)指向用來(lái)初始化對(duì)話框的數(shù)據(jù)的指針。用來(lái)初始化對(duì)話框的數(shù)據(jù)是從資源文件中裝載的,通過(guò)一個(gè)資源ID與對(duì)話框相聯(lián)系。
· m_pParentWnd —— 一個(gè)指向父窗口或主窗口的CWnd指針。
· m_hWndTop —— 最頂層的父窗口
· m_pOccDialogInfo —— 存儲(chǔ)OLE控件所需要的信息。
CDialog的一些成員函數(shù)
· virtual PreTranslateMessage() —— 為特殊情況過(guò)濾消息,如工具提示和SHIFT-F1環(huán)境幫助
· virtual OnCmdMsg() —— 將命令消息路由給所有者和當(dāng)前的 CWinThread 對(duì)象。忽略控件通知
· virtual CheckAutoCenter() —— 檢查用戶是否已經(jīng)指定了對(duì)話框自動(dòng)位于中心
· virtual SetOccDialogInfo —— 用于設(shè)置 m_pOccDialogInfo。
· virtual PreInitDialog() —— 在WM_INITDIALOG(OnInitDialog() )之前被調(diào)用。
· PreModal() —— 通過(guò)準(zhǔn)備將自己粘貼到已創(chuàng)建的對(duì)話框窗口上,為DoModal()邏輯準(zhǔn)備CDialog
· PostModal —— 在DoModal()邏輯之后將CDialog分離。
-------------
CDialog的構(gòu)造函數(shù)
DLGCORE.CPP
DoModal() 《深入解析MFC》 P155
處理對(duì)華框的創(chuàng)建、顯示和銷(xiāo)毀
· 首先載入對(duì)話框資源: 從資源文件中查找、載入并鎖定對(duì)話框模版。無(wú)法定位資源時(shí),返回-1.
· 準(zhǔn)備創(chuàng)建對(duì)話框: 先調(diào)用PreModal() 來(lái)執(zhí)行安全檢查并未對(duì)話框查找所需的父句柄,調(diào)用CWnd::GetSageOwner()來(lái)完成這些,并將結(jié)果存入m_hWnd中。
然后調(diào)用EnableWindow(hWndParent,FALSE)來(lái)講對(duì)話框的父窗口禁用,強(qiáng)制實(shí)現(xiàn)了模態(tài)化。
· 創(chuàng)建并顯示對(duì)話框:
先通過(guò)調(diào)用AfxHookWindowCreate() 將一個(gè)MFC對(duì)象粘貼到對(duì)話框中,
然后調(diào)用 CWnd::CreateDlgIndirect(),該函數(shù)會(huì)做一些錯(cuò)誤檢查,然后設(shè)置對(duì)話框字體,再用 WF_CONTINUEMODAL
對(duì) CWnd::m_nFlags成員做 OR 操作,然后調(diào)用 ::CreateDialogIndirect(),參數(shù)是對(duì)話框模版、父窗口句柄 和 AfxDlgProc()
(作為對(duì)話框過(guò)程)。返回TRUE或FALSE。
調(diào)用CWnd::RunModalLoop() 處理消息直至用戶按下 ”OK“或”Cancel“,OnOK() 和 OnCancel() 調(diào)用 EndDialog(),EndDialog() 調(diào)用
CWnd::EndModalLoop(),這樣就結(jié)束了loop處理過(guò)程,控制流轉(zhuǎn)給DoModal()。
調(diào)用 SetWindowPos() 隱藏目前已經(jīng)死掉的對(duì)話框,再調(diào)用 EnableWindow(hWndParent,TRUE)來(lái)激活父窗口。
· 收尾:
調(diào)用DestroyWindow()來(lái)銷(xiāo)毀Windows對(duì)話框?qū)ο螅缓笳{(diào)用PostModal() 將第一步中粘貼到Windows對(duì)象的C++對(duì)象分離,設(shè)置m_hWndTop為NULL
CDialog控件的初始化
WM_INITDIALOG
WM_INITDIALOG映射到CDIalog::HandleInitDialog().HandleInitDialog()執(zhí)行OLE控件的一些初始化,從而調(diào)用CDialog::OnInitDialog()。
OnInitDialog()調(diào)用CWnd::ExecuteDlgInit(),由后者完成最終對(duì)話框的初始化。
----------------------------------------------------------------------------------------------------------------
DDX/DDV: CDialog數(shù)據(jù)交換與驗(yàn)證 P160
CDataExchange 成員函數(shù)
· 構(gòu)造函數(shù) —— 傳入一個(gè)對(duì)話框指針和 bSaveAndValidate的初始化設(shè)置。
· PrepareCtrl() —— 為DDX/DDV準(zhǔn)備一個(gè)非編輯控件。通常這個(gè)函數(shù)會(huì)調(diào)用內(nèi)部對(duì)話框指針 m_pDlgWnd 的 GetDlgItem() 方法
· PrepareEditCtrl() —— 為DDX/DDV 準(zhǔn)備一個(gè)編輯控件。首先調(diào)用PrepareCtrl(),然后將m_bEditLastControl設(shè)置為T(mén)RUE
. Fail() —— 如果驗(yàn)證失敗就調(diào)用這個(gè)函數(shù)。將焦點(diǎn)設(shè)置到前一個(gè)控件,并拋出CUserException異常。
· PrepareOleCtrl() —— 為DDX/DDV 準(zhǔn)備一個(gè)OLE控件。
· m_bSaveAndValidate —— 指定了DDX的方向,以及是否應(yīng)該做 DDV ,F(xiàn)ALSE時(shí),數(shù)據(jù)從成員變量流到控件。
· m_pDlgWnd —— 一個(gè)指向?qū)υ捒虻腃Wnd指針,它包含了需要交換和驗(yàn)證的控件。這個(gè)值通過(guò)CDataExchange構(gòu)造函數(shù)傳入。
· m_hWndLastControl —— 記錄了前一個(gè)控件的句柄。
· m_bEditLastControl —— 一個(gè)Boolean變量,指定了前一個(gè)控件是否是編輯器。
DDX_Text()
① 先調(diào)用 PrepareEditCtrl() 或 PrepareCtrl()。
② 然后DDX_Text() 檢查 Boolean 變量 m_bSaveAndValidate的值,
③ 若標(biāo)志為T(mén)RUE(數(shù)據(jù)從控件流向成員變量),調(diào)用 Win32API(如 ::GetWindowText()),從編輯控件獲得字符串,將字符串放在"value"中
若為FALSE,調(diào)用 AfxSetWindowText(),使用CString參數(shù)的內(nèi)容更新編輯控件。
DDV_MaxChars()
檢查 CDataExchange::m_bSaveAndValidate 標(biāo)志,確保為 TRUE(即現(xiàn)在正在進(jìn)行驗(yàn)證)。然后根據(jù)指定的大小檢查字符串的長(zhǎng)度。
------------------------------------------------------------------------------------------------
CWnd::UpdateData()
①· 首先創(chuàng)建一個(gè)CDataExchange實(shí)例,將 this 作為CDialog指針傳入,并繼續(xù)傳入bSaveAndValidate 標(biāo)志(OnDialogInit()調(diào)用時(shí)為FALSE…)。
②· UpadteData() 會(huì)阻塞當(dāng)前線程,使線程不能接收控件的消息。
③· 本地Boolean變量 bOK用來(lái)存儲(chǔ) DDX/DDV 代碼的返回值。一旦UpdateData() 將 bOK設(shè)置為 FALSE,它會(huì)調(diào)用你的CDialog派生類的DoDataExchange方法,
同時(shí)將CDataExchange實(shí)例作為參數(shù)傳入。 bOK被設(shè)置為T(mén)RUE, 這個(gè)值最終返回給 OnUpdate(),表示當(dāng)前交換和驗(yàn)證沒(méi)有錯(cuò)誤。
④· 出現(xiàn)CUserException異常時(shí),調(diào)用這塊代碼,bOK被設(shè)置為FALSE,最終UpdateData會(huì)返回0;
⑤· 如果DDX/DDV處理過(guò)程中沒(méi)有任何異常拋出,會(huì)報(bào)告已經(jīng)發(fā)生的一系列錯(cuò)誤,也有可能導(dǎo)致返回FALSE。
⑥· 重置通知窗口,從而按常規(guī)方法處理控件通知,最終返回FALSE 或 TRUE(依賴于DoDataExchange()的返回值 )。
========================================================================
MFC公用對(duì)話框
CColorDialog 讓用戶選擇一種顏色
CFileDialog 用于打開(kāi)和保存文件
CFindReplaceDialog 允許用戶查找和替代項(xiàng)
CFontDialog 提供了選擇字體的對(duì)話框
CPrintDialog 讓用戶選擇打印機(jī)、頁(yè)數(shù)、打印方向
----------------------------------------------------------------------------------
屬性頁(yè)
CPropertySheet CPropertyPage
1、創(chuàng)建多個(gè)對(duì)話框模板,使模板描述你想要的屬性頁(yè)中布置的控件和布局。
2、為每個(gè)對(duì)話框模板創(chuàng)建CPropertyPage的一個(gè)派生類,在類中增加成員變量來(lái)存儲(chǔ)屬性頁(yè)的狀態(tài)。
3a、對(duì)于一個(gè)模態(tài)的屬性頁(yè),只要?jiǎng)?chuàng)建它的CPropertySheet實(shí)例,然后調(diào)用CPropertySheet::AddPage()將前兩步創(chuàng)建的單個(gè)屬性頁(yè)加入到屬性頁(yè)中。
再調(diào)用CDialog::DoModal()來(lái)顯示屬性頁(yè)。
3b、如果創(chuàng)建一個(gè)非模態(tài)的屬性頁(yè),則創(chuàng)建的CPropertySheet派生類須增加一些東西一邊能夠關(guān)閉對(duì)話框,MFC不會(huì)自動(dòng)增加OK/Apply/Cancel
然后創(chuàng)建你的CPropertySheet派生類的一個(gè)實(shí)例,并調(diào)用它的Create()來(lái)顯示非模態(tài)屬性頁(yè)。
使用屬性頁(yè)公用控件
首先,填寫(xiě)PROPSHEETHEADER結(jié)構(gòu)的一些域,然后調(diào)用 ::PropertyPage(),以該結(jié)構(gòu)為參數(shù),負(fù)責(zé)創(chuàng)建屬性頁(yè)。
創(chuàng)建完成后,屬性頁(yè)發(fā)送 WM_NOTIFY 消息:
· PSN_APPLY —— Apply 按鈕被按下
· PSN_WIXZBACK —— 在向?qū)J较拢脩舭聪铝?Previous 按鈕
· PSN_WIZNEXT —— 在向?qū)J较拢脩舭聪铝?Next 按鈕
· PSN_WIZFINISH —— 在向?qū)J较拢脩舭聪铝?Finish 按鈕
若想響應(yīng)剛提到的通知,可想屬性頁(yè)控件發(fā)送消息。
· PSM_SETTILTLE —— 設(shè)置屬性頁(yè)的標(biāo)題
· PSM_ADDPAGE —— 在屬性頁(yè)中增加一個(gè)單頁(yè)。消息的 lParam參數(shù)是一個(gè)指向已創(chuàng)建的屬性單頁(yè)(由CreatePropertySheetPage()創(chuàng)建)的句柄
· PSM_REMOVEPAGE —— 在屬性頁(yè)中刪除一個(gè)屬性單頁(yè)。
CPropertySheet 內(nèi)幕
· CommonConstruct() —— 不同的CPropertySheet構(gòu)造函數(shù)接受的參數(shù)傳遞給 CommonConstruct()。 首先設(shè)置了m_psh所指向的 PSH_PROPSHEETPAGE
結(jié)構(gòu)的 dwFlags 域,然后初始化了 m_psh 所指向結(jié)構(gòu)的幾個(gè)其他域。 并將 m_bStacked 設(shè)置為T(mén)RUE,m_bModeless設(shè)為FALSE。
· EnableStackedTabs() —— 將CPropertySheet設(shè)置為“stacked”模式而不是“scrolled”模式。只修改了 m_bStacked 成員變量。
· BuildPropPageArray() —— 負(fù)責(zé)將 m_pages 數(shù)組轉(zhuǎn)化成 PROPSHEETPAGE 結(jié)構(gòu)。
· OnInitDialog() —— 1、若不是出于 向?qū)?模式,將整個(gè)屬性頁(yè)傳遞出去,以免控件綁在一起。
2、根據(jù) m_bStacked 的值修改標(biāo)簽的風(fēng)格。通過(guò)調(diào)用 EnableStackedTabs() 完成。
3、若屬性頁(yè)為非模態(tài),也不處于 向?qū)?nbsp; 狀態(tài),OnInitDialog()就刪除標(biāo)準(zhǔn)按鈕。
4、調(diào)用 CWnd::CenterWindow(),是屬性頁(yè)出于窗口正中央。
· m_pages —— 一個(gè)指針數(shù)組,是 CPtrArray 的實(shí)例,保存了指向?qū)傩皂?yè)的所有 CPropertyPage 類的指針。
· m_strCaption —— 屬性頁(yè)的說(shuō)明
· m_pParentWnd —— 指向父窗口的指針
· m_bStacked —— 一個(gè)Boolean 變量,記錄了用戶指定的標(biāo)簽風(fēng)格是“stacked”還是“scrolled”。
· m_bModeless —— 一個(gè)Boolean變量, 記錄用戶希望屬性頁(yè)是模態(tài)還是非模態(tài)。
CPropertySheet::DoModal() (DLGPROP.CPP)
首先,調(diào)用 AfxDeferRegisterClass(),導(dǎo)致MFC調(diào)用 InitCommonControls()函數(shù),該函數(shù)初始化了Windows 通用控件 DLL。
然后調(diào)用 BuildPropPageArray(),
然后禁用主窗口,強(qiáng)制實(shí)現(xiàn)模態(tài)化,調(diào)用 ::PropertySheet() 來(lái)創(chuàng)建并顯示公用屬性頁(yè)控件。
再調(diào)用 CWnd::RunModalLoop() 在模態(tài)化窗口中處理消息泵。完成后,銷(xiāo)毀窗口,激活父窗口,然后返回 RunModalLoop() 的返回值。
tag: 直到 DoModal()(對(duì)于模態(tài)屬性頁(yè))或Create() (對(duì)于非模態(tài)屬性頁(yè))被調(diào)用,類都不能映射到一個(gè)Windows窗口句柄。
在::PropertySheet() 被調(diào)用后,Windows窗口句柄才被創(chuàng)建,并和 MFC CPropertySheet 對(duì)象綁定在一起。
CPropertySheet::AddPage()
首先,在內(nèi)部變量 m_pages(屬性單頁(yè)指針數(shù)組)中保持了CPropertyPage指針。
檢查 m_hWnd非空后,調(diào)用 ::CreatePropertySheetPage(),并發(fā)送 PSM_ADDPAGE 消息給屬性頁(yè)控件,從而讓它將創(chuàng)建的屬性單頁(yè)加入。
CPropertySheet::BuildPropPageArray()
首先,刪除了 m_psh.ppsp 域所指向的數(shù)組,并將它設(shè)置為NULL,為新數(shù)組保留控件,新的數(shù)組來(lái)自內(nèi)部 CPtrArray 數(shù)組 m_pages。
然后,為本地變量 ppsp分配了一個(gè)新數(shù)組(數(shù)組基類型是 PROPSHEETPAGES),并將 m_psh.ppsp 指向同一塊內(nèi)存。
遍歷 m_pages數(shù)組中的所有 CPropertyPage。對(duì)于每個(gè)夜,它將CPropertyPage 的內(nèi)部 PROPSHEETPAGE 結(jié)構(gòu)拷貝到 PROPSHEETHEADER 數(shù)組
ppsp中。拷貝結(jié)束后,調(diào)用_ChangePropPageFont() 來(lái)修改所有控件的字體,參數(shù)是一個(gè)指向?qū)υ捒蚰0娴闹羔槪@樣控件和屬性頁(yè)使用相同的字體。
所有頁(yè)都放入了 m_psh.ppsp,就更新 m_psh.nPages 這個(gè)單頁(yè)計(jì)數(shù),以便和 m_pages 的大小相匹配。
CProperSheet 通知
CPropertySheet 將幾個(gè)屬性頁(yè)控件通知映射為可覆蓋的回調(diào)函數(shù)
================================================================
MFC控件類
· AFXWIN2.INL —— 所有控件類的內(nèi)嵌函數(shù)
· WINCTRL1.CPP —— CStatic 、 CButton 、 CListBox 、CComboBox、CEdit和 CScrollBar
· WINCTRL2.CPP —— CDragListBox。
· WINCTRL3.CPP —— CCheckListBox
· WINBTN.CPP —— CBitmapButton
posted on 2010-03-15 23:09
Euan 閱讀(3845)
評(píng)論(0) 編輯 收藏 引用 所屬分類:
windows