下面我們來設(shè)計這個程序的UI,首先在“視圖”菜單中選擇“工具箱”,這個工具箱窗口中,列出了很多常用的Windows控件(如按鈕、復(fù)選框、編輯框、標(biāo)簽等),點擊一個控件,此時鼠標(biāo)變?yōu)槭中?,像使用畫圖工具一樣在設(shè)計器上繪出該控件,也可以同過拖拽的方法將控件直接拖到UI設(shè)計器中的對話框上,還可以直接雙擊工具欄中的控件。
在UI設(shè)計器上的靜態(tài)標(biāo)簽“TODO: 在此放置對話框控件?!笨梢詫⑵鋭h掉。按照上述方法在對話框上繪制一個按鈕控件,右擊此控件,選擇“屬性”會出現(xiàn)屬性窗口,在這個窗口中,左側(cè)是控件的一種屬性,右側(cè)是該屬性的當(dāng)前值,在屬性窗口的低端是屬性的具體含義。對于按鈕控件,我們現(xiàn)在關(guān)注Caption屬性,即按鈕上顯示的文字,我們將其設(shè)為“Hello World”,輸入完畢后按回車鍵確定。在UI設(shè)計器上的那個按鈕已經(jīng)變?yōu)椤?/span>Hello World”了。在看一下按鈕的ID屬性,這個ID表示對話框每個控件唯一的標(biāo)識(注意ID為IDC_STATIC的控件代表該控件為靜態(tài)的,在程序運行時不可以動態(tài)的改變改控件的屬性,如標(biāo)簽控件。但我們亦可以通過,修改標(biāo)簽的ID改為非IDC_STATIC值,這樣這個控件就轉(zhuǎn)為動態(tài)控件了,此種控件可以用IDC_STATIC來標(biāo)識,IDC_STATIC沒有唯一的限制,),我們一般需要將該屬性值改為更有意義的,如IDC_HELLOWORLD。
下面我們?yōu)榘粹o控件編寫事件響應(yīng)代碼,右擊Hello World按鈕,選擇“添加事件處理程序”,依然彈出了一個向?qū)υ捒颍谙㈩愋椭羞x擇選擇按鈕要響應(yīng)的消息,在這個程序中我們是要響應(yīng)按鈕的單擊事件(其實在一般情況下,按鈕就是用來單擊的),所以保持默認“BN_CLICKED”,在類列表中說明了響應(yīng)代碼是作為哪個類的成員函數(shù),默認選擇那個以“Dlg”結(jié)尾的類,這個類是繼承于CDialog類的一個派生類。程序員可以設(shè)置自己的“函數(shù)處理程序名稱”。完成后點擊“添加編輯”按鈕,進入代碼編輯器編輯代碼(對于按鈕這種簡單控件,我們要添加其響應(yīng)代碼,一般在UI設(shè)計器中直接雙擊這個控件即可直接進入到代碼編輯器中編輯響應(yīng)默認消息的函數(shù))。
系統(tǒng)為我們自動在CHelloWorldDlg類中添加了OnBnClickedHelloworld()函數(shù),該函數(shù)的聲明部分在HelloWorldDlg.h文件中,在VS9開發(fā)環(huán)境的右側(cè)“解決方案資源管理器”中雙擊該文件,或在HelloWorldDlg.cpp文件中右擊,在彈出菜單中選擇“轉(zhuǎn)到頭文件”。讓我們來看一下這個MFC框架是如何構(gòu)成的。
在這個頭文件的頭部有一行“#pragma once”,這是條編譯命令,功能是讓次頭文件(Header)在編譯時只被編譯一次,因為同一個頭文件可能被包含(include)過多次。在這個頭文件中定義了CHelloWorld類,在這個類中聲明了一個HICON類型的m_hIcon成員變量,這是個描述該應(yīng)用程序圖標(biāo)的變量。如果你不了解HICON這個非標(biāo)準(zhǔn)類型,可以在代碼的HICON處右擊鼠標(biāo),然后選擇“轉(zhuǎn)到聲明”或“轉(zhuǎn)到定義”,這是VS9會自動定位光標(biāo)到HICON的聲明或定義代碼處,筆者認為這是一個非常體貼的功能,為團隊開發(fā)提供了很大的便利。
這里,我們順便來簡單談?wù)?span style="line-height:normal;">MFC采用的“匈牙利標(biāo)識符命名法”,這是一個約定,可以增加代碼的可讀性。如果你聲明或定義了一個類,那么這個類可以一“C”(class)為前綴,如CHelloWorldDlg類,如果要定義一個無符號型的局部變量,那么可以用“u”(unsigned)為前綴,如UINT uPort; ULONG uFlags;如果是int或long類型的變量則以“n”為前綴,DWORD類型的變量前綴為“dw”,字符數(shù)組以“sz”作為前綴,CString類的對象以“str”作為前綴,指針以“lp”或“p”(long pointer或pointer,在WIN32環(huán)境下這兩種指針并沒有什么差別)作為前綴,引用以“r”為前綴,布爾型變量以“b”為前綴,句柄型的變量以“h”(handle)作為前綴。如果變量是全局的,那么以“g_”(global)開頭,如BOOL g_bFlags;如果是類的成員變量則以“m_”(member)開頭,如HICON m_hIcon;。
討論完MFC的命名規(guī)則,讓我們繼續(xù)看這個頭文件,在此類中聲明了一個標(biāo)準(zhǔn)構(gòu)造函數(shù)CHelloWorldDlg(CWnd* pParent = NULL);可選形參CWnd* pParent指定此對話框的父窗口。
代碼enum { IDD = IDD_HELLOWORLD_DIALOG };的意思是MFC巧妙的將ID為IDD_HELLOWORLD_DIALOG的對話框資源與CHelloWorldDlg類綁定在一起。
這些除外的成員函數(shù)都是對父類CDialog中成員函數(shù)的重載。
成員函數(shù)virtual void DoDataExchange(CDataExchange* pDX);是用來支持DDX(對話框數(shù)據(jù)交換,將一個變量和一個控件進行綁定的時候用DDX)和DDV(對話框數(shù)據(jù)效驗,檢驗該控件是否為你所需要的時候用DDV)機制的成員函數(shù)。
成員函數(shù)virtual BOOL OnInitDialog();是在對話被創(chuàng)建(Create)后立即被執(zhí)行的函數(shù),因此在這里可以添加對話框的初始化所需要的自定義代碼。
成員函數(shù)afx_msg void OnSysCommand(UINT nID, LPARAM lParam);是對話框的處理WM_SYSCOMMAND消息的函數(shù)。WM_SYSCOMMAND消息是關(guān)于系統(tǒng)控制的消息,如鼠標(biāo)在標(biāo)題欄上的操作等。
成員函數(shù)afx_msg void OnPaint();是對話框處理WM_PAINT的函數(shù),當(dāng)對話框窗體發(fā)生重繪時有WM_PAINT消息到達程序。
成員函數(shù)afx_msg HCURSOR OnQueryDragIcon();當(dāng)用戶拖動最小化窗口時系統(tǒng)調(diào)用此函數(shù)取得光標(biāo)顯示。
接下來是一個DECLARE_MESSAGE_MAP()宏,這是MFC處理消息用的,具體資料可以查閱MSDN。
最下一行afx_msg void OnBnClickedHelloworld();就是剛自動生成的處理按鈕單擊消息的處理函數(shù)。
讀者可能注意到了afx_msg,在MFC中,只要是處理消息的響應(yīng)函數(shù),在聲明時,都要加上afx_msg。通過查找其定義,筆者認為這個并沒有什么功能性意義,只是一個占位用的或是一個說明標(biāo)識。
我們分析完HelloWorldDlg.h文件后,再來看看HelloWorldDlg.cpp文件。在文件的include部分,包含了stdafx.h文件,讀者可以在“”stdafx.h””上右擊鼠標(biāo),打開該頭文件,在這個頭文件中包含了該MFC程序必要的文件,如果程序員要添加自定義類,如果類中還需要MFC支持,那么必須包含這個頭文件。HelloWorld.h文件中定義了ChelloWorldApp類(繼承于CWinApp),是MFC的應(yīng)用程序類。一個MFC WIN32應(yīng)用程序必須是在CWinApp對象之上的,在CHelloWorldApp類中的InitInstance()成員函數(shù)的定義中,有相應(yīng)加載該對話框的代碼(CHelloWorldDlg dlg;之后的代碼)。
然后是一組宏定義#ifdef _DEBUG #define new DEBUG_NEW #endif,_DEBUG宏對應(yīng)前面提到的Debug模式。
接下來定義了另一個對話框類CAboutDlg,這是個“關(guān)于”對話框的類,用來顯示程序版權(quán)信息的。
來看看BEGIN_MESSAGE_MAP() …… END_MESSAGE_MAP()這組宏定義,在這中間的代碼就是MFC用來指定哪個消息用哪個成員函數(shù)來響應(yīng)。對于那些無需指定的,MFC則沒有明確指定,如ON_WM_PAINT()是指定處理WM_PAINT消息的成員函數(shù)是OnPaint(),但,這個用戶一般不會去更改,所以在這里就沒有去明確指定。但ON_BN_CLICKED()中,卻指定了具體的成員函數(shù):ID值為IDC_HELLOWORLD的控件的BN_CLICKED消息由CHelloWorldDlg類的OnBnClickedHelloworld()成員函數(shù)來處理。
在看看OnInitDialog()函數(shù)的定義部分,首先調(diào)用了父類了初始化函數(shù),接下來到SetIcon前,是將“關(guān)于”加載到了系統(tǒng)菜單中,這樣用戶在單擊程序窗體左上角的圖標(biāo)時,或右擊標(biāo)題欄時,或右擊任務(wù)欄長條圖標(biāo)時,彈出的菜單就會包含“關(guān)于”的菜單項。
SetIcon()的后面有相應(yīng)注釋:“設(shè)置大圖標(biāo)”和“設(shè)置小圖標(biāo)”。
在OnSysCommand()函數(shù)中,處理了有關(guān)標(biāo)題欄上發(fā)生的事件,代碼(nID & 0xFFF0) == IDM_ABOUTBOX表示,如果用戶點擊了標(biāo)題欄系統(tǒng)菜單中的“關(guān)于”菜單項,顯示“關(guān)于”對話框。
在OnPaint()函數(shù)中的代碼,是用來實現(xiàn)重繪操作的。
----------------------------------------------------------
編輯框(Edit)控件。以ID為IDC_INPUT的編輯控件為例:
獲取/設(shè)置編輯框內(nèi)的文字、設(shè)置其可用狀態(tài)和設(shè)置其可見狀態(tài)等方法與操作按鈕的方法類似,在次就不再贅述了。
我們知道我們通過GetDlgItem()函數(shù)是獲取的其文本內(nèi)容,如果想要獲取其數(shù)值,則可以用int nValue = _ttoi(strInput);的方法來轉(zhuǎn)換。這里介紹一個DDX的方法,將一個成員變量與一個目標(biāo)控件進行綁定,這樣變量的值就可以用來反應(yīng)控件的值了,同樣,也可以通過設(shè)置這個變量的值,來控制控件的值。下面來看具體步驟:在工具箱中,拖拽一個編輯框到對話框上,ID設(shè)為IDC_INPUT。右鍵點擊這個控件,選擇“添加變量”菜單項,在彈出的“添加成員變量向?qū)А睂υ捒蛑性O(shè)置其訪問屬性(筆者建議用protected);在類別列表框中選擇Value(默認是Control);在變量類型組合框中選擇一種變量類型,這里選擇int;輸入變量名m_nValue;最小值、最大值、最大字符數(shù)和注釋,根據(jù)實際情況選填(這里略過);點擊完成按鈕。完成“添加變量”向?qū)Ш?,?/span>HelloWorldDlg.cpp文件中的CHelloWorldDlg::DoDataExchange()中,系統(tǒng)自動添加了一條語句:DDX_Text(pDX, IDC_INPUT, m_nValue);這條語句的意思是將ID為IDC_INPUT的控件與m_nValue成員變量進行綁定。當(dāng)程序執(zhí)行UpdateData()的時候,數(shù)據(jù)便開始進行交換,數(shù)據(jù)交換方向由UpdateData的參數(shù)確定。
在對話框上繪制一個按鈕,Caption設(shè)為“判斷”,雙擊編寫其消息響應(yīng)代碼:
void CHelloWorldDlg::OnBnClickedButton1()
{
// TODO: 在此添加控件通知處理程序代碼
UpdateData(TRUE);
if (m_nValue >= 0) MessageBox(_T("大于等于零"));
else MessageBox(_T("小于零"));
}
這里,UpdateData(TRUE);就是讓文本框的值更新到m_nValue里,如果是UpdateData(FALSE);就是將變量中的數(shù)據(jù)返回給文本框里。在對話框初始化時,MFC會默認調(diào)用一次UpdateData(FALSE);,用來初始化控件中的顯示內(nèi)容;在對話框結(jié)束時,MFC會調(diào)用一次UpdateData(TRUE);,用來更新控件數(shù)據(jù)到成員變量中去。
還可以為為控件添加控件變量,即在“添加變量向?qū)А敝械淖兞款悇e列表中選擇Control,則系統(tǒng)會添加一個MFC的控件類的對象作為改對話框的成員變量,并且將這個控件對象與響應(yīng)的控件進行綁定,這樣操作這個控件對象就相當(dāng)于操作相應(yīng)的控件。
復(fù)選框(Check)和單選框(Radio)控件。分別以ID為IDC_CHECK1和IDC_RADIO1的控件為例:
獲取/設(shè)置其選中狀態(tài):
成員函數(shù)原型:
int GetCheck() const;
void SetCheck(int nCheck);
這兩個函數(shù)都是CButton類的成員函數(shù),所以在用GetDlgItem()獲取CWnd指針后,一定要強制轉(zhuǎn)換為CButton的指針類型。
例子:
BOOL bState;
bState = ((CButton*)GetDlgItem(IDC_CHECK1))->GetCheck(); //獲取復(fù)選框狀態(tài)
((CButton*)GetDlgItem(IDC_RADIO1))->SetCheck(1); //設(shè)置單選框狀態(tài)為選中
注意,當(dāng)復(fù)選框的Tri-State屬性為True的時候,那么復(fù)選框可以有三種狀態(tài),在SetCheck()的時候可以通過傳給0、1或2來設(shè)置狀態(tài)。
操作復(fù)選框和單選框的狀態(tài),同樣也可以采用DDX的方法,添加一個int型的成員變量來與控件進行綁定。
列表框(List)和組合框(Combo),在此只介紹成員函數(shù)原型:
列表框常用成員函數(shù):
void ResetContent(); //清空列表
int AddString(LPCTSTR lpszItem); //追加列表項
int DeleteString(UINT nIndex); //刪除指定項
int SetSel(int nIndex, BOOL bSelect = TRUE); //設(shè)置指定項的選擇狀態(tài)
int SetCurSel(int nSelect); //設(shè)置當(dāng)前選中項
int GetCurSel() const; //獲取當(dāng)前選中項
int GetSelItems(int nMaxItems, LPINT rgIndex) const; //獲取所有選中項
int GetSelCount() const; //獲取選中項總數(shù)
int GetTextLen(int nIndex) const; //獲取指定項的文本長度
int GetText(int nIndex, LPTSTR lpszBuffer) const; //獲取指定項文本
void GetText(int nIndex, CString &rString) const;
int FindString(int nStartAfter, LPCTSTR lpszItem) const; //查找指定項
對于組合框來說,可以理解為由編輯框和列表框組合而成的復(fù)合控件,因此其MFC中的成員函數(shù)與也可以參照編輯框和列表框控件類的成員函數(shù)。
對話框編程就介紹到此了,更多具體資料還請參看Microsoft的MSDN,MSDN的簡體中文網(wǎng)址為:http://msdn.microsoft.com/。MSDN是Microsoft最大的資料庫。