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