轉https://blog.csdn.net/wang18323834864/article/details/78621633
以下是我從其他網站中學的內容,後有相應的網站學習鏈接地址,可供學習
1.選擇菜單項File->New->Project,彈出“New Project”對話框。
2.左側面板中Installed Templated的Visual C++下選擇MFC,中間窗口中選擇MFC Application,然後在下面的Name編輯框中鍵入工程名稱,本例取名“Addition”,在Location編輯框中設置工程的保存路徑。點“OK”。
3.點“Next”到“Application Type”對話框,在Application type下選擇Dialog based,其他使用默認設置,點“Finish”。
我們可以在Solution Explorer視圖中看到,此工程的文件要比單文檔應用程序少的多,在Class View中主要有三個類:CAboutDlg、CAdditionApp和CAdditionDlg。CAboutDlg是應用程序的“關於”對話框類,CAdditionApp是由CWinApp派生的類,CAdditionDlg是主對話框類,主對話框也就是此應用程序運行後顯示的主要界面。
在Resource View視圖中可以看到工程Addition的資源樹,展開Addition.rc,下面有四個子項:Dialog(對話框)、Icon(圖標)、String Table(字符串表)和Version(版本)。然後展開Dialog項,下面有兩個對話框模板,其ID分別爲:IDD_ABOUTBOX和IDD_ADDITION_DIALOG,前者是“關於”對話框的模板,後者是主對話框的模板。ID是資源的唯一標識,本質上是一個無符號整數,一般ID代表的整數值由系統定義,我們無需干涉。
可見對於主對話框來說,創建對話框第一步中的創建新的對話框模板已經由系統自動完成了。而如果是再添加對話框需要創建新的對話框模板時,需要在Resource View的“Dialog”節點上點右鍵,在右鍵菜單中選擇“Insert Dialog”,就會生成新的對話框模板,並且會自動分配ID。
在Resource View的資源樹中雙擊某個ID,可在中間區域內顯示相應的資源界面。雙擊IDD_ADDITION_DIALOG時,中間區域就會顯示Addition對話框模板。如下圖:
在Addition對話框模板上點右鍵,然後在右鍵菜單中選擇Properties,則在右側面板中會顯示對話框的屬性列表。如下圖:
1.ID:對話框ID,唯一標識對話框資源,可以修改。此處爲IDD_ADDITION_DIALOG,我們不修改它。
2.Caption:對話框標題。此處默認爲Addition,我們將其修改爲“加法計算器”。
3.Border:邊框類型。有四種類型:None、Thin、Resizing和Dialog Frame。我們使用默認的Dialog Frame。
4.Maximize:是否使用最大化按鈕。我們使用默認的False。
5.Minimize:是否使用最小化按鈕。同樣我們使用默認的False。
6.Style:對話框類型。有三種類型:Overlapped(重疊窗口)、Popup(彈出式窗口)和Child(子窗口)。彈出式窗口比較常見。我們使用默認的Popup類型。
7.System Menu:是否帶有標題欄左上角的系統菜單,包括移動、關閉等菜單項。我們使用默認的True。
8.Title Bar:是否帶有標題欄。我們使用默認的True。
9.Font(Size):字體類型和字體大小。如果將其修改爲非系統字體,則Use System自動改爲False。而如果Use System原來爲False,將其修改爲True,則Font(Size)自動設置爲系統字體。這裏我們使用默認的系統字體。
根據以上說明,其實我們只修改了標題屬性。這時我們運行此程序後的界面如下:
1.爲對話框添加一個靜態文本框(Static Text),用於顯示字符串--“被加數”。
對於自動添加的一個標題爲“TODO:Place dialog controls here.”的靜態文本框,這裏將它刪掉,繼續添加新的靜態文本框。
刪除控件時,可以使用鼠標左鍵點擊選中它,選中後控件的周圍會出現虛線框,然後按Delete鍵就可以將其刪除了。在“Addition”工程的Resource View中打開上一講中創建的對話框模板IDD_ADDITION_DIALOG,自動添加的靜態文本框就可以使用這種方法刪除。
在添加新的靜態文本框以前,先看看Toolbox視圖是否顯示了,如果沒有顯示,在菜單欄上點擊View->Toolbox即可。Toolbox視圖如下圖:
Toolbox中列出了一些常用控件,其中有一個是Static Text,即是我們要添加的控件。在Toolbox中的Static Text上點下鼠標左鍵不放開,並拖到IDD_ADDITION_DIALOG對話框模板上,模板上會出現一個虛線框,我們找到合適的位置鬆開鼠標左鍵放下它。
用鼠標左鍵選中控件後周圍出現虛線框,然後鼠標移到虛線框上幾個黑點的位置會變成雙向箭頭的形狀,此時就可以按下鼠標左鍵並拖動來改變控件大小了。我們可以這樣改變新添加的靜態文本框控件的大小,以更好的顯示標題。當然,整個對話框模板也可以用這種方法改變大小。
接下來就該修改靜態文本框的文字了。鼠標右鍵點擊靜態文本框,在右鍵菜單中選擇“Properties”,Properties面板就會顯示出來,在面板上修改Caption屬性爲“被加數”,ID修改爲IDC_SUMMAND_STATIC。此時模板如下圖:
2.爲對話框添加一個編輯框(Edit Control),用來輸入被加數。
添加編輯框的過程與靜態文本框類似,在Toolbox中選中Edit Control控件拖到對話框模板上,並使其與之前的靜態文本框水平對齊(爲了美觀),然後調整其大小使之適合被加數的輸入。
在編輯框上點右鍵,仍然在右鍵菜單中選擇“Properties”顯示出屬性(Properties)面板,修改其ID爲IDC_SUMMAND_EDIT。此時模板如下圖:
3.按照1的方法添加一個標題爲“加數”的靜態文本框,用於顯示字符串--“加數”。並將其ID改爲IDC_ADDEND_STATIC。
4.按照2的方法添加一個ID爲IDC_ADDEND_EDIT的編輯框,用來輸入加數。
5.按照1的方法添加一個標題爲“和”的靜態文本框,用於顯示文字--“和”。並修改其ID爲IDC_SUM_STATIC。
6.按照2的方法添加一個ID爲IDC_SUM_EDIT的編輯框,用來顯示最終的加和。
7.類似的添加按鈕(Button)控件到對話框模板,用於在被點擊後觸發加法計算。修改其標題爲“計算”,ID爲IDC_ADD_BUTTON。
到此,對話框模板如圖:
8.刪除OK按鈕。打開Cancel按鈕的屬性面板,將標題改爲“退出”,並使其與“計算”按鈕水平對齊。
9.根據控件的佈局,適當調整整個對話框模板的大小,使其相對控件佈局來說大小合適,界面美觀。
這樣在對話框模板中就把我們在本例中需要用到的控件就添加完了。最終效果如下:
因爲例程Addition是基於對話框的程序,所以程序自動創建了對話框模板IDD_ADDITION_DIALOG,並自動生成了對話框類CAdditionDlg,它是從CDialogEx類派生的。大家用過VC++ 6.0的可能記得,我們定義的對話框類都是從CDialog類派生的,但在VS2010中,一般對話框類都是繼承自CDialogEx類。
創建對話框類
如果是自己新添加的對話框模板,怎樣爲它創建對話框類呢?
1.在Resource View的“Dialog”節點上右鍵,然後在右鍵菜單中選擇“Insert Dialog”創建一個新的對話框模板,ID就使用默認的IDD_DIALOG1。
2.在中間區域會顯示新建的對話框模板,然後選中此對話框模板,點右鍵,在右鍵菜單中選擇Add Class。
3.選擇“Add Class”後會彈出一個對話框,在對話框中“Class name”下的編輯框中寫入自定義的類名就可以了,例如CMyDialog。
4.最後點“Finish”完成。
最終你就可以在Class View中看到新生成的對話框類CMyDialog了,並且在Solution Explorer中有相應的MyDialog.h頭文件和MyDialog.cpp源文件生成。CMyDialog類同樣派生於CDialogEx類。
注意,一般類名都以C打頭,又比如,CTestDlg。
爲對話框中的控件添加變量
在上一講中爲對話框添加了幾個控件,包括三個靜態文本框,三個編輯框,一個按鈕控件。程序自動生成的Cancel按鈕保留,作爲退出按鈕,而OK按鈕刪除掉了。
靜態文本框只是爲了說明後面緊跟的編輯框中數據的意義,是被加數、加數還是和,所以它們是不會變的,我們就不爲它們添加變量了。按鈕控件是用來操作的,這裏也不爲它們添加變量。編輯框中的數據可能會經常變化,有必要爲它們每個控件關聯一個變量。
首先爲被加數的編輯框IDC_SUMMAND_EDIT添加變量。
1.在編輯框上點右鍵,在右鍵菜單中選擇“Add Variable”。彈出添加成員變量的嚮導對話框。
2.我們想爲其添加值變量而不是控件變量,所以對話框中“Category”下的組合框中選擇Value。
3.“Variable type”下的組合框此時默認選中的是“CString”,CString是字符串類,顯然不能進行加法運算。我們可以選擇double、float、int等。這裏我們選擇double,即編輯框關聯一個double類型的變量。
4.在“Variable name”中寫入自定義的變量名。雞啄米爲其取名m_editSummand。
5.點“Finish”完成。
注意,類的成員變量名一般以m_打頭,以標識它是一個成員變量。
參照此方法,再分別爲加數的編輯框IDD_ADDEND_EDIT添加double型變量m_editAddend、和的編輯框IDD_SUM_EDIT添加double型變量m_editSum。
對話框類的數據交換和檢驗
在程序運行界面中,用戶往往會改變控件的屬性,例如,在編輯框中輸入字符串,或者改變組合框的選中項,又或者改變複選框的選中狀態等。控件的屬性改變後MFC會相應修改控件關聯變量的值。這種同步的改變是通過MFC爲對話框類自動生成的成員函數DoDataExchange()來實現的,這也叫做對話框的數據交換和檢驗機制。
我們爲三個編輯框添加了變量以後,在AdditionDlg.cpp中CAdditionDlg的DoDataExchange()函數的函數體中多了三條DDX_Text調用語句。下面是函數體代碼和雞啄米添加的註釋。
C++代碼
- void CAdditionDlg::DoDataExchange(CDataExchange* pDX)
- {
- // 處理MFC默認的數據交換
- CDialogEx::DoDataExchange(pDX);
- // 處理控件IDC_SUMMAND_EDIT和變量m_editSummand之間的數據交換
- DDX_Text(pDX, IDC_SUMMAND_EDIT, m_editSummand);
- // 處理控件IDC_ADDEND_EDIT和變量m_editAddend之間的數據交換
- DDX_Text(pDX, IDC_ADDEND_EDIT, m_editAddend);
- // 處理控件IDC_SUM_EDIT和變量m_editSum之間的數據交換
- DDX_Text(pDX, IDC_SUM_EDIT, m_editSum);
- }
再以Addition程序爲例簡單說下數據交換機制。如果我們在程序運行界面中輸入被加數,則通過CAddition的DoDataExchange()函數可以將輸入的值保存到m_editSummand變量中,反之如果程序運行中修改了變量m_editSummand的值,則通過CAddition的DoDataExchange()函數也可以將新的變量值顯示到被加數的編輯框中。
但是這種數據交換機制中,DoDataExchange()並不是被自動調用的,而是需要我們在程序中調用CDialogEx::UpdateData()函數,由UpdateData()函數再去自動調用DoDataExchange()的。
CDialogEx::UpdateData()函數的原型爲:
BOOL UpdateData(BOOL bSaveAndValidate = TRUE);
參數:bSaveAndValidate用於指示數據傳輸的方向,TRUE表示從控件傳給變量,FALSE表示從變量傳給控件。默認值是TRUE,即從控件傳給變量。
返回值:CDialogEx::UpdateData()函數的返回值表示操作是否成功,成功則返回TRUE,否則返回FALSE。
在下一講中將具體演示CDialogEx::UpdateData()函數如何使用。
雞啄米本節主要講的是新建對話框類和添加控件變量,控件的消息處理函數將在下一講詳細介紹。
*********************************************************************************************************************************************************************
創建對話框類和添加控件變量在上一講中已經講過,這一講的主要內容是如何爲控件添加消息處理函數。
MFC爲對話框和控件等定義了諸多消息,我們對它們操作時會觸發消息,這些消息最終由消息處理函數處理。比如我們點擊按鈕時就會產生BN_CLICKED消息,修改編輯框內容時會產生EN_CHANGE消息等。一般爲了讓某種操作達到效果,我們只需要實現某個消息的消息處理函數。
一.添加消息處理函數
雞啄米仍以前面的加法計算器的程序爲例,說明怎樣爲“計算”按鈕控件添加消息處理函數。添加方法列出4種:
1.使用Class Wizard添加消息處理函數
用過的VC++ 6.0的朋友應該對Class Wizard很熟悉了,添加類、消息處理函數等經常會用到它,可以說是一個很核心的功能。但從VS2002開始就見不到Class Wizard了,大部分功能都集成到對話框和控件等的屬性中了,使用很方便。到VS2010,久違的Class Wizard又回來了。但雞啄米已經習慣了使用屬性中的功能了,對於從VC++ 6.0直接轉VS2010的朋友可能覺得還是使用Class Wizard比較習慣。
大家應該記得,“計算”按鈕的ID爲IDC_ADD_BUTTON,上圖中Commands標籤下,Oject IDs列表中有此ID,因爲我們是想實現點擊按鈕後的消息處理函數,所以在Messages列表中選擇BN_CLICKED消息,然後點右上方的Add Handler就可以添加BN_CLICKED消息處理函數OnClickedAddButton了。當然你也可以改名,但一般用的默認的就可以。
2.通過“Add Event Handler...”添加消息處理函數
在“計算”按鈕上點右鍵,然後在右鍵菜單中選擇菜單項“Add Event Handler...”,彈出“Event Handler Wizard”對話框,如下圖:
可見“Message type”中默認選中的就是BN_CLICKED消息,函數名和所在類都已經自動給出,直接點“Add and Edit”就可以了。
3.在按鈕的屬性視圖中添加消息處理函數
上面說過,從VS2002開始就主要從屬性視圖添加消息處理函數了。我們在“計算”按鈕上點右鍵,在右鍵菜單中選擇“Properties”,右側面板中會顯示按鈕的屬性視圖。
我們可以像上圖中那樣,點屬性視圖的“Control Events”按鈕(類似閃電標誌),下面列出了“計算”按鈕的所有消息。我們要處理的是BN_CLICKED消息,點其右側空白列表項,會出現一個帶下箭頭的按鈕,再點此按鈕會出現“<Add> OnBnClickedAddButton”選項,最後選中這個選項就會自動添加BN_CLICKED處理函數了。
4.雙擊按鈕添加消息處理函數
最直接最簡單的方法就是,雙擊“計算”按鈕,MFC會自動爲其在CAdditionDlg類中添加BN_CLICKED消息的處理函數OnBnClickedAddButton()。
二.在消息處理函數中添加自定義功能
在我們使用任意一種方法添加了消息處理函數以後,都只能得到一個空的OnBnClickedAddButton()函數的函數體,要實現我們想要的功能,還需要在函數體中加入自定義功能代碼。
在加法計算器程序中,我們想要“計算”按鈕實現的功能是,獲取被加數和加數的數值,然後計算它們的和並顯示到和的編輯框裏。那麼,OnBnClickedAddButton()的函數體就應修改爲:
C++代碼
- void CAdditionDlg::OnBnClickedAddButton()
- {
- // TODO: Add your control notification handler code here
- // 將各控件中的數據保存到相應的變量
- UpdateData(TRUE);
- // 將被加數和加數的加和賦值給m_editSum
- m_editSum = m_editSummand + m_editAddend;
- // 根據各變量的值更新相應的控件。和的編輯框會顯示m_editSum的值
- UpdateData(FALSE);
- }
雞啄米在上面的代碼中已經添加註釋,大家應該很容易理解了。對於UpdateData()函數的說明在上一講中已經介紹過,如果忘了可以再回上一講了解了解。
接下來我們運行下此應用程序。在運行結果界面中,輸入被加數5.1,加數2.3,然後點“計算”:
在上圖中可以看到,點“計算”按鈕後,和的編輯框中顯示了正確結果:7.4。
雞啄米簡單分析下運行過程:輸入被加數和加數,點“計算”按鈕後產生點擊消息,從而調用OnBnClickedAddButton()函數。進入此函數後,首先由UpdateData(TRUE)函數將被加數的值5.1和加數的值2.3分別保存到變量m_editSummand和m_editAddend,然後通過語句m_editSum = m_editSummand + m_editAddend;計算出被加數和加數的和爲7.4,並把7.4賦值給m_editSum。最後調用UpdateData(FALSE)根據被加數、加數、和的值更新三個編輯框的顯示值,就得到了上圖中的結果。
到此,一個具有簡單的加法運算功能的加法計算器應用程序就基本完成了。
************************************************************************************************************************************************************************
本節主要講對話框上控件的Tab順序如何調整。
上一講爲“計算”按鈕添加了消息處理函數後,加法計算器已經能夠進行浮點數的加法運算。但是還有個遺留的小問題,就是對話框控件的Tab順序問題。
運行加法計算器程序,顯示對話框後不進行任何操作,直接按回車,可以看到對話框退出了。這是因爲“退出”按鈕是Tab順序爲1的控件,也就是第一個接受用戶輸入的控件。但是按照我們的輸入習慣,應該是被加數的編輯框首先接受用戶輸入,然後是加數編輯框,再接下來是“計算”按鈕,最後纔是“退出”按鈕。
我們先來直觀的看看各個控件的Tab順序吧。打開“Resource View”視圖,然後在資源中找到對話框IDD_ADDITION_DIALOG,雙擊ID後中間客戶區域出現其模板視圖。在主菜單中選擇“Format”->"Tab Order",或者按快捷鍵Ctrl+D,對話框模板上就會顯示各個控件的Tab順序數字。如下圖:
上圖中每個控件左上角都有一個數字,這就是它的Tab響應順序。對話框剛打開時輸入焦點就在Tab順序爲1的“退出”按鈕上,不做任何操作按下Tab鍵,輸入焦點就會轉移到Tab順序爲2的“被加數”靜態文本框上,但是因爲靜態文本框不接受任何輸入,所以輸入焦點繼續自動轉移到Tab順序爲3的被加數編輯框,再按Tab鍵,輸入焦點又會轉移到Tab順序爲4的“加數”靜態文本框上,同樣由於它是靜態文本框,輸入焦點不停留繼續轉移到加數編輯框,後面的控件同理。
我們認爲這個順序不合理,那怎麼修改呢?很簡單,從自己認爲Tab順序應該爲1的控件開始依次單擊,隨着單擊的完成,各控件的Tab響應順序也按我們的想法設置好了。
例如,此例中我們可以依次單擊被加數編輯框、“被加數”靜態文本框、加數編輯框、“加數”靜態文本框、和編輯框、“和”靜態文本框、“計算”按鈕和“退出”按鈕。設置完後如下圖:
最後按ESC鍵,確認設置並退出對話框模板的Tab順序設置狀態。
現在我們再運行程序,可以看到對話框打開後最初的輸入焦點在被加數編輯框上,然後我們按Tab鍵,輸入焦點移到加數編輯框上,繼續多次按Tab鍵時,輸入焦點會按“和編輯框--‘計算’按鈕--‘退出’按鈕--被加數編輯框--加數編輯框--和編輯框......”的順序循環轉移。這樣就達到了我們的目的。
*******************************************************************************************************************************************************************************************
加法計算器對話框程序大家照着做一遍後,相信對基於對話框的程序有些瞭解了,有個好的開始對於以後的學習大有裨益。趁熱打鐵,雞啄米這一節講講什麼是模態對話框和非模態對話框,以及模態對話框怎樣彈出。
一.模態對話框和非模態對話框
Windows對話框分爲兩類:模態對話框和非模態對話框。
模態對話框是這樣的對話框,當它彈出後,本應用程序其他窗口將不再接受用戶輸入,只有該對話框響應用戶輸入,在對它進行相應操作退出後,其他窗口才能繼續與用戶交互。
非模態對話框則是,它彈出後,本程序其他窗口仍能響應用戶輸入。非模態對話框一般用來顯示提示信息等。
大家對Windows系統很瞭解,相信這兩種對話框應該都遇到過。之前的加法計算器對話框其實就是模態對話框。
二.模態對話框是怎樣彈出的
畢竟加法計算器程序大部分都是MFC自動生成的,對話框怎麼彈出來的大家可能還不是很清楚。雞啄米下面簡單說說它是在哪裏彈出來的,再重新建一個新的對話框並彈出它,這樣大家實踐以後就能更靈活的使用模態對話框了。
大家打開Addition.cpp文件,可以看到CAdditionApp類有個InitInstance()函數,在MFC應用程序框架分析中提到過此函數,不過那是單文檔應用程序App類中的,函數體不太相同,但都是進行App類實例的初始化工作。
InitInstance()函數的後半部分有一段代碼就是定義對話框對象並彈出對話框的,雞啄米下面給出這段代碼並加以註釋:
C++代碼
- CAdditionDlg dlg; // 定義對話框類CAdditionDlg的對象dlg
- m_pMainWnd = &dlg; // 將dlg設爲主窗口
- INT_PTR nResponse = dlg.DoModal(); // 彈出對話框dlg,並將DoModal函數的返回值(退出時點擊按鈕的ID)賦值給nResponse
- if (nResponse == IDOK) // 判斷返回值是否爲OK按鈕(其ID爲IDOK,雞啄米已經將它刪除)
- {
- // TODO: Place code here to handle when the dialog is
- // dismissed with OK
- }
- else if (nResponse == IDCANCEL) // 判斷返回值是否爲Cancel按鈕(其ID爲IDCANCEL,雞啄米將它的Caption改爲了“退出”)
- {
- // TODO: Place code here to handle when the dialog is
- // dismissed with Cancel
- }
彈出對話框比較關鍵的一個函數,就是對話框類的DoModal()函數。CDialog::DoModal()函數的原型爲:
virtual INT_PTR DoModal();
返回值:整數值,指定了傳遞給CDialog::EndDialog(該函數用於關閉對話框)的nResult參數值。如果函數不能創建對話框,則返回-1;如果出現其它錯誤,則返回IDABORT。
調用了它對話框就會彈出,返回值是退出對話框時所點的按鈕的ID,比如,我們點了“退出”按鈕,那麼DoModal返回值爲IDCANCEL。
三.添加一個新對話框並彈出它
雞啄米再爲加法計算器程序添加一個對話框,以在計算之前詢問用戶是否確定要進行計算。大家可以完整的看下對話框的添加和彈出過程。
1.在Resource View中的“Dialog”上點右鍵選擇“Insert Dialog”,創建一個新的對話框模板,修改其ID爲IDD_TIP_DIALOG,Caption改爲“提示”,然後參考“爲對話框添加控件”中所講,在對話框模板上添加一個靜態文本框(static text),Caption改爲“您確定要進行加法計算嗎?”,接下來修改OK按鈕的Caption爲“確定”,Cancel按鈕的Caption爲“取消”,最後調整各個控件的位置和對話框的大小。最終的對話框模板如下圖:
2.根據“創建對話框類和添加控件變量”中創建對話框類的方法,在對話框模板上點右鍵選擇“Add Class...”,彈出添加類的對話框,設置“Class name”爲CTipDlg,點“OK”。在Solution Explorer中可以看到生成了CTipDlg類的頭文件TipDlg.h和源文件TipDlg.cpp。
3.我們要在點“計算”按鈕之後彈出此提示對話框,那麼就要在“計算”按鈕的消息處理函數OnBnClickedAddButton()中訪問提示對話框類,所以爲了訪問CTipDlg類,在AdditionDlg.cpp中包含CTipDlg的頭文件:#include "TipDlg.h"。
4.修改OnBnClickedAddButton()的函數體,在所有代碼前,構造CTipDlg類的對象tipDlg,並通過語句tipDlg.DoModal();彈出對話框,最後判斷DoModal()函數的返回值是IDOK還是IDCANCEL來確定是否繼續進行計算。OnBnClickedAddButton()函數修改後如下:
C++代碼
- void CAdditionDlg::OnBnClickedAddButton()
- {
- // TODO: Add your control notification handler code here
- INT_PTR nRes; // 用於保存DoModal函數的返回值
- CTipDlg tipDlg; // 構造對話框類CTipDlg的實例
- nRes = tipDlg.DoModal(); // 彈出對話框
- if (IDCANCEL == nRes) // 判斷對話框退出後返回值是否爲IDCANCEL,如果是則return,否則繼續向下執行
- return;
- // 將各控件中的數據保存到相應的變量
- UpdateData(TRUE);
- // 將被加數和加數的加和賦值給m_editSum
- m_editSum = m_editSummand + m_editAddend;
- // 根據各變量的值更新相應的控件。和的編輯框會顯示m_editSum的值
- UpdateData(FALSE);
- }
5.測試。編譯運行程序後,在對話框上輸入被加數和加數,點“計算”,彈出提示對話框詢問是否進行計算,如果選擇“確定”,則提示對話框退出,並在主對話框上顯示被加數和加數的和,而如果選擇“取消”,則提示對話框也會退出,但主對話框顯示的和不變,即沒有進行加法計算。
**********************************************************************************************************************************************************************
本節接着講另一種對話框--非模態對話框的創建及顯示。
雞啄米已經說過,非模態對話框顯示後,程序其他窗口仍能正常運行,可以響應用戶輸入,還可以相互切換。雞啄米會將上一講中創建的Tip模態對話框改爲非模態對話框,讓大家看下效果。
實際上,模態對話框和非模態對話框在創建對話框資源和生成對話框類上是沒有區別的,所以上一講中創建的IDD_TIP_DIALOG對話框資源和CTipDlg類都不需要修改。
創建及顯示非模態對話框的步驟
需要修改的是,對話框類實例的創建和顯示,也就是之前在CAdditionDlg::OnBnClickedAddButton()函數體中添加的對話框顯示代碼。下面是具體步驟:
1.在AdditionDlg.h中包含CTipDlg頭文件並定義CTipDlg類型的指針成員變量。詳細操作方法是,在AdditionDlg.cpp中刪除之前添加的#include "TipDlg.h",而在AdditionDlg.h中添加#include "TipDlg.h",這是因爲我們需要在AdditionDlg.h中定義CTipDlg類型的指針變量,所以要先包含它的頭文件;然後在AdditionDlg.h中爲CAdditionDlg類添加private成員變量CTipDlg *m_pTipDlg;。
2.在CAdditionDlg類的構造函數中初始化成員變量m_pTipDlg。如果cpp文件中函數太多,我們可以在Class View上半個視圖中找到CAdditionDlg類,再在下半個視圖中找到其構造函數雙擊,中間客戶區域即可馬上切到構造函數的實現處。在構造函數體中添加m_pTipDlg = NULL;,這是個好習慣,雞啄米在C++編程入門系列的指針的賦值和指針運算中說到過,在任何指針變量使用前都初始化,可以避免因誤訪問重要內存地址而破壞此地址的數據。
3.將上一講中添加的模態對話框顯示代碼註釋或刪除掉,添加非模態對話框的創建和顯示代碼。VC++中註釋單行代碼使用“//”,註釋多行代碼可以在需註釋的代碼開始處添加“/*”,結束處添加“*/”。修改後的CAdditionDlg::OnBnClickedAddButton()函數如下:
C++代碼
- void CAdditionDlg::OnBnClickedAddButton()
- {
- // TODO: Add your control notification handler code here
- /*INT_PTR nRes; // 用於保存DoModal函數的返回值
- CTipDlg tipDlg; // 構造對話框類CTipDlg的實例
- nRes = tipDlg.DoModal(); // 彈出對話框
- if (IDCANCEL == nRes) // 判斷對話框退出後返回值是否爲IDCANCEL,如果是則return,否則繼續向下執行
- return;*/
- // 如果指針變量m_pTipDlg的值爲NULL,則對話框還未創建,需要動態創建
- if (NULL == m_pTipDlg)
- {
- // 創建非模態對話框實例
- m_pTipDlg = new CTipDlg();
- m_pTipDlg->Create(IDD_TIP_DIALOG, this);
- }
- // 顯示非模態對話框
- m_pTipDlg->ShowWindow(SW_SHOW);
- // 將各控件中的數據保存到相應的變量
- UpdateData(TRUE);
- // 將被加數和加數的加和賦值給m_editSum
- m_editSum = m_editSummand + m_editAddend;
- // 根據各變量的值更新相應的控件。和的編輯框會顯示m_editSum的值
- UpdateData(FALSE);
- }
4.因爲此非模態對話框實例是動態創建的,所以需要手動刪除此動態對象來銷燬對話框。我們在CAdditionDlg類的析構函數中添加刪除代碼,但是MFC並沒有自動給出析構函數,這時需要我們手動添加,在對話框對象析構時就會調用我們自定義的析構函數了。在AdditionDlg.h文件中爲CAdditionDlg添加析構函數聲明:~CAdditionDlg();,然後在AdditionDlg.cpp文件中添加析構函數的實現,函數體如下:
C++代碼
- CAdditionDlg::~CAdditionDlg()
- {
- // 如果非模態對話框已經創建則刪除它
- if (NULL != m_pTipDlg)
- {
- // 刪除非模態對話框對象
- delete m_pTipDlg;
- }
- }
這樣,非模態對話框創建和顯示的代碼就添加修改完了。讓我們運行下看看效果吧。
在加法計算器對話框上輸入被加數和加數,然後點“計算”按鈕,依然像上節一樣彈出了提示對話框,但是先不要關閉它,你可以拖動它後面的加法計算器對話框試試,我們發現加法計算器對話框竟然可以拖動了,而且“和”編輯框裏已經顯示了運算結果,這表明提示對話框顯示以後還沒有關閉,OnBnClickedAddButton() 就繼續向下執行了,不僅如此,加法計算器的每個編輯框還都可以響應輸入。
這只是一個簡單的例子,非模態對話框的用處有很多,以後大家在軟件開發中會用到。
本節教程就到這裏了,相信大家對對話框的使用更上了一個臺階了,在不同的情況下可以選擇使用模態對話框和非模態對話框了。雞啄米歡迎大家留言討論。
***************************************************************************************************************************************************************************
前面講了模態對話框和非模態對話框,本節開始雞啄米講一種特殊的對話框--屬性頁對話框。另外,本套教程所講大部分對VC++各個版本均可適用或者稍作修改即可,但考慮到終究還是基於VS2010版本的,所以將《VC++/MFC編程入門》改爲《VS2010/MFC編程入門》。
屬性頁對話框的分類
屬性頁對話框想必大家並不陌生,XP系統中桌面右鍵點屬性,彈出的就是屬性頁對話框,它通過標籤切換各個頁面。另外,我們在創建MFC工程時使用的嚮導對話框也屬於屬性頁對話框,它通過點擊“Next”等按鈕來切換頁面。
屬性頁對話框就是包含一般屬性頁對話框和嚮導對話框兩類。它將多個對話框集成於一身,通過標籤或按鈕來切換頁面。
屬性頁對話框相關類
我們使用屬性頁對話框時,用到的類主要有兩個:CPropertyPage類和CPropertySheet類。
1.CPropertyPage類
CPropertyPage類繼承自CDialog類,它被用於處理某單個的屬性頁,所以要爲每個屬性頁都創建一個繼承自CPropertyPage的子類。大家可以在VS2010的MSDN中查找CPropertyPage類以及它的成員的詳細說明。下面雞啄米就爲大家講解MSDN中列出的CPropertyPage類的部分主要成員函數。
(1)構造函數
這裏講三個CProperty類的構造函數,函數原型爲:
CPropertyPage( );
explicit CPropertyPage(
UINT nIDTemplate,
UINT nIDCaption = 0,
DWORD dwSize = sizeof(PROPSHEETPAGE)
);
explicit CPropertyPage(
LPCTSTR lpszTemplateName,
UINT nIDCaption = 0,
DWORD dwSize = sizeof(PROPSHEETPAGE)
);
第一個是沒有任何參數的構造函數。
第二個構造函數中,參數nIDTemplate是屬性頁的對話框資源ID,參數nIDCaption是屬性頁對話框選項卡的標題所用字符串資源的ID,若設爲0,則選項卡標題就使用該屬性頁的對話框資源的標題。
第三個構造函數中,參數lpszTemplateName爲屬性頁的對話框資源的名稱字符串,不能爲NULL。參數nIDCaption同上。
(2)CancelToClose()函數
在模態屬性頁對話框的屬性頁進行了某不可恢復的操作後,使用CancelToClose()函數將“OK”按鈕改爲“Close”按鈕,並禁用“Cancel”按鈕。函數原型爲:
void CancelToClose( );
(3)SetModified()函數
調用此函數可激活或禁用“Apply”按鈕,函數原型爲:
void SetModified(BOOL bChanged = TRUE);
(4)可重載函數
CPropertyPage類提供了一些消息處理函數,來響應屬性頁對話框的各種消息。我們重載這些消息處理函數,就可以自定義對屬性頁對話框操作的處理。可重載的消息處理函數包括:
OnApply:處理屬性頁的“Apply”按鈕被單擊的消息
OnCancel:處理屬性頁的“Cancel”按鈕被單擊的消息
OnKillActive:處理屬性頁當前活動狀態被切換的消息,常用於數據驗證
OnOK:處理屬性頁的“OK”按鈕、“Apply”按鈕或者“Close”按鈕被單擊的消息
OnQueryCancel:處理屬性頁的“Cancel”按鈕被單擊前發出的消息
OnReset:處理屬性頁的“Reset”按鈕被單擊的消息
OnSetActive:處理屬性頁被切換爲當前活動頁的消息
OnWizardBack:處理屬性頁的“Back”按鈕被單擊的消息,僅在嚮導對話框中有效
OnWizardFinish:處理屬性頁的“Finish”按鈕被單擊的消息,僅在嚮導對話框中有效
OnWizardNext:處理屬性頁的“Next”按鈕被單擊的消息,僅在嚮導對話框中有效
2.CPropertySheet類
CPropertySheet類繼承自CWnd類,它是屬性表類,負責加載、打開或刪除屬性頁,並可以在屬性頁對話框中切換屬性頁。它跟對話框類似,也有模態和非模態兩種。下面雞啄米就講解CPropertySheet類的部分成員函數。
(1)構造函數
這裏依然列出CPropertySheet類的三個構造函數:
CPropertySheet( );
explicit CPropertySheet(
UINT nIDCaption,
CWnd* pParentWnd = NULL,
UINT iSelectPage = 0
);
explicit CPropertySheet(
LPCTSTR pszCaption,
CWnd* pParentWnd = NULL,
UINT iSelectPage = 0
);
參數nIDCaption:標題的字符串資源的ID。
參數pParentWnd:屬性頁對話框的父窗口,若設爲NULL,則父窗口爲應用程序的主窗口。
參數iSelectPage:初始狀態時,活動屬性頁的索引,默認爲第一個添加到屬性表的屬性頁。
參數pszCaption:標題字符串。
(2)GetActiveIndex()函數
獲取當前活動屬性頁的索引。函數原型爲:
int GetActiveIndex( ) const;
返回值:當前活動屬性頁的索引。
(3)GetActivePage()函數
獲取當前活動屬性頁對象。函數原型爲:
CPropertyPage* GetActivePage( ) const;
返回值:當前活動屬性頁對象的指針。
(4)GetPage()函數
獲取某個屬性頁對象。函數原型爲:
CPropertyPage* GetPage(int nPage) const;
參數nPage:目標屬性頁的索引。
返回值:目標屬性頁對象的指針。
(5)GetPageCount()函數
獲取屬性頁的數量。函數原型爲:
int GetPageCount( ) const;
返回值:屬性頁的數量。
(6)GetPageIndex()函數
獲取某屬性頁在屬性頁對話框中的索引。函數原型爲:
int GetPageIndex(CPropertyPage* pPage);
參數pPage:要獲取索引的屬性頁對象的指針。
返回值:屬性頁對象在屬性頁對話框中的索引。
(7)SetActivePage()函數
設置某個屬性頁爲活動屬性頁。函數原型爲:
BOOL SetActivePage(
int nPage
);
BOOL SetActivePage(
CPropertyPage* pPage
);
參數nPage:要設置爲活動屬性頁的索引。
參數pPage:要設置爲活動屬性頁的對象指針。
(8)SetWizardButtons()函數
在嚮導對話框上啓用或禁用Back、Next或Finish按鈕,應在調用DoModal之前調用此函數。函數原型爲:
void SetWizardButtons(
DWORD dwFlags
);
參數dwFlags:設置嚮導按鈕的外觀和功能屬性。可以是以下值的組合:
PSWIZB_BACK 啓用“Back”按鈕,如果不包含此值則禁用“Back”按鈕。
PSWIZB_NEXT 啓用“Next”按鈕,如果不包含此值則禁用“Next”按鈕。
PSWIZB_FINISH 啓用“Finish”按鈕。
PSWIZB_DISABLEDFINISH 顯示禁用的“Finish”按鈕。
(9)SetWizardMode()函數
設置屬性頁對話框爲嚮導對話框模式,應在調用DoModal之前調用此函數。函數原型爲:
void SetWizardMode( );
(10)SetTitle()函數
設置屬性對話框的標題。函數原型爲:
void SetTitle(
LPCTSTR lpszText,
UINT nStyle = 0
);
參數lpszText:標題字符串。
參數nStyle:指定屬性表標題的風格。應當爲0或PSH_PROPTITLE。如果設爲PSH_PROPTITLE,則單詞“Properties”會出現在指定標題之後。例如,SetTitle("Simple",PSH_PROPTITLE)這種調用會使得屬性表標題爲“Simple Properties”。
(11)AddPage()函數
爲屬性對話框添加新的屬性頁。函數原型爲:
void AddPage(
CPropertyPage *pPage
);
參數pPage:要添加的新的屬性頁的對象指針。
(12)PressButton()函數
模擬按下某指定的按鈕。函數原型爲:
void PressButton(
int nButton
);
參數nButton:要模擬按下的按鈕,它可以是下列值之一:
PSBTN_BACK 選擇“Back”按鈕。
PSBTN_NEXT 選擇“Next”按鈕。
PSBTN_FINISH 選擇“Finish”按鈕。
PSBTN_OK 選擇“OK”按鈕。
PSBTN_APPLYNOW 選擇“Apply”按鈕。
PSBTN_CANCEL 選擇“Cancel”按鈕。
PSBTN_HELP 選擇“幫助”按鈕。
(13)RemovePage()函數
刪除某屬性頁。函數原型爲:
void RemovePage(
CPropertyPage *pPage
);
void RemovePage(
int nPage
);
參數pPage:要刪除的屬性頁的對象指針。
參數nPage:要刪除的屬性頁的索引。
屬性對話框和相關的兩個類雞啄米就先介紹到這,主要是爲後面使用屬性頁對話框做準備。有問題可以到雞啄米博客交流。謝謝。
*************************************************************************************************************************************************************************
上一講雞啄米講了屬性頁對話框和相關的兩個類CPropertyPage類和CPropertySheet類,對使用屬性頁對話框做準備。本節將爲大家演示如何創建嚮導對話框。
仍然以前面的“加法計算器”的例子爲基礎,在其中加入嚮導對話框,我們可以用它來說明加法計算器的使用方法,一步一步引導用戶操作,這也是比較常見的用法。
加法計算器使用時大概可以分爲三步:輸入被加數、輸入加數、點“計算”按鈕。
雞啄米就詳細說明嚮導對話框的創建步驟:
1.創建屬性頁對話框資源
根據創建對話框模板和修改對話框屬性中所講方法,在“Resource View”的Dialog”節點上點右鍵,然後在右鍵菜單中選擇“Insert Dialog”創建第一個對話框模板,對話框的ID屬性設置爲IDD_SUMMAND_PAGE,Caption屬性改爲“被加數頁”,Style屬性在下拉列表中選擇“Child”,Border屬性在下拉列表中選擇“Thin”。
刪除“OK”和“Cancel”按鈕,再按照爲對話框添加控件中所講方法,添加一個靜態文本框,並修改靜態文本框的Caption屬性爲“請先輸入double型被加數”。
按照上述步驟,繼續添加第二個和第三個對話框資源。第二個對話框模板的ID設爲IDD_ADDEND_PAGE,Caption屬性改爲“加數頁”,也添加一個靜態文本框,Caption設爲“請繼續輸入double型加數”,其他屬性同第一個對話框。第三個對話框模板的ID設爲IDD_ADD_PAGE,Caption屬性改爲“計算頁”,添加靜態文本框的Caption屬性改爲“最後請按下“計算”按鈕”,其他屬性也第一個對話框一樣。
2.創建屬性頁類
按照創建對話框類和添加控件變量中的方法,在第一個對話框模板上點右鍵,在右鍵菜單中選擇“Add Class”,彈出類嚮導對話框,在“Class name”編輯框中輸入類名“CSummandPage”,與之前不同的是,因爲屬性頁類都應繼承於CPropertyPage類,所以要修改下面“Base class”的選項,在下拉列表中選擇“CPropertyPage”。
因爲是第一個屬性頁,所以它應該有一個“下一步”按鈕,在哪裏添加呢?上一講CPropertyPage類的可重載函數中提到,OnSetActive函數用於處理屬性頁被切換爲當前活動頁的消息,所以我們可以在OnSetActive函數中進行相關設置。
那怎樣重載OnSetActive函數呢?我們可以在“Class View”中找到“CSummandPage”節點,點右鍵彈出右鍵菜單,選擇“Properties”,然後VS2010右側面板上會顯示對話框的屬性列表,屬性列表的工具欄上有個tip信息爲“Overrides”的按鈕,按下它,下方列表中就列出了重載函數,找到“OnSetActive”,點其右側空白列表項出現向下箭頭,再點箭頭就在下面出現了“<Add>OnSetActive”的選項,選擇它就會自動在CSummandPage類中添加函數OnSetActive。
我們只需在OnSetActive函數體中添加相關代碼就可以實現添加“下一步”按鈕的效果了。新的函數體如下:
C++代碼
- BOOL CSummandPage::OnSetActive()
- {
- // TODO: Add your specialized code here and/or call the base class
- // 獲得父窗口,即屬性表CPropertySheet類
- CPropertySheet* psheet = (CPropertySheet*) GetParent();
- // 設置屬性表只有“下一步”按鈕
- psheet->SetWizardButtons(PSWIZB_NEXT);
- return CPropertyPage::OnSetActive();
- }
爲第二個和第三個對話框也分別添加屬性頁類CAddendPage和CAddPage。但第二個對話框的屬性頁不需要重載OnSetActive函數。第三個對話框是最後一個對話框,所以不需要“下一步”按鈕,而應該換成“完成”按鈕,所以也需要重載OnSetActive函數設置“完成”按鈕。重載後的OnSetActive如下:
C++代碼
- BOOL CAddPage::OnSetActive()
- {
- // TODO: Add your specialized code here and/or call the base class
- // 獲得父窗口,即屬性表CPropertySheet類
- CPropertySheet* psheet = (CPropertySheet*) GetParent();
- //設置屬性表只有“完成”按鈕
- psheet->SetFinishText(_T("完成"));
- return CPropertyPage::OnSetActive();
- }
上面的代碼段中,字符串“完成”前加了個_T,這是因爲本工程創建的時候用的默認的Unicode字符集,而如果“完成”前不加_T就是ASCII字符串。_T實際上是一個宏,工程的字符集選擇爲Unicode時字符串就轉爲Unicode字符串,選擇爲Muli-Byte時就轉爲ASCII字符串。我們可以在Solution Explorer的Addition根節點上點右鍵,在右鍵菜單上選擇“Properties”,彈出工程的屬性對話框,Configuration Properties->General右側列表中的Character Set就顯示選擇的字符集。
那點了第三個屬性頁上的“完成”按鈕我們想進行某些處理的話,就重載OnWizardFinish函數,方法同OnSetActive函數。重載後的OnWizardFinish函數如下:
C++代碼
- BOOL CAddPage::OnWizardFinish()
- {
- // TODO: Add your specialized code here and/or call the base class
- // 提示嚮導完成
- MessageBox(_T("使用說明嚮導已閱讀完!"));
- return CPropertyPage::OnWizardFinish();
- }
3.創建屬性表類
屬性頁資源和屬性頁類創建完以後,還不能生成嚮導對話框,我們還需要一個屬性表類,來容納這些屬性頁。
在Solution Explorer視圖中的根節點“Addition”上點右鍵,在右鍵菜單中選擇Add->Class,彈出“Add Class”對話框,然後在中間區域中選擇“MFC Class”,點“Add”按鈕,彈出另一個類嚮導對話框,設置Class name爲CAddSheet,Base class選擇“CPropertySheet”,點“Finish”按鈕,這樣就屬性表類就建好了。
接下來,在新生成的AddSheet.h中包含三個屬性頁類的頭文件:
#include "SummandPage.h"
#include "AddendPage.h"
#include "AddPage.h"
之後在AddSheet.h中添加private變量:
CSummandPage m_summandPage;C
CAddendPage m_addendPage;
CAddPage m_addPage;
然後在AddSheet.cpp文件中修改CAddSheet的兩個構造函數爲:
C++代碼
- CAddSheet::CAddSheet(UINT nIDCaption, CWnd* pParentWnd, UINT iSelectPage)
- :CPropertySheet(nIDCaption, pParentWnd, iSelectPage)
- {
- // 添加三個屬性頁到屬性表
- AddPage(&m_summandPage);
- AddPage(&m_addendPage);
- AddPage(&m_addPage);
- }
- CAddSheet::CAddSheet(LPCTSTR pszCaption, CWnd* pParentWnd, UINT iSelectPage)
- :CPropertySheet(pszCaption, pParentWnd, iSelectPage)
- {
- // 添加三個屬性頁到屬性表
- AddPage(&m_summandPage);
- AddPage(&m_addendPage);
- AddPage(&m_addPage);
- }
4.顯示嚮導對話框
我們在加法計算器對話框上添加一個按鈕,點擊它就打開向導對話框。此按鈕的ID設爲IDC_INSTRUCT_BUTTON,Caption屬性設爲“使用說明”。
按照爲控件添加消息處理函數中所講方法,爲IDC_INSTRUCT_BUTTON按鈕在CAdditionDlg類中添加點擊消息的處理函數OnBnClickedInstructButton。然後在AdditionDlg.cpp文件中包含CAddSheet的頭文件:#include "AddSheet.h"。最後修改OnBnClickedInstructButton函數如下:
C++代碼
- void CAdditionDlg::OnBnClickedInstructButton()
- {
- // TODO: Add your control notification handler code here
- // 創建屬性表對象
- CAddSheet sheet(_T(""));
- // 設置屬性對話框爲嚮導對話框
- sheet.SetWizardMode();
- // 打開模態嚮導對話框
- sheet.DoModal();
- }
到此,嚮導對話框就完整的創建完成了,並可以在加法計算器對話框上點“使用說明”按鈕顯示出來。我們來看看效果吧:
上圖只是被加數頁的效果,點其上“下一步”按鈕就可以繼續顯示後面的兩個頁面。
是不是嚮導對話框沒有以前想象的那般複雜了?大家可以發揮想象,進行更復雜的修改,實現更完善的功能。依然歡迎朋友們到雞啄米博客來交流學習。
**********************************************************************************************************************************************************************************
屬性頁對話框包括嚮導對話框和一般屬性頁對話框兩類,上一節雞啄米講了如何創建並顯示嚮導對話框,本節將繼續介紹一般屬性頁對話框的創建和顯示。
實際上,一般屬性頁對話框的創建和顯示過程和嚮導對話框是很類似的。雞啄米將上一節中的嚮導對話框進行少量修改,使其成爲一般屬性頁對話框。
一般屬性頁對話框的創建步驟:
1.創建屬性頁對話框資源
屬性頁對話框資源的創建方法同向導對話框是一樣的,上一講中的對話框資源不需進行任何修改。
2.創建屬性頁類
屬性頁類的創建和嚮導對話框的屬性頁類也基本一樣,只是一般屬性頁對話框中不需要“下一步”和“完成”等按鈕,所以上一講中屬性頁類的OnSetActive和OnWizardFinish等重載函數可以去掉。即CSummandPage類中的OnSetActive函數、CAddPage類中的OnSetActive函數和OnWizardFinish函數可以刪除或註釋掉。其他部分不需作任何修改。
3.創建屬性表類
創建屬性表類的過程同向導對話框屬性表類也是一樣的,所以上一講中的CAddSheet類不需修改。
4.顯示一般屬性頁對話框
上一講嚮導對話框的顯示是在OnBnClickedInstructButton函數中實現的,其中語句sheet.SetWizardMode();旨在設置屬性表爲嚮導對話框模式,所以顯示一般屬性頁對話框時不需調用SetWizardMode成員函數。另外,我們可以將屬性頁對話框的標題設爲“使用說明”,在構造屬性表對象時將此字符串作爲構造函數的參數傳入。OnBnClickedInstructButton函數修改如下:
C++代碼
- void CAdditionDlg::OnBnClickedInstructButton()
- {
- // TODO: Add your control notification handler code here
- // 創建屬性表對象
- CAddSheet sheet(_T("使用說明"));
- // 打開模態一般屬性頁對話框
- sheet.DoModal();
- }
這樣一般屬性頁對話框的創建和顯示就講完了,我們運行下程序,在結果對話框上點“使用說明”按鈕看看效果吧:
再總結下,一般屬性頁對話框和嚮導對話框的創建和顯示的不同包括,是否需要OnSetActive和OnWizardFinish等重載函數,是否需要調用屬性表類的SetWizardMode函數設置爲嚮導對話框模式。