因最近研究工作要用到MFC,故再次重溫了孫鑫老師的MFC對話框編程,因所用的編譯軟件為VS2008,與視頻中孫老師使用的VC++6.0有很大出入,造成很大不便,我通過各方查找,實現了VS2008相對應於VC++6.0的方法。現將對話框編程整個視頻的詳細內容分享如下,希望對有緣看到的朋友提供方便。筆者水平有限,難免有不足之處,歡迎批評指正。
一.准備工作
1.創建一個MFC Application.
File->New->Project->Visual C++->MFC->MFC Application.輸入項目名稱Menu后,點擊NEXT,Application type->Single document.建議取消勾選下方的Use Unicode libraries,不然有時候會出錯。然后直接點Finish。完成MFC程序創建過程。
2.新建對話框
點擊Resource View(資源視圖)->Dialog,右鍵添加資源->選擇對話框->新建,控件ID為IDD_DIALOG1。
我們會看到,程序剛建好時資源視圖對話框中已有IDD_ABOUTBOX,這個是版本信息提示對話框,程序中點擊幫助可查看。
點擊類視圖,對話框資源對應的類:CObject->CCmdTarget->CWnd->CDialog
一個概念:模態對話框和非模態對話框,模態對話框顯示時不能執行程序其它任務,而非模態對此無限制。
3.新建一個和對話框相關的類
左鍵資源視圖中剛創建的對話框資源,右鍵右面的對話框,Add Class->Class Name->CTestDlg,MFC中前綴C代表類。
這樣,我們就得到一個新類---CTestDlg,此類就是與剛創建的對話框相對應的,我們對對話框的操作可以在這里進行。
兩個重要函數:
構造函數:調用基類CDialog的構造函數,IDD為對話框資源的ID

CTestDlg::CTestDlg(CWnd* pParent /*=NULL*/) : CDialog(CTestDlg::IDD, pParent) { } CTestDlg::DoDataExchange(),此函數主要用來對話框數據交換和數據校驗 void CTestDlg::DoDataExchange(CDataExchange* pDX) { CDialog::DoDataExchange(pDX); }
4.創建對話框操作
在Menu菜單中增加菜單項”Dialog”,屬性設置為PopUp
在該菜單項添加消息響應函數:
Message Type:COMMAND,Class List:CmenuView
二.對話框操作
1.模態對話框與非模態對話框
(1)模態對話框
模態對話框的創建函數:CDialog::DoModal()
模態對話框的關閉函數:CDialog::EndDialog()
#注意:View類中並不知道CTestDlg類型,要包涵頭文件”TestDlg.h”,創建過程在View類的OnDialog()函數中。

void CmenuView::OnDialog() { // TODO: Add your command handler code here CTestDlg dlg; dlg.DoModal();//對話框出現就不能在操作其他,除非先關閉它 }
(2)非模態對話框
非模態對話框的創建函數:BOOL CDialog::Create(ID號,父窗口指針);
若父窗口指針設為NULL,對話框父窗口就被設置為主應用程序窗口(此應用中為框架窗口)。
CtestDlg dlg;
dlg.Create(IDD_DIALOG1,this);
#注意:
對於非模態對話框必須調用ShowWindow();而模態創建函數本身有顯示功能 dlg.ShowWindow(SW_SHOW);
非模態對話框不能是局部變量,而模態因為要暫停,程序停止執行,停留在其生命周期內,所以可以是局部變量
非模態也可以是局部變量兩種方案:
a.定義Dlg類型的成員變量
b.定義指針,在堆上分配內存
CTestDlg *pDlg=new CTestDlg();
Dlg->Create(IDD_DIALOG1,this);
pDlg->ShowWindow(SW_SHOW);
//這種方法不能解決內存釋放重利用問題,最好是添加成員變量,再用析構函數delete釋放
#注意:
對於模態對話框,點擊OK后窗口被銷毀,而非模板對話框沒被銷毀而是隱藏了。
非模態對話框點擊OK時,由基類中OnOK虛函數響應,只隱藏不銷毀。
好的方法是類中要覆蓋基類的OnOK()在其內部調用DestroyWindow()銷毀。
所以,之后示例中我們都使用模態對話框。
2.關於對話框的操作
(1)Task:在對話框上添加一個按鈕,點擊該按鈕再動態創建一個按鈕
VS2008切換到資源視圖,工具欄直接拖動添加名稱是Add的按鈕。為這個按鈕添加消息響應函數,按鈕的點擊屬於通告消息:
右鍵按鈕選擇“Add Event Handler”,MessageType:BN_CLICKED,ClassList:CTestDlg。
對CTestDlg類添加成員變量 private: CButton m_btn;

void CTestDlg::OnBnClickedBtnAdd() { // TODO: Add your control notification handler code here m_btn.Create(“WW”,BS_DEFPUSHBUTTON|WS_VISIBLE|WS_CHILD, CRect(0,0,100,100),this,123); )//MSDN中查找Button類型,加了WS_VISIBLE就不用調用ShowWindow了, //否則必須調用,第三個參數是Button空間矩形區域大小,四是控件父窗口, //五是ID號 }
#注意:
我們發現當我們兩次點擊Add按鈕時,非法操作。因為m_btn重復創建窗口,所以錯誤。
我們要預防重復創建有兩個方法:
方法一,判斷當前button對象如果已經和一個窗口關聯在一起了,就銷毀窗口,再一次點擊再創建。
增加類CtestDlg成員變量bool m_bIsCreate;這個變量還可以是局部變量,為了不讓每次調用都要初始化,把它設置成靜態局部變量。
方法二,凡是從CWnd繼承的類都有一個m_hWnd的成員變量,保存了和C++對象相關的句柄。當對象和一個窗口向關聯時句柄就有值,否則句柄值為空。

if(!m_btn.m_hWnd) { m_btn.Create(L"WW",BS_DEFPUSHBUTTON|WS_VISIBLE|WS_CHILD, CRect(0,0,100,100),this,123); } else { m_btn.DestroyWindow(); }
(2)Task:點擊靜態文本框,讓其Caption變為中文
首先,我們要熟悉相關控件。控件大小對齊等操作使用上面的格式按鈕,格式快捷按鈕和Format中的菜單項一樣,注意Format菜單只有在對話框編輯時才有。
靜態文本框控件:用來標記,起注釋作用(放同一個空間,可以點擊那個控件按CTRL鍵拖動),可以在屬性頁更改Caption。所有的靜態文本框ID號都一樣。一般不用於響應消息
編輯框控件:用於輸入文本內容。屬性頁沒有Caption
更改靜態文本框ID號為IDC_NUMBER1,增加消息響應函數。
靜態文本框實際上也是窗口,獲得窗口內容用函數CWnd::GetWindowText()
獲得指定控件的指針或者子窗口指針函數CWnd::GetDlgItem()。

void CTestDlg::OnStnClickedNumber1() { // TODO: Add your control notification handler code here CString str;//接收靜態文本框文本 If(GetDlgItem(IDC_NUMBER1)->GetWindowText(str),str==”Number1:”)//整個逗號表達式的值是第二個表達式的值 { GetDlgItem(IDC_NUMBER1)->SetWindowText(“數值1:”); } else { GetDlgItem(IDC_NUMBER1)->SetWindowText(“Number1:”); } }
發現不能實現功能,排錯,先看函數調用,沒錯,再看字符是否寫對。我們可以直接復制靜態文本框的Caption,這樣可以保證正確。排錯后發現還是不行。
答案:靜態文本框默認不能接受響應消息,我們必須把屬性內的Notify改為TRUE才行。
(3)Task:在第一個、第二個編輯框分別輸入數值,點擊Add按鈕讓第三個顯示兩者之和
訪問控件方法一:

void CTestDlg::OnBnClickedBtnAdd() { int num1,num2,num3; char ch1[10],ch2[10],ch3[10]; GetDlgItem(IDC_EDIT1)->GetWindowText((LPTSTR)ch1,10); GetDlgItem(IDC_EDIT2)->GetWindowText((LPTSTR)ch2,10); //將字符轉換為數值 num1=::atoi(ch1);//ch1字符串中的內容必須是數值 num2=::atoi(ch2); num3=num1+num2; ::itoa(num3,ch3,10); GetDlgItem(IDC_EDIT3)->SetWindowText((LPTSTR)ch3); }
訪問控件方法二:

GetDlgItemText()相當於GetDlgItem()+GetWindowText() void CTestDlg::OnBnClickedBtnAdd() { int num1,num2,num3; char ch1[10],ch2[10],ch3[10]; GetDlgItemText(IDC_EDIT1,(LPTSTR)ch1,10); GetDlgItemText(IDC_EDIT2,(LPTSTR)ch2,10); num1=::atoi(ch1);//ch1字符串中的內容必須是數值 num2=::atoi(ch2); num3=num1+num2; ::itoa(num3,ch3,10); SetDlgItemText(IDC_EDIT3,(LPTSTR)ch3); }
訪問控件方法三:
CWnd::GetDlgItemInt()獲取一個控件的文本轉換為整型返回
CWnd::GetDlgItemInt(Int nID,BOOL* lpTrans=NULL,BOOL bSigned=TRUE);
參數一控件ID,參數二錯誤標示,參數三符號標示,若為真,要看正負號
CWnd::SetDlgItemInt(Int nID,UINT nValue,BOOL bSigned=TRUE);
參數一控件ID,參數二欲設置的值,參數三符號標示,若為真,要看正負號
這種方法推薦采用。

void CTestDlg::OnBnClickedBtnAdd() { int num1,num2,num3; num1=GetDlgItemInt(IDC_EDIT1); num2=GetDlgItemInt(IDC_EDIT2); num3=num1+num2; SetDlgItemInt(IDC_EDIT3,num3); }
訪問控件方法四(最簡單的一種):添加數據變量
將對話框上的三個編輯框控件分別關聯三個成員變量,我們可用成員變量操縱編輯框變量。
給控件添加成員變量方法:右鍵控件->Add variable,我們還可以為值設定數值范圍,超過的報錯提示系統已編寫好。添加控件如下圖:
添加后,在CTestDlg::DoDataExchange()(數據交換函數,此函數不能直接調用,必須調用UpdataData())會有如下函數:
DDX_Text(pDX, IDC_EDIT1, m_num1);
DDX_Text()用來將制定控件與成員變量相關聯
進行控件與變量的數據交換是DoDataExchange()進行的,但是我們不能直接調用此函數,而是通過調用UpdateData()來實現,參數TRUE表明從控件當中取回值,反之,將成員變量的值賦予控件;為了從編輯框中獲得輸入值。
將m_num3的值放入編輯框中再次調用UpdateData(),進行初始化。

void CTestDlg::OnBnClickedBtnAdd() { UpdateData(); m_num3=m_num1+m_num2; UpdateData(false); }
訪問控件方法五:添加控件變量

void CTestDlg::OnBnClickedBtnAdd() { int num1,num2,num3; char ch1[10],ch2[10],ch3[10]; m_edit1.GetWindowText((LPTSTR)ch1,10); m_edit2.GetWindowText((LPTSTR)ch2,10); num1=::atoi(ch1);//ch1字符串中的內容必須是數值 num2=::atoi(ch2); num3=num1+num2; ::itoa(num3,ch3,10); m_edit3.SetWindowText((LPTSTR)ch3); } //添加控件變量過程和添加數值變量基本一致,把圖2category改為control即可。
方法六:通過消息機制獲取文本WM_GETTEXT

void CTestDlg::OnBnClickedBtnAdd() { int num1,num2,num3; char ch1[10],ch2[10],ch3[10]; //發送消息的四種û¡式 //形式.表明調用的是Win32API函數,而不是類的成員函數 //參數一先獲取編輯框指針再指向其句柄,參數二消息類型WM_GETTEXT, //參數三指示要拷貝的字符數,參數四表示文本buffer。 ::SendMessage(GetDlgItem(IDC->EDIT1)->w_hWnd,WM_GETTEXT,10,(LPARAM)ch1); //形式.因編輯框已關聯控件變量故可直接使用控件變量 ::SendMessage(m_edit1.m_hWnd,WM_GETTEXT,10,(LPARAM)ch1); //形式.不使用Win32API函數,用CWnd成員函數SendMessage()發送消息 GetDlgItem(IDC_EDIT1)->SendMessage(WM_GETTEXT,10,(LPARAM)ch1); //形式.使用控件變量的SendMessage()直接發áê消息 m_edit1.SendMessage(WM_GETTEXT,10,(LPARAM)ch1); m_edit2.SendMessage(WM_GETTEXT,10,(LPARAM)ch2); num1=::atoi(ch1);//ch1字符串中的內容必須是數值 num2=::atoi(ch2); num3=num1+num2; ::itoa(num3,ch3,10); m_edit3.SendMessage(WM_SETTEXT,0,(LPARAM)ch3); }
訪問控件方法七:直接給一個對話框的子控件發送消息

CWnd::SendDlgItemMessage()等價於先調用GetDlgMessage()再調用SendMessage()。 void CTestDlg::OnBnClickedBtnAdd() { int num1,num2,num3; char ch1[10],ch2[10],ch3[10]; SendDlgItemMessage(IDC_EDIT1,WM_GETTEXT,10,(LPARAM)ch1); SendDlgItemMessage(IDC_EDIT1,WM_GETTEXT,10,(LPARAM)ch1); num1=::atoi(ch1);//ch1字符串中的內容必須是數值 num2=::atoi(ch2); num3=num1+num2; ::itoa(num3,ch3,10); SendDlgItemMessage(IDC_EDIT3,WM_SETTEXT,0,(LPARAM)ch3); //獲取編輯框鼠標復選部分內容而非整個窗口內容EM_GETSEL,EM_SETSEL //對於EM_SETSEL消息,當范圍是0,-1時,全選對話框內容 SendDlgItemMessage(IDC_EDIT3,EM_SETSEL,1,3); //將編輯框3設置為焦點所在 m_edit3.SetFocus(); }
##對話框控件訪問七種方式總結:
1.GetDlgItem()//獲取子控件指針->Get(Set)WindowText()
2.GetDlgItemText()/SetDlgItemText()
3.GetDlgItemInt()/SetDlgItemInt()
4.將控件和整型變量相關聯
5.將控件和控件變量相關聯
6.SendMessage()
7.SendDlgItemMessage()
其中1和4最常用,當然要根據實際需求
用的最少的是發送消息6和7,SDK經常用發送消息
(3)Task:對話框的收縮與擴展功能,點擊Shrink按鈕的時候切除對話框一部分,之后,將文本內容變為Extend,點Extend時還原對話框
增加Button控件,編輯相應的消息相應函數。對按鈕的相應函數,只需在資源列表雙擊按鈕圖標!

void CTestDlg::OnBnClickedButton2() { // TODO: Add your control notification handler code here CString str; if(GetDlgItemText(IDC_BUTTON2,str),str==”Shrink<<”) { SetDlgItemText(IDC_BUTTON2,”Extend>>”); } else { SetDlgItemText(IDC_BUTTON2,”Shrink<<”); } }
(4)Task:點擊Shirk,分隔符以下的收縮,點擊Extend恢復。
增加分隔符用於表示要切割的部分:
資源視圖,分隔符用圖像控件代替,查看屬性:分隔符ID為IDC_STATIC,說明不能接受相應消息。改為IDC_SEPARATOR,SunKen屬性改為TRUE。
思路:對話框尺寸、原點應保留。右下角橫坐標不變,縱坐標改變

static CRect rectLarge;//為避免重復定義,聲明為static static CRect rectSmall; //判斷對話框原始尺寸是否是沒被賦值,用CRect成員函數 //IsRectNull()看CRect四個坐標值是否都為空,IsRectEmpty()看CRect區域是否為空 if(rectLarge.IsRectNull()) { CRect rectSeparator; //獲取原始窗口矩形區域尺寸 GetWindowRect(&rectLarge); //獲取切割后剩余矩形區域尺寸大小,左上角頂部左頂點不變,變化的只是右下角頂點的縱坐標 GetDlgItem(IDC_SEPARATOR)->GetWindowRect(&rectSeparator); rectSmall.left=rectLarge.left; rectSmall.top=rectLarge.top; rectSmall.right=rectLarge.right; rectSmall.bottom=rectSeparator.bottom; } //伸縮擴展功能CWnd::SetWindowPos()設置頂窗口位置與ZOrder //SetWindowPos(const CWnd* pWndInsertAfter,int x,int y,int cx,int cy,UINT nFlags);詳見MSDN if(str==L"Shrink<<") { SetWindowPos(NULL,0,0,rectSmall.Width(),rectSmall.Height(), SWP_NOMOVE|SWP_NOZORDER); } else { SetWindowPos(NULL,0,0,rectLarge.Width(),rectLarge.Height(), SWP_NOMOVE|SWP_NOZORDER); } //隱藏分割線將控件的visible屬性設為FALSE
(5)Task:編輯框1輸入點擊回車,鼠標焦點轉向下一個控件
#發現一旦回車,對話框消失,因為OK鍵默認相應回車
a.將OK鍵的相應函數取消。雙擊資源視圖OK鍵進入響應函數,注釋其調用的OnOK();
b.SetWindowLong(HWND hWnd,int nIndex,LONG dwNewLong);改變指定窗口的屬性。參數1指定窗口,參數2指定改變類型,參數3指定新值
c.VC++6.0在CTestDlg類中添加WM_INITDIALOG消息,此消息是指在對話框都創建好且顯示前要發送的消息。VS2008在CTestDlg類中右鍵屬性窗口中的重寫圖標(最后一個)添加OnInitDialog消息映射。
此過程不能在對話框的ONCREATE函數中設置,因為我們是對編輯框的操作,在對話框ONCREATE函數中,編輯框還沒有產生。子控件創建過程在對話框的ONCREATE函數執行后和對話框顯示之前完成。

WNDPROC prevProc; //定義一個窗口過程類型,放在最前面 BOOL CTestDlg::OnInitDialog() { CDialog::OnInitDialog(); // TODO: Add extra initialization here prevProc=(WNDPROC)SetWindowLong(GetDlgItem(IDC_EDIT1)->m_hWnd,GWL_WNDPROC,(LONG)WinWuProc); return TRUE; // return TRUE unless you set the focus to a control // EXCEPTION: OCX Property Pages should return FALSE } //寫窗口過程函數: LRESULT CALLBACK WinWuProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam ) { //對於一個WM_CHAR消息,w_Param保存了字符的ASCII碼 //如果消息是WM_CHAR並且是回車,讓光標移到下一個編輯框窗口 if(uMsg==WM_CHAR&&wParam==0x0d) { //目前窗口過程函數是全局函數,所以不能用CWnd成員函數,只能用WIN32API函數(SDK平台函數) //獲取編輯框下一個窗口句柄GetNextWindow() //::SetFocus(::GetNextWindow(hwnd,GW_HWNDNEXT)); //另一個獲取窗口句柄函數 ::SetFocus(::GetWindow(hwnd,GW_HWNDNEXT)); //第三種獲取窗口句柄函數 ::SetFocus(::GetNextDlgTabItem(::GetParent(hwnd),hwnd,false)); return 1; } else//如果不是,返回給先前窗口過程函數 { return prevProc(hwnd,uMsg,wParam,lParam); } } //注意將編輯框一屬性MultiLine設為True;
Task(5)方法二(更方便)實現按回車,焦點依次下移
缺省按鈕的響應函數來響應回車鍵,在相應函數中將焦點依次向下傳遞
將編輯框1的multiLine屬性恢復為FALSE。
在OnOk()VS2008變為OnBnClickedOk()函數中完成

void CTestDlg::OnBnClickedOk() { // TODO: Add your control notification handler code here //GetDlgItem(IDC_EDIT1)->GetNextWindow()->SetFocus();只能到第二個 //GetFocus()->GetNextWindow()->SetFocus();//可實現但是最后一個會出錯 //GetFocus()->GetNextWindow(GW_HWNDNEXT)->SetFocus();//可實現但是最后一個會出錯 //GetNextDlgTabItem()此函數按照Tab_Stop屬性依次向下查找 //查看Tab Order,Format子菜單下Tab Order GetNextDlgTabItem(GetFocus())->SetFocus(); //OnOK(); }
#注意:
我們可以把Shrink<<按鈕設置為Default Button,這是回車時,由Shrink<<按鈕的相應函數進行相應。一般情況默認OnOk()響應回車,就算沒有OK按鈕。
我們可以自己添加OK按鈕,ID設置為IDOK而不是IDC_OK。
Task(6):逃跑按鈕
實現細節:在對話框上增加一個按鈕,我們點擊按鈕時,按鈕自動移動到其他位置
在這里,我們做一個簡化,巧妙地方法是設置兩個相同的按鈕,鼠標移動到其中一個就隱藏。
#注意:對話框Font屬性可修改字體
實現步驟:
1.添加兩個相同的按鈕控件IDC_BUTTON1和IDC_BUTTON2
2.增加新類:->project->Add Class->MFC類CWinWu,基類設置為CButton.
3.將兩個控件分別關聯成員變量m_btn1、m_btn2,variable設置為CWinWu.
4.在CMFC_Dialog2Dlg頭文件中包涵”WinWu.h”
5.為CWinWu類中添加OnMouseMove()消息處理函數,用這個函數來完成一個控件隱藏一個控件顯示的功能。
6.獲取控件指針:添加成員變量m_pBtn,類型CWinWu*
//m_btn1和m_btn2作為CWinWu的實例化對象內部都有m_pBtn這個指針變量。我們讓每一個對象的指針保留對方對象的首地址,當其中一個隱藏,我們就調用它的指針(指向另一個對象)的ShowWindow()。
7.交換地址放在OnInitDialog()函數內部,此函數用來響應WM_INITDIALOG消息的,該消息在對話框顯示之前發送。

BOOL CMFC_Dialog2Dlg::OnInitDialog() { CDialog::OnInitDialog(); m_btn1.m_pBtn=&m_btn2; m_btn2.m_pBtn=&m_btn1; return TRUE; // return TRUE unless you set the focus to a control }
8.隱藏自身按鈕

void CWinWu::OnMouseMove(UINT nFlags, CPoint point) { // TODO: Add your message handler code here and/or call default ShowWindow(SW_HIDE); m_pBtn->ShowWindow(SM_SHOW); CButton::OnMouseMove(nFlags, point); }
9.隱藏其中的一個按鈕:到資源視圖,選中其中一個屬性visible->false
---------------------
版權聲明:本文為CSDN博主「Bright_Geek」的原創文章,遵循CC 4.0 by-sa版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/bright_geek/article/details/40456279