MFC-第三节 基于对话框编程、常用控件

基于对话框编程

  1. 对话框是一种特殊类型的窗口,绝大多数Windows程序都通过对话框与用户进行交互。在Visual C++中,对话框既可以单独组成一个简单的应用程序,又可以成为文档/视图结构程序的资源。

创建基于对话框的 MFC 应用程序框架

  1. 选择“文件 新建  项目”菜单;
  2. 在“新建项目”对话框中,选择“ MFC 应用程序 ”,输入工程名称(MFCDialog),选择“确定”。
  3. 应用程序类型:选择“ 基于对话框”,即创建基于对话框的应用程序,选择“完成

对话框应用程序框架介绍

  1. 资源视图
    1. 创建基于对话框的应用程序框架(假定工程名为 MFCDialog )后,项目工作区上增加了一个“资源视图”选项卡。或者,通过视图找到“资源视图”选项卡
    2. 点击该选项卡,展开项目目录,找到Dialog文件目录下会看到2个文件”IDD_ABOUTBOX”/”IDD_MFCDIALOG_DIALOG”
    3. 在 MFC中,与用户进行交互的对话框界面被认为是一种资源。
      1. ID为IDD_MFCDIALOG_DIALOG:
        1. 中间部分(MFCDIALOG)与项目名称相同,对应中间的对话框设计界面。不管在何时,只要双击对话框资源的ID,对话框设计界面就会显示在中间。
      2. IDD_ABOUTBOX:
        1. 关于对话框,用于说明当前应用程序的一些信息
    4. 运行demo,显示的视窗,就是IDD_MFCDIALOG_DIALOG资源,相当于iOS的sb,点击左上角的图标弹出系统菜单,点击”关于…“,弹出的就是关于对话框IDD_ABOUTBOX资源
    5. 右击IDD_MFCDIALOG_DIALOG资源,属性,找到Caption设置内容,这便是对话框的标题设置
  2. 类视图
    1. 在类视图中,可以看到生成了3 个类:CAboutDlg、CMFCDialogApp和CMFCDialogDlg。
      • CAboutDlg:对应生成的版本信息对话框。
      • CDialogApp:应用程序类,从 CWinApp 继承过来,封装了初始化、运行、终止该程序的代码。
      • CDialogDlg:对话框类,从CdialogEx继承过来的,在程序运行时看到的对话框就是它的一个具体对象
        1. DoDataExchange函数:该函数主要完成对话框数据的交换和校验。
        2. OnInitDialog函数:相当于对对话框进行初始化处理。
  3. 工具箱:
    1. 视图->工具箱
    2. 各种控件用于丰富资源界面

模态对话框与非模态对话框

模态对话框

  1. 当模态对话框显示时,程序会暂停执行,直到关闭这个模态对话框之后,才能执行程序中的其他任务。
  2. 实现点击”模态对话框“按钮,则弹出一个模态对话框
    1. 主界面添加”模态对话框“按钮
      1. 通过工具箱在主界面上放一个Button,右击->属性,找到caption修改名字为模态对话框
      2. 双击此按钮即可跳转到按钮处理函数:会看到是在MFCDialogDlg.cpp文件中
        1. 除了双击,还有2种方法,添加点击按钮的实现
          1. 右击按钮->属性->点击右边的闪电图标(控件事件)->BN_CLICKED->右边的下来箭头Add…
          2. 右击按钮->添加事件处理程序->选择事件类型、修改处理程序名称->添加编辑即可
    2. 添加模态对话框的资源视图
      1. 资源视图 -> Dialog -> 右击 -> 插入 Dialog:会看到资源视图的Dialog文件夹下多了一个IDD_DIALOG1资源视图
      2. 设置当前视图名称为”模态对话框“
      3. 修改对话框的ID,在属性中找到ID,修改为IDD_EXEC
    3. 当前模态对话框添加相应的类
      1. 点击对话框模板 -> 右击 -> 添加类:
      2. 设置类名(CDlgEexec)点击确定,即可
      3. 类视图中多了一个自定义类CDlgEexec:这个类就是与这个对话框关联的类
    4. 主界面按钮处理函数创建对话框,以模态方式运行。
      1. 实现模态对话框的创建需要调用CDialog类的成员函数CDialog::DoModel,该函数的功能就是创建并显示一个对话框:
      2. 在MFCDialogDlg.cpp文件中
        1. 导入CDlgEexec.h文件
        2. 找到”模态对话框“按钮相应函数
         void CMFCDialogDlg::OnBnClickedButton1()
         {
         	// TODO: 在此添加控件通知处理程序代码
         	CDlgEexec dlg;
         	dlg.DoModal();//以模态的方式运行
         }
        

非模态对话框

  1. 当非模态对话框显示时,运行转而执行程序中的其他任务,而不用关闭这个对话框
  2. 实现点击”非模态对话框“按钮,则弹出一个非模态对话框
    1. 图形界面操作过程和模态对话框一样,ID为IDD_SHOW,类名为CDlgShow
    2. 在CMFCDialogDlg类的成员函数中添加成员属性

       // MFCDialogDlg.h: 头文件
       #pragma once
       #include "CDlgShow.h"
         
       // CMFCDialogDlg 对话框
       class CMFCDialogDlg : public CDialogEx
       {
       // 构造
       public:
       	...
       // 实现
       protected:
       	...
       	   //这个要保存到成员属性中强引用,否则就会释放掉
          CDlgShow  dlg;
       };
      
    3. 在OnInitDialog函数中create
      1. 这个方法就相当于iOS的viewDidLoad
       // CMFCDialogDlg 消息处理程序
       BOOL CMFCDialogDlg::OnInitDialog()
       {
       	...
       	// TODO: 在此添加额外的初始化代码
       	//该方法不允许多次创建,只能创建一次,因此可以把它放在CMFCDialogDlg类的OnInitDialog函数中
       	dlg.Create(IDD_SHOW);
       	return TRUE;  // 除非将焦点设置到控件,否则返回 TRUE
       }
      
    4. 实现非模态按钮点击方法

       void CMFCDialogDlg::OnBnClickedButton2()
       {
       	// TODO: 在此添加控件通知处理程序代码
       	//这个要保存到成员属性中强引用,否则就会释放掉
       	//CDlgShow  dlg;
       	//该方法不允许多次创建,只能创建一次,因此可以把它放在CMFCDialogDlg类的OnInitDialog函数中
       	//dlg.Create(IDD_SHOW);
       	dlg.CenterWindow();//居中显示
       	dlg.ShowWindow(SW_SHOWNORMAL);
       }
      

常用控件

静态文本框CStatic

  1. 静态文本框是最简单的控件,它主要用来显示文本信息,不能接受用户输入,一般不需要连接变量,也不需要处理消息。
  2. 静态文本框的重要属性有:
    1. ID:所有静态文本框的缺省ID都是IDC_STATIC,静态ID,不响应任何消息(事件)
    2. Caption:修改显示的内容
  3. 常用的接口

     CWnd::SetWindowText  设置控件内容
     CWnd::GetWindowText 获取控件内容
     CStatic::SetBitmap 设置位图(后缀为bmp的图片)
    
  4. 关联变量(相当于iOS的脱线成属性)
    1. 由于XXX_STATIC静态ID是不能关联变量,故需把ID修改后,再关联变量:右击属性,修改ID:IDC_STATICLabel
    2. 右击->添加变量,设置变量名、访问类型,点击确定即可(m_text)
    3. 就可以在当前主窗口类中生成一个成员变量
    4. 代码示例:

       void CMy01staticTextDlg::OnBnClickedButton1()
       {
       	// TODO: 在此添加控件通知处理程序代码
       	//设置文本
       	m_text.SetWindowTextW(TEXT("hhhh"));
       }
              
              
       void CMy01staticTextDlg::OnBnClickedButton2()
       {
       	// TODO: 在此添加控件通知处理程序代码
       	//获取当前的文本
       	CString str;
       	m_text.GetWindowTextW(str);
       	MessageBox(str);
       }
      
  5. 显示bmp图片
    1. 将bmp图片复制到项目根目录下 1.bmp
    2. 在拖一个用来显示图片,修改ID,添加为成员变量,命名m_img
    3. 显示图片

       //显示图片
       	//设置静态控件窗口风格为位图居中显示
       	m_img.ModifyStyle(0xf, SS_BITMAP | SS_CENTERIMAGE);
       	//通过路径获取bitmap句柄
       	#define HBMP(filepath,width,height) (HBITMAP)LoadImage(AfxGetInstanceHandle(),filepath,IMAGE_BITMAP,width,height,LR_LOADFROMFILE|LR_CREATEDIBSECTION)
              
       	//获取控件宽高
       	CRect rect;
       	m_img.GetWindowRect(rect);
              
       	//静态控件设置bitmap
       	m_img.SetBitmap(HBMP(TEXT("./1.bmp"), rect.Width(), rect.Height()));
      

普通按钮 CButton

  1. 按钮是最常见的、应用最广泛的一种控件。在程序执行期间,当单击某个按钮后就会执行相应的消息处理函数。
  2. 按钮的主要属性是Caption,来设置在按钮上显示的文本。
  3. 命令按钮处理的最多的消息是:BN_CLICKED,双击按钮即可跳转到处理函数。或者,通过按钮属性 -> 控制事件 -> 选择所需事件,添加处理函数:
  4. 常用接口:

     CWnd::SetWindowText 设置控件内容
     CWnd::GetWindowText 获取控件内容
     CWnd::EnableWindow 设置控件是否变灰
    
  5. 关联控件变量:跟CStatic一样

编辑框CEdit

  1. 常用属性设置:

     Number               True只能输入数字
     Password             True密码模式
     Want return          True接收回车键,自动换行,只有在多行模式下,才能换行
     Multiline            True多行模式
     Auto VScroll         True 当垂直方向字符太多,自动出现滚动条,同时设置Vertical Scroll才有效
     Vertical Scroll      True当垂直方向字符太多,自动出现滚动条,和Auto VScroll配合使用
     Horizontal Scroll    True当垂直方向字符太多,自动出现滚动条,和Auto HScroll配合使用
    Read Only            True 只读
    
  2. 常用接口:

     CWnd::SetWindowText  设置控件内容
     CWnd::GetWindowText  获取控件内容
    
  3. 关联控件变量
    1. 同上
  4. 关联基本类型变量
    1. 右击该控件->添加变量
      1. 变量类型:选择适合的类型
      2. 变量名:自己定义
      3. 类别:
        1. 控件(control):默认
        2. value(值)
          1. 若一个编辑框连接了一个Value类别的变量,则该变量就表示这个编辑框中显示的内容就是变量的值。
          2. 但是,改变了编辑框的内容并不会自动更新对应的变量的值,同样,改变了变量的值也不会自动刷新编辑框的内容。若要保持一致,需要使用UpdateData()函数更新:
            • 若编辑框的内容改变了,则应使用语句UpdateData(TRUE) 获取对话框数据
            • 若变量的值改变了,则应使用语句UpdateData(FALSE) 初始化对话框控件
  5. 代码示例:(变量类别选择为“值(value)”类型。)
    1. 回车退出: 该对话框运行起来,输入内容,然后回车,就会返现程序会退出
      1. 解决:将属性Multiline 改为ture
    2. 点击回车,自动换行
      1. 将属性Want return设置为ture
    3. 横向、纵向无限输入:当输入内容超过默认大小时,自动滚动输入 Auto VScroll、Auto HScroll,设置为true
    4. 代码:

       void CMy02EditTestDlg::OnBnClickedButton1()
       {
       	// TODO: 在此添加控件通知处理程序代码
       	//点击copy按钮,将edit1的内容粘贴到edit2中
       	CString str;
       	m_edit1.GetWindowTextW(str);
       	m_edit2.SetWindowTextW(str);
       }
       //退出当前窗口
       void CMy02EditTestDlg::OnBnClickedButton2()
       {
       	// TODO: 在此添加控件通知处理程序代码
       	//点击“close”关闭应用程序
       	//退出整个程序
       	//exit(0);
       	//退出当前对话框
       	//以确定的方式退出
       	//CDialogEx::OnOK();
       	//以取消的方式退出
       	CDialogEx::OnCancel();
       }
              
       //设置edit3
       	//注意:edit3是以值(value)的类型关联的成员变量
       void CMy02EditTestDlg::OnBnClickedButton3()
       {
       	// TODO: 在此添加控件通知处理程序代码 
       	//设置edit3,会发现没有GetWindowTextW方法
       	m_edit3 = TEXT("hhhhh");
       	//将变量同步到控件上
       	UpdateData(FALSE);
       }
              
       //获取edit3内容
       void CMy02EditTestDlg::OnBnClickedButton4()
       {
       	// TODO: 在此添加控件通知处理程序代码
       	//将控件内容同步到变量中
       	UpdateData(TRUE);
              
       	MessageBox(m_edit3);
       }
      

组合框(下拉框) CComboBox

  1. 常用属性设置:

     data  设置内容,不同内容间用英文的分号“;”分隔
     type  显示风格
     Sort  True 内容自动排序
    
  2. 常用接口:

     CComboBox::AddString  组合框添加一个字符串
     CComboBox::SetCurSel  设置当前选择项(当前显示第几项),下标从0开始
     CComboBox::GetCurSel  获取组合框中当前选中项的下标
     CComboBox::GetLBText  获取指定位置的内容
     CComboBox::DeleteString  删除指定位置的字符串
     CComboBox::InsertString   在指定位置插入字符串
    
  3. 使用:

    1. 拖一个CComboBox,右击属性设置data:可乐;雪碧;经典
    2. 运行程序会发现顺序不是设置的顺序,主要原因是属性中的sort的原因,设置为false即可
    3. 默认框中是可以输入内容的,但是不想让输入,使用type属性,设置为“下拉列表”即可

       // CMy03commonboxDlg 消息处理程序
       BOOL CMy03commonboxDlg::OnInitDialog()
       {
       	...
          
       	// TODO: 在此添加额外的初始化代码
       	//对下拉框添加数据
       	m_cbx.AddString(TEXT("郑州"));
       	m_cbx.AddString(TEXT("北京"));
       	m_cbx.AddString(TEXT("南京"));
       	//设置默认值
       	m_cbx.SetCurSel(2);
       	//删除某项
       	m_cbx.DeleteString(0);
       	//插入数据
       	m_cbx.InsertString(2, TEXT("成都"));
       	//根据索引查找内容
       	/*CString str;
       	m_cbx.GetLBText(2, str);
       	MessageBox(str);*/
              
       	return TRUE;  // 除非将焦点设置到控件,否则返回 TRUE
       }
      
      
    4. 选择某个item的事件监听
      1. 组合框常用的事件为:CBN_SELCHANGE,当选择组合框某一项时,自动触发此事件。
      2. 右击CComboBox控件,属性->点击闪电(事件),找到 CBN_SELCHANGE,会看见响应函数已经实现OnCbnSelchangeCombo1
      3. 找到该函数实现

         void CMy03commonboxDlg::OnCbnSelchangeCombo1()
         {
         	// TODO: 在此添加控件通知处理程序代码
         	//获取到当前索引
         	int index = m_cbx.GetCurSel();
         	//拿到内容
         	CString str;
         	m_cbx.GetLBText(index, str);
         	//弹出选择的内容
         	MessageBox(str);
         }
        

列表控件 CListCtrl

  1. 常用属性设置:view -> Report(报表方式)
  2. 常用接口:

     CListCtrl::SetExtendedStyle    设置列表风格
     CListCtrl::GetExtendedStyle    获取列表风格
     CListCtrl::InsertColumn        插入某列内容,主要用于设置标题
     CListCtrl::InsertItem          在某行插入新项内容
     CListCtrl::SetItemText         设置某行某列的子项内容
     CListCtrl::GetItemText         获取某行某列的内容
    
  3. 使用
    1. 拖一个listctrl注意不是listbox
    2. 右击属性->view,里面有很多样式,选择report
    3. 关联变量

       // CMy05listtestDlg 消息处理程序
      
       BOOL CMy05listtestDlg::OnInitDialog()
       {
       ...
              
       	// TODO: 在此添加额外的初始化代码
       	//listctrl初始化
       	//插入表头
       	//插入列头
       	CString str[3] = { TEXT("姓名"),TEXT("性别"),TEXT("年龄") };
       	for (int  i = 0; i < 3; i++)
       	{
       		//索引、内容、宽度
       		m_list.InsertColumn(i, str[i],LVCFMT_LEFT,100);
       	}
              
       	//设置正文
              
       	////设置张三行
       	//m_list.InsertItem(0, TEXT("张三"));
       	//m_list.SetItemText(0, 1, TEXT("男"));
       	//m_list.SetItemText(0, 2, TEXT("18"));
       	for (int  i = 0; i < 10; i++)
       	{
       		int j = 0;
       		CString name;
       		name.Format(TEXT("张三 %d"), i);
       		m_list.InsertItem(i, name);
       		m_list.SetItemText(i, ++j, TEXT("男"));
       		m_list.SetItemText(i, ++j, TEXT("18"));
       	}
              
       	//设置风格样式
       	//LVS_EX_GRIDLINES 网格
       	//LVS_EX_FULLROWSELECT 选中整行
       	m_list.SetExtendedStyle(m_list.GetExtendedStyle()
       		| LVS_EX_GRIDLINES | LVS_EX_FULLROWSELECT
       	);
       	return TRUE;  // 除非将焦点设置到控件,否则返回 TRUE
       }
      

树控件 CTreeCtrl

  1. 常用属性设置:

     has buttons    True 有展开按钮
     has lines      True 有展开线
     lines at root  True 有根节点
    
  2. 常用接口:

     AfxGetApp()   获取应用程序对象指针
     CWinApp::LoadIcon   加载自定义图标
     CImageList::Create    创建图像列表
     CImageList::Add         图像列表追加图标
     CTreeCtrl::SetImageList    设置图形状态列表
     CTreeCtrl::InsertItem	插入节点
     CTreeCtrl::SelectItem	设置默认选中项
     CTreeCtrl::GetSelectedItem   获取选中项
     CTreeCtrl::GetItemText	 获取某项内容
    
  3. 使用

    1. 拖一个CTreeCtrl控件
    2. 设置常用属性:
      1. 属性has lines设置为true展开则有线连接
      2. 属性has buttons 设置为true 每级有按钮
      3. 属性lines at root True 根节点之前也有线
    3. 添加为成员变量m_tree
    4. 添加图片资源
      1. 打开项目根目录->res文件夹->打开,将图片资源复制进去,图片资源为ico结尾
      2. 导入图片资源:
        1. 打开“资源视图”有个icon目录文件夹
        2. 右击Icon->添加资源->icon->导入->打开res文件夹->选中刚才所有导入的图片资源->打开,即将所有的资源导入了
    5. 初始化方法中添加初始化CTreeCtrl控件

       // TODO: 在此添加额外的初始化代码
       	//使用
       	//1. 设置资源
       	//1.1手动导入图片资源
       	//1.2准备HICON
       	HICON icon[4];
       	//IDI_ICON1为导入图片资源的ID
       	icon[0] = AfxGetApp()->LoadIconW(IDI_ICON1);
       	icon[1] = AfxGetApp()->LoadIconW(IDI_ICON2);
       	icon[2] = AfxGetApp()->LoadIconW(IDI_ICON3);
       	icon[3] = AfxGetApp()->LoadIconW(IDI_ICON4);
       	//放到成员属性中
       	/*CImageList list;*/
       	//1.3准备list图标的集合
       	list.Create(30, 30, ILC_COLOR32, 4, 4);
       	//1.4 加载图片
       	for (int i = 0; i < 4; i++)
       	{
       		list.Add(icon[i]);
       	}
       	//list.Add()
       	m_tree.SetImageList(&list,TVSIL_NORMAL);
       	//2. 设置节点
       	//参数:结点名/默认图片/选中图片/父节点
       	HTREEITEM rootitem = m_tree.InsertItem(TEXT("根节点"),0,0,NULL);
       	HTREEITEM partentitem = m_tree.InsertItem(TEXT("父节点"), 1, 1, rootitem);
       	HTREEITEM sub1item = m_tree.InsertItem(TEXT("子节点1"), 2, 2, partentitem);
       	HTREEITEM sub2item = m_tree.InsertItem(TEXT("子节点2"), 3, 3, partentitem);
       	//3. 设置默认选中节点
       	m_tree.SelectItem(sub1item);
      
    6. 点击每个结点,事件监听
      1. 树控件常用事件为:TVN_SELCHANGED,当选择某个节点时,自动触发此事件。
      2. 在属性、事件列表中找到该事件,然后右击添加,定位到函数

         void CMy06treectrolDlg::OnTvnSelchangedTree1(NMHDR *pNMHDR, LRESULT *pResult)
         {
         	LPNMTREEVIEW pNMTreeView = reinterpret_cast<LPNMTREEVIEW>(pNMHDR);
         	// TODO: 在此添加控件通知处理程序代码
         	//获取当前选中的行
         	HTREEITEM item = m_tree.GetSelectedItem();
         	//根据当前选中的行,获取内容
         	CString str = m_tree.GetItemText(item);
         	MessageBox(str);
         	*pResult = 0;
         }
        

标签控件 CTabCtrl

  1. 功能实现:一个tab2个item:系统消息、系统设置,点击相应的item,显示相应的视窗
  2. 拖入一个CTabCtrl控件
  3. 添加为成员变量,但是这个变量继承自CTabSheet这个自定义的类
    1. 将TabSheet.h与.cpp复制到项目根目录下(这两个文件是自定义的CTabCtrl控件类CTabSheet)
    2. 右击项目,添加现有项目,分别把.h与.cpp加入
    3. 右击CTabCtrl控件->添加变量->变量类型修改为CTabSheet,命名为m_tab
  4. 插入2个DIALOG
    1. 右击Dialog,插入2个
    2. 修改这两个Dialog 属性,Border设置为None,style设置为child
    3. 分别拖一个Button一个为“系统消息”,一个为“系统设置”
  5. 分别为这两个Dialog添加类:Dialog1,Dialog2
  6. 主对话框中导入头文件,添加成员变量

     #include"TabSheet.h"
     #include"Dlg1.h"
     #include"Dlg2.h"
     // CMy08TBDlg 对话框
     class CMy08TBDlg : public CDialogEx
     {
      ...
      public:
     	CTabSheet m_tab;
     	Dlg1 dlg1;
     	Dlg2 dlg2;
     };
    
  7. 主对话框类中 OnInitDialog() 做初始化工作

     // TODO: 在此添加额外的初始化代码
     //dlg1.dlg2加载到tab中
     m_tab.AddPage(TEXT("系统消息"), &dlg1, IDD_DIALOG1);
     m_tab.AddPage(TEXT("系统设置"), &dlg2, IDD_DIALOG2);
     //显示
     m_tab.Show();
    
Table of Contents