MFC對話框編程詳細學習筆記


      因最近研究工作要用到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);

}
View Code

 

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();//對話框出現就不能在操作其他,除非先關閉它

}
View Code

 

(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號

}
View Code

 

#注意:

我們發現當我們兩次點擊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();

}
View Code

 

(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:”);

}

}
View Code

 

發現不能實現功能,排錯,先看函數調用,沒錯,再看字符是否寫對。我們可以直接復制靜態文本框的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);

}
View Code

 

訪問控件方法二:

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);

}
View Code

 

訪問控件方法三:

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);

}
View Code

 

訪問控件方法四(最簡單的一種):添加數據變量

將對話框上的三個編輯框控件分別關聯三個成員變量,我們可用成員變量操縱編輯框變量。

給控件添加成員變量方法:右鍵控件->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);

}
View Code

 

訪問控件方法五:添加控件變量

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即可。
View Code

 

方法六:通過消息機制獲取文本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);

}
View Code

 

訪問控件方法七:直接給一個對話框的子控件發送消息

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();

}
View Code

##對話框控件訪問七種方式總結:

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<<”);

}

}
View Code

(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
View Code

(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;
View Code

 

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();

}
View Code

 

#注意:

我們可以把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

}
View Code

 

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);

}
View Code

 

9.隱藏其中的一個按鈕:到資源視圖,選中其中一個屬性visible->false
---------------------
版權聲明:本文為CSDN博主「Bright_Geek」的原創文章,遵循CC 4.0 by-sa版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/bright_geek/article/details/40456279


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM