目录
一、对话款基本认识
二、对话框项目创建
三、控件操作
四、对话框创建和显示
模态对话框
非模态对话框
五、动态创建按钮
六、访问控件
控件添加控制变量
访问对话框
操作对话框
SendMessage()
七、对话框伸缩功能实现
八、对话框小项目-逃跑按钮
九、小项目
一、对话款基本认识
对话框是与用户进行交互的控件,如文件对话框,字体对话框,颜色对话框等,一般用于告示,提醒等。对话框就是一个窗口,它不仅可以接收消息,而且还可以被移动和关闭,甚至可以在它的客户区中进行绘图。相当于一个窗口,在它上面能够旋转各种标准控件和扩展控件。都是由CWnd类派生来。
Windows应用程序(win桌面应用程序)工作的基本流程是从用户那里得到数据,经过相应的处理之后,先把处理结果输出到屏幕,打印机或者其他输出设备。这就需要用到Windows应用程序的用户接口对话框。
常用对话框
这些控件也是对话框
二、对话框项目创建
创建MFC应用项目,创建基于对话框的程序类型
用户界面,按需选择
高级功能按需选择
生成的类:一个APP类,一个Dialog类
三、控件操作
WM系列消息是对话框接收处理,子控件的消息处理需要在消息映射中指明ID和控件事件
给对话框的控件添加消息处理的3种方式
- 双击对话框里面的控件就会在Dlg类消息映射表中添加消息映射
- 右击控件,选择类向导,命令一栏中,在里面选择控件的ID值,在选择对应的控件事件
- 右击对话框,选择属性,选择控件事件,选择要处理的消息
如何给对话框添加控件?VS中打开工具箱,直接选中拖曳即可
当控件的消息处理无法满足需求时,可以控件也是对话框,可以给控件,对话框添加类,指定ID,父窗口即可
资源添加一个新的对话框,在对话框中右击选择 添加类,会生成一个 .h .cpp文件
继承的基类选择,可以选CDialog,或者CDialogEx(继承自前者,提供了更加丰富的控件支持)
这个创建后,其构造函数如下:
ButtonText::ButtonText(CWnd* pParent /*=nullptr*/)
: 这是构造函数的定义,它属于名为ButtonText的类。构造函数的作用是在创建类的对象时进行初始化操作。:CDialogEx(IDD_DIALOG1, pParent)
: 这是构造函数的初始化列表(initializer list)。在这里,我们调用了基类(CDialogEx)的构造函数,并传入两个参数:对话框模板资源的ID(IDD_DIALOG1)和父窗口指针(pParent)。通过调用基类的构造函数,确保了从基类继承的成员变量得到了正确的初始化。
在这个构造函数中,传入的 pParent
参数指定了对话框的父窗口。父窗口是指对话框所属的上层窗口,通常用于确定对话框的显示位置和处理窗口间的关系。在这里,pParent
参数指定了对话框的父窗口,可以根据调用这个构造函数时传入的具体参数值来确定。在实际使用中,根据具体的情景,可以传入不同的父窗口指针,以确立对话框与哪个窗口有关联关系。
AddDialogText::AddDialogText(CWnd* pParent /*=nullptr*/): CDialogEx(IDD_DIALOG1, pParent)
{}
控件的类不想要了,怎么办?项目中删除即可,即可把类的引用修改回来,否则会造成程序连接时候报错。
四、对话框创建和显示
一个控件(对话框),使用消息映射来处理控件事件;一个控件(对话框),添加类,创建类并且show是不一样的。或者提供了更加丰富的功能,更重要的是可以绘制出来,与用户交互,而且拥有自己的消息循环。
创建一个对话框,并设置ID值,在属性一栏中,可以控制外观,位置,行为等,给该对话框添加类。
当点击第一个对话框确定按钮后,就会弹出一个新的对话框。
所以需要在第一个对话框的 .cpp 文件包含新对话框的类。
需要在第一个消息对话框中为Button(确定按钮)使用消息映射机制,
模态对话框
对话框,控件的本质都是对话框,创建他们需要先Create后Show
模态(Model)对话框:指当其显示时,程序会暂停执行,直到关闭这个模态对话框后,才能继续执行程序中其它任务。当一个模态对话框打开时,用户只能与该对话框进行交互,而其它用户界面对象接收不到输入信息。在其显示时,整个程序会暂停,直到关闭该对话框。
void CMFCApplication1Dlg::OnBnClickedOk()
{// 1.模态对话框使用CBingoDialog dlg;dlg.DoModal(); // 模态对话框,只能操作当前窗口,会阻塞消息循环
}
一但使用后该进程只能先等该对话框处理完后才能继续处理消息。
非模态对话框
对话框,控件的本质都是对话框,创建他们需要先Create后Show
非模态(Modeless)对话框:当其显示时,允许执行程序中其它任务,而不用关闭这个对话框。在MFC中,对资源的操作通常都是通过一个与资源相关的类来完成。对话框资源对应CDialog基类。
需要考虑一个问题,如果在函数内部实例化对话框,创建,绘制对话框,函数执行结束,对话框就会销毁。最好的处理办法就是:在第一个对话框类中,实例化对象,并在调用初始化函数时创建出来,在消息函数中绘制。
void CMFCApplication1Dlg::OnBnClickedOk()
{// 2.非模态对话框//需要作为MFCApplication1Dlg类的成员变量//原因:非模态对话框不会阻塞消息循环,一旦函数结束就会析构掉对象,造成程序崩溃// 窗口绘制之前还需要先创建,在这个类初始化时进行操作if (dlg.m_hWnd == NULL){dlg.Create(IDD_DLG_EDAOYUN, this);}dlg.ShowWindow(SW_SHOW);
}
五、动态创建按钮
动态的需求,需要多少个按钮在程序开始时时不确定的,只能在程序运行时创建
void CBingoDialog::OnBnClickedButton1()
{// TODO: 在此添加控件通知处理程序代码TRACE("%s(%d):%s\n", __FILE__, __LINE__, __FUNCTION__);if (m_button.m_hWnd == NULL){m_button.Create(_T("动态"), BS_DEFPUSHBUTTON | WS_VISIBLE | WS_CHILD,CRect(100, 100, 200, 150), this, 9999);}TCHAR buf[20] = _T("");::SendMessage(m_edit1.m_hWnd, WM_GETTEXT, 20, (LPARAM)buf);m_edit1.SendMessage(WM_SETTEXT, sizeof(buf), (LPARAM)buf);this->SendMessage(WM_GETTEXT, 20, (LPARAM)buf);
}
相关必要的解释:
- CButton类是继承自CWND类,m_hWnd就是该类的一个成员变量,记录了Windows系统创建该窗口后,这个窗口的句柄由 m_hWnd 来保存。说明控件(对话框)的本质就是窗口
BS_DEFPUSHBUTTON
:这是按钮的样式,表示默认的按钮外观。WS_VISIBLE | WS_CHILD
:这是窗口样式,表示按钮是可见的并且作为父窗口的子窗口存在。CRect(100, 100, 200, 150)
:这是按钮控件的位置和大小,使用矩形区域来表示,左上角坐标为 (100, 100),右下角坐标为 (200, 150)。this
:这个参数表示按钮控件的父窗口,也就是按钮所在的对话框9999
:这是按钮控件的标识符,用于在程序中唯一标识这个按钮控件。
父窗口,子窗口:当我们在对话框中创建一个按钮时,按钮控件会成为对话框的子窗口,并且对话框将成为该按钮的父窗口。这意味着按钮的显示、布局、事件处理等都是相对于对话框来进行的。同时,对话框也负责管理和维护这些子窗口,比如控制它们的显示和隐藏,处理它们的消息等。
-
父窗口:
- 父窗口通常是一个容器窗口,它可以包含其他窗口作为它的子窗口。
- 父窗口负责管理和布局它的子窗口,包括显示、隐藏、移动、调整大小等操作。
- 一个窗口可以同时是另一个窗口的子窗口和父窗口,这种多层次的父子关系可以形成窗口的层次结构。
-
子窗口:
- 子窗口是被包含在父窗口内的窗口,它的位置和尺寸相对于父窗口来确定。
- 子窗口可以是各种控件,比如按钮、文本框、列表框等,也可以是其它窗口。
- 子窗口的行为通常由其父窗口来管理和控制,父窗口处理子窗口的消息并进行相应的响应。
在 MFC 编程中,对话框类通常是一个典型的父窗口,它可以包含各种控件作为其子窗口,比如按钮、编辑框、组合框等。当你在对话框资源编辑器中放置控件时,这些控件就成为了对话框的子窗口。在对话框类的代码中,你可以通过控件的成员变量或者控件 ID 来操作和管理这些子窗口。
上述在按钮消息处理函数中,this指针指向的就是父窗口
六、访问控件
控件都提供一些方法用于访问,比如编辑框,提供了设置默认内容,获取编辑框输入内容给的方法
用编辑框为例,学习相关使用。
控件添加控制变量
右击控件,添加变量
在父类中,会增加以下内容:
int m_value1;
int m_value2;
int m_value3;
父类构造函数
CBingoDialog::CBingoDialog(CWnd* pParent /*=nullptr*/): CDialog(IDD_DLG_EDAOYUN, pParent), m_value1(0), m_value2(0), m_value3(0)
{}
CEdit m_edit1;
CEdit m_edit2;
CEdit m_edit3;
添加变量 值,控件都会在 DoDataExchange 中指定对话框类成员变量与控件之间的数据交换规则,以及进行数据验证。
void CBingoDialog::DoDataExchange(CDataExchange* pDX)
{CDialog::DoDataExchange(pDX);DDX_Text(pDX, IDC_EDIT1, m_value1);DDV_MinMaxInt(pDX, m_value1, -9999, 9999);DDX_Text(pDX, IDC_EDIT2, m_value2);DDV_MinMaxInt(pDX, m_value2, -9999, 9999);DDX_Text(pDX, IDC_EDIT3, m_value3);DDV_MinMaxInt(pDX, m_value3, -9999, 9999);DDX_Control(pDX, IDC_EDIT1, m_edit1);DDX_Control(pDX, IDC_EDIT2, m_edit2);DDX_Control(pDX, IDC_EDIT3, m_edit3);
}
DoDataExchange
是 MFC 对话框类中的一个重要成员函数,用于实现对话框和控件之间的数据交换。在该函数中,可以通过调用 DDX_
和 DDV_
系列的宏来指定对话框类成员变量与控件之间的数据交换规则,以及进行数据验证。
具体来说,DoDataExchange
函数通常被用来执行两个方面的工作:
-
数据传递(DataExchange):将对话框类中的成员变量的值与与之关联的控件进行双向的数据交换。这包括从控件更新数据到成员变量(如
DDX_Control
、DDX_Text
等),以及将成员变量的值反向填充到相关联的控件(如DDX_Control
、DDX_Text
等)。 -
数据验证(DataValidation):在数据交换过程中,执行数据验证操作,确保用户输入的数据符合预期的格式和范围。这包括使用
DDV_
系列的宏来对用户输入的数据进行验证,例如格式、范围等。
在你提供的代码中,DoDataExchange
函数拥有一个名为 pDX
的参数,这是数据交换指针,它在数据传递和数据验证过程中扮演着重要的角色。通过在 DoDataExchange
函数中处理 pDX
参数,可以实现对话框和控件之间的数据交换和验证。
在 MFC(Microsoft Foundation Classes)中,DDX_Text
是一个用于数据交换的宏,用于在对话框和控件之间进行文本数据的交换。
具体地说,DDX_Text
宏用于将文本控件(如编辑框)中的数据与对话框类中的成员变量相关联,或者将对话框类中的成员变量的值与文本控件相关联。
DDX_Text(pDX, nIDC, memberName);
其中:
pDX
是数据交换指针,负责在对话框和控件之间进行数据交换。nIDC
是控件的 ID,即要进行数据交换的控件的标识符。memberName
是对话框类中的成员变量,用于存储或显示控件中的数据。
在你提供的代码中,DDX_Text(pDX, IDC_EDIT1, m_value1)
的作用是将 ID 为 IDC_EDIT1
的编辑框中的文本与对话框类中名为 m_value1
的成员变量进行关联。这样,在对话框中修改编辑框的内容时,相关联的成员变量 m_value1
也会相应地更新,反之亦然。
通过使用 DDX_Text
宏,可以很方便地实现对话框中控件和对话框类成员变量之间的双向数据交换,这在 MFC 编程中是非常常见和有用的操作。
在 MFC(Microsoft Foundation Classes)中,DDV_MinMaxInt
是一个用于数据验证的宏,用于验证整数类型的数据是否在指定的最小值和最大值范围内。
具体地说,DDV_MinMaxInt
宏用于在对话框数据验证期间检查整数类型的数据,确保其值在指定的最小值和最大值之间。其语法如下:
DDV_MinMaxInt(pDX, value, minVal, maxVal);
其中:
pDX
是数据交换指针,负责数据验证。value
是要进行验证的整数值,通常是对话框类中的成员变量。minVal
是允许的最小值。maxVal
是允许的最大值。
在你提供的代码中,DDV_MinMaxInt(pDX, m_value1, -9999, 9999)
的作用是验证对话框类中名为 m_value1
的整数成员变量,确保其值在 -9999 到 9999 的范围内。如果用户输入的值不在这个范围内,MFC 将会显示相应的验证错误消息,并阻止用户继续进行操作,直到输入的值符合要求为止。
通过使用 DDV_MinMaxInt
宏,可以方便地实现对话框中整数类型数据的范围验证,确保用户输入的数据符合预期的范围要求。
DDX_Control
宏用于将对话框中的控件与对话框类中的成员变量进行关联,其作用是将控件的句柄(handle)与对话框类中的成员变量相关联,从而可以通过成员变量来操作控件。
具体来说,DDX_Control
宏的语法如下:
DDX_Control(pDX, nIDC, m_memberName);
其中:
pDX
是数据交换指针,负责在对话框和控件之间进行数据交换。nIDC
是控件的 ID,即要进行数据交换的控件的标识符。m_memberName
是对话框类中的成员变量,用于存储控件的句柄。
在你提供的代码中,DDX_Control(pDX, IDC_EDIT1, m_edit1)
的作用是将 ID 为 IDC_EDIT1
的编辑框控件与对话框类中名为 m_edit1
的成员变量进行关联。这样一来,通过 m_edit1
成员变量可以直接操作对应的编辑框控件,例如获取或设置文本内容、启用或禁用控件等操作。
通过使用 DDX_Control
宏,可以方便地实现对话框类成员变量与控件之间的关联,简化了对控件的操作和管理。
访问对话框
GetDlgItem 根据控件的 ID 来获取对应的编辑框控件对象的指针。
SetWindowText:为编辑框设置初始值
GetWindowText:获取编辑框内的值
SetDlgItemText,GetDlgItemText 上面两个功能的综合体
BOOL CBingoDialog::OnInitDialog()
{CDialog::OnInitDialog();// TODO: 在此添加额外的初始化CWnd* pEdit01 = GetDlgItem(IDC_EDIT1);CWnd* pEdit02 = GetDlgItem(IDC_EDIT2);//CWnd* pEdit03 = GetDlgItem(IDC_EDIT3);CString strText;if (pEdit01 != NULL){pEdit01->SetWindowText(_T("100"));pEdit01->GetWindowText(strText);}if (pEdit02 != NULL){SetDlgItemText(IDC_EDIT2, _T("200"));GetDlgItemText(IDC_EDIT2, strText);}//if (pEdit03 != NULL) pEdit03->SetWinodwText(_T("300"));SetDlgItemInt(IDC_EDIT3, 100);return TRUE; // return TRUE unless you set the focus to a control// 异常: OCX 属性页应返回 FALSE
}
操作对话框
在对话框中输入三个值,点击确定按钮后,会把前两个值相加放到第三个中
UpdateData()
是 MFC 中用于进行对话框和控件之间数据交换的函数。通过调用 UpdateData(TRUE)
,可以将对话框中的控件的当前值更新到关联的成员变量中;而通过调用 UpdateData(FALSE)
,可以将成员变量的值更新到关联的控件中。
void CBingoDialog::OnBnClickedOk()
{// TODO: 在此添加控件通知处理程序代码//CDialog::OnOK();//UpdateData();//m_value3 = m_value2 + m_value1;//UpdateData(FALSE);CString str1, str2, str3;m_edit1.GetWindowText(str1);m_edit2.GetWindowText(str2);int t = _wtoi(str1) + _wtoi(str2);TCHAR buf[32] = _T("");str3 = buf;m_edit3.SetWindowTextW(str3);
}
SendMessage()
一个是调用全局的方法,一个是调用编辑框类的方法
::SendMessage(m_edit1.m_hWnd, WM_GETTEXT, 20, (LPARAM)buf);
m_edit1.SendMessage(WM_SETTEXT, sizeof(buf), (LPARAM)buf);
SendMessage()
是 Windows API 中用于发送消息的函数之一,在 MFC 中也可以使用。通过 SendMessage()
函数,可以向窗口或控件发送特定的消息,以实现各种操作,如获取或设置控件的状态、通知控件发生了特定的事件等。
具体来说,SendMessage()
函数的原型为:
LRESULT SendMessage(HWND hWnd,UINT Msg,WPARAM wParam,LPARAM lParam
);
hWnd
:指定要接收消息的窗口或控件的句柄。Msg
:指定要发送的消息类型,是一个表示消息的整数值。wParam
和lParam
:作为消息参数传递的额外信息,具体意义取决于不同的消息类型。
在 MFC 中,你可以使用 SendMessage()
函数来与窗口控件进行交互,例如设置控件的状态、获取控件的信息、自定义处理消息等。常见的用法包括发送 WM_SETTEXT
消息来设置控件的文本内容,发送 BM_CLICK
消息来模拟按钮的点击操作,以及发送自定义消息来实现特定的功能。
七、对话框伸缩功能实现
思路:初始化对话框函数中,获取窗口大小,计算得到缩小后的窗口大小
GetWindowRect获取对话框大小
判断一个矩形是否为空:IsRectEmpty(),IsRectNull()。前者是判断矩形面积是否为空,后者是判断矩形的四个坐标值是否为0,不关心是否能做为一个矩形。
SetWindowPos()改变窗口的大小。
BOOL CBingoDialog::OnInitDialog()
{CDialog::OnInitDialog();// 获取窗口范围GetWindowRect(m_large);m_small = m_large;// 计算得到缩小后的范围m_small.right = m_small.left + m_small.Width() / 2;m_small.bottom = m_small.top + m_small.Height() / 2;return TRUE; // return TRUE unless you set the focus to a control// 异常: OCX 属性页应返回 FALSE
}
在缩小按钮按钮事件中完成对窗口的缩放处理
void CBingoDialog::OnBnClickedBtnLarge()
{// TODO: 在此添加控件通知处理程序代码CRect curRect;GetWindowRect(curRect);CWnd* pButton = GetDlgItem(IDC_BTN_LARGE);CString strTitle;if (pButton){pButton->GetWindowText(strTitle);if (strTitle == _T("放大") && m_large.IsRectEmpty() == FALSE){pButton->SetWindowTextW(_T("缩小"));SetWindowPos(NULL, curRect.left, curRect.top, m_large.Width(), m_large.Height(), SWP_NOMOVE | SWP_NOZORDER);// 窗口Z轴控制//SetWindowPos(&wndTopMost, curRect.left, curRect.top, m_large.Width(), m_large.Height(), SWP_NOMOVE);}else if (m_small.IsRectEmpty() == FALSE){pButton->SetWindowTextW(_T("放大"));SetWindowPos(NULL, curRect.left, curRect.top, m_small.Width(), m_small.Height(), SWP_NOMOVE | SWP_NOZORDER);//SetWindowPos(&wndTopMost, curRect.left, curRect.top, m_small.Width(), m_small.Height(), SWP_NOMOVE);}}
}
八、小项目-逃跑按钮
创建一个基于对话框的项目,为两个按钮添加变量-控件
给这个项目,类向导,添加MFC类
这是,两个按钮添加变量-控件,是属于CBUTTON类;CMyButton类是属于CButton的子类。
鼠标移动到按钮控件上的消息会被控件捕获,而不会给对话框
不信的话,可以在对话框中设置一个鼠标移动消息验证一下
TRACE("%s(%d):%s %d %d\n", __FILE__, __LINE__, __FUNCTION__, point x, point y);
项目思路:为两个按钮对象给出MouseMove方法,响应消息
两个按钮创建变量-控件后,控件就会被类关联;使用CMyButton 指向这两个控件变量
CMyButton m_btn_light;CMyButton m_btn_left;
CMyButton中设置一个类指针
CMyButton* m_pButton;
在对话框初始化时关联起来
m_btn_left.m_pButton = &m_btn_light;m_btn_light.m_pButton = &m_btn_left;
处理逻辑
void CMyButton::OnMouseMove(UINT nFlags, CPoint point)
{// TODO: 在此添加消息处理程序代码和/或调用默认值ShowWindow(SW_HIDE);if (m_pButton != NULL){m_pButton->ShowWindow(SW_SHOW);}CButton::OnMouseMove(nFlags, point);
}
点击成功提示
void CRunningButtonDlg::OnBnClickedButton2()
{// TODO: 在此添加控件通知处理程序代码MessageBox(_T("恭喜你,点到我了"), _T("成功!"));
}
九、小项目-属性页添加
在八的基础上,给对话框添加一个新的按钮,点击后,会给出一个新的对话框
添加对话框资源
给新的对话框控件:Group Box,单选框,静态文本,列表
给这个对话框添加类,类向导,添加MFC类
给单选框添加控制变量-值,不必设置范围
这样做的意义就是有了对话框类,成员变量是控件的值,就可以拿到这些值了。
但是设置三个值记录太麻烦了,不如再次基础上魔改以下
修改为BOOL数组记录值
修改初始化函数,和关联函数
为新对话框类重写初始化函数,选择这个类,属性,重写,选择要重写的类,要在初始化时为列表进行初始化,单选框不用,因为我们只需要获得它的状态。
BOOL PROP_01::OnInitDialog()
{CPropertyPage::OnInitDialog();// TODO: 在此添加额外的初始化// 因为GetDlgItem拿到根据ID拿控件的对象地址,默认类型时CWND,需要强制转换// 就可以通过地址来操作类的成员属性CListBox* pListBox = (CListBox*)GetDlgItem(IDC_LIST_COMPANY);if (pListBox){pListBox->AddString(_T("阿里巴巴"));pListBox->AddString(_T("华为"));pListBox->AddString(_T("腾讯"));pListBox->AddString(_T("京东"));pListBox->AddString(_T("百度"));}return TRUE; // return TRUE unless you set the focus to a control// 异常: OCX 属性页应返回 FALSE
}
按照上面方法再创建一个对话框,添加控件
为对话框添加MFC类,右击对话框,类向导,添加MFC类
为多选框添加控制变量-值,和上面一样
按照上面再添加一个,最后一个了
类向到,添加MFC类
添加控制变量-值
十、属性页处理
再追加一个MFC类,类向导,MFC类
将三个对话框作为成员变量添加给类 CMyProSheet
两个构造函数都要处理
父对话框触发点击事件
运行程序试试看
程序崩溃,尝试Debug,点击重试,程序就可以定位到崩溃处
问题出在这里
可能时对话框实例失败,验证以下,问题不在这里
查看调用堆栈,貌似也没啥问题,嗯?发现问题
查看路径下源码行处,发现对于处理单选框需要指定Group属性
单选框只能选一个,CTRL+D,可见Tab顺序
需要3,4,5是一个tab中,只需要再第一个单选框选中即可 GROUP=TRUE
那么单选框也就不需要数组了,Int 即可
接下来,优化对话框的上一步,下一步,去掉帮助按钮,未优化
优化:重写三个对话框类的激活方法(不是消息,是类的一个方法)
给公司添加控制变量
优化:重写OnwizardNext(),必须选择才能执行到下一步
十一、一些开发经验
需要初始化的控件可以在父窗口类初始化时进行。
添加控制变量(控件):值,可以拿到控件的值(编辑框的内容等等);控件,可以拿到控件的句柄保存到一个对象中。
将控制变量放到父窗口类中,我们可以自由拿到,通过父窗口对象操作控件
当需要处理控件的消息时,需要为控件添加控制变量到父窗口类中;如果想要处理该控件的消息,就需要实例化这个控件(所有控件都是继承自CWIND,都是窗口)。就需要创建出来一个类,利用多态,新类指向控制变量-控件,在新类中设置处理消息的函数,就可以了。参考八
获取窗口大小的操作都是放到初始化时完成的
在函数中不可实例化对象,因为函数结束对象销毁,不妨把要实例化的类放在类中,在函数中创建重绘。
添加MFC类,其实道理和八一样,对话框(控件)有了自己的类,就可以有更多操作的控件;再框架中要通过消息映射机制,操作空间有限