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

原文地址:https://www.cnblogs.com/blogpro/p/11340233.html