MFC使用

MFC是一个封装了WIN32 API的Windows下UI设计库。

一、WIN API

#include<windows.h>
windows使用句柄控制对象, Windows系统程序的入口为winMain

1.1 windows的消息处理机制

Windows程序设计是一种事件驱动方式的程序设计模式,主要是基于消息的。
每一个 Windows 应用程序开始执行后, 系统都会为该程序创建一个消息队列, 这个消息队列用来存放该程序创建的窗口的消息。
操作系统会感知到用户的操作事件,然后将事件包装成一个消息,投递到对应的应用程序的消息队列中。
然后应用程序通过一个消息循环不断地从消息队列中取出消息,并进行响应。
在这个处理过程中,操作系统也会给应用程序“ 发送消息”。所谓“ 发送消息”,实际上是操作系统调用程序中一个专门负责处理消息的函数,这个函数称为窗口过程。
0
 
0

二、MFC入门

MFC开发相关帮助文档:VC++之MFC类库中文手册、MSDN

2.1 MFC程序创建

MFC应用程序有程序类CWinAPP,每一个MFC应用程序都会有继承此基类的应用程序类,并且有且仅有一个应用程序类实例,且必须声明为全局。该应用程序类的InitInstance()是程序入口。
在VS2019中使用向导创建应用程序,一般选中基于对话框或基于单个文档(SDI)创建。
常使用"类视图”“资源视图"”工具箱“”属性“这几个VS中的视图参与开发。
”类视图“可以帮助查看项目的类结构
”资源视图“可以看见MFC中的视图资源,也可在此创建、添加资源。
”工具箱“中可以查看MFC的常用控件
”属性“可以查看对象属性,可以在对应的控件右键菜单中的”属性“中跳转到此视图

2.1.1 SDI

MFC应用程序框架结构的基石是文档/视图体系结构,它定义了一种程序结构,这种结构依靠文档对象保存应用程序的数据,并依靠视图对象控制视图中显示的数据,把数据本身与它的显示分离开。
数据的存储和加载由文档类来完成,数据的显示和修改则由视类来完成。 MFC在类CDocument和CView中为稳定视图提供了基础结构。CWinApp、CFrameWnd和其他类与CDocument和CView合作,把所有的片段连在了一起。
VS2019默认创建的SDI程序可直接运行,可以在用户界面功能和高级功能中裁剪

2.1.1 项目属性设置

  1. 字符集,使用Unicode字符集,可在”项目“-”属性“-”配置属性“-”常规“-”字符集“中选择
    • TEXT(_T)宏,此宏可以自动适应字符类型,如果定义了预处理器程序符号_UNICODE,那么编译器将使用Unicode字符,如果没用定义该预处理器程序符号,那么编译器将使用ANSI字符。
    • TCHAR类型,如果定义了_UNICODE符号TCHAR将为wchar_t类型。如果没用定义_UNICODE符号,TCHAR将变为普通古老的char类型。
  1. 添加支持_s函数的预处理命令,_CRT_SECURE_NO_WARNINGS,在“项目”-“属性”-“C/C++”-“预处理器”-“预处理器定义”中添加

2.2 消息映射机制

消息映射是一个将消息和成员函数相互关联的表。比如,框架窗口接收到一个鼠标左击消息,MFC将搜索该窗口的消息映射,如果存在一个处理WM_LBUTTONDOWN消息的处理程序,然后就调用OnLButtonDown。

2.2.1 将消息映射添加到一个类中所做的全部工作

  1. 在所操作的类中声明消息映射宏 DECLARE_MESSAGE_MAP() ,注意,后面不用加分号
  2. 放置标志消息的宏来执行消息映射,相应的类将在对BEGIN_MESSAGE_MAP和END_MESSAGE_MAP的调用之间处理消息。

2.2.2 自定义消息并设置消息映射

  1. 在头文件中自定义消息,NM_A是自定义的消息名,100是自定义的偏移值,WM_USER+100唯一表示NM_ANM_A
1 #define NM_A (WM_USER+100)
  1. 在需要响应此消息的类定义在BEGIN_MESSAGE_MAP和END_MESSAGE_MAP之间添加消息映射。ON_MESSAGE()表示这是一个自定义消息的映射,NM_A是响应的消息,OnMyChange是自定义的响应函数。
1 ON_MESSAGE(NM_A, OnMyChange)
  1. 自定义响应函数可以参考
1 LRESULT CMainFrame::OnMyChange(WPARAM wParam, LPARAM lParam){}
  1. 触发消息可以参考
1 //类比::PostMessage和PostMessage和SendMessage
2 ::PostMessage(AfxGetMainWnd()->GetSafeHwnd(), NM_C,(WPARAM)NM_C, (LPARAM)0);

2.3 MFC框架中的一些重要函数

  1. InitInstance函数,应用程序类的一个虚函数,MFC应用程序的入口。
  2. PreCreateWindow函数,当框架调用CreateEx函数创建窗口时,会首先调用PreCreateWindow函数。通过修改传递给PreCreateWindow的结构体类型参数CREATESTRUCT,应用程序可以更改用于创建窗口的属性。在产生窗口之前让程序员有机会修改窗口的外观。最后再调用reateWindowEx函数完成窗口的创建。
  3. OnCreate函数,OnCreate是一个消息响应函数,是响应WM_CREATE消息的一个函数,而WM_CREATE消息是由Create函数调用的。一个窗口创建(Create)之后,会向操作系统发送WM_CREATE消息,OnCreate()函数主要是用来响应此消息的。
    OnCreate与Create的区别
    • Create()负责注册并产生窗口,像动态创建控件中的Create()一样,窗口创建之后会向操作系统发送WM_CREATE消息。
    • OnCreate()不产生窗口,只是在窗口显示前设置窗口的属性如风格、位置等。
    • OnCreate()是消息WM_CREATE的消息响应函数
  1. OnDraw和OnPaint
    • OnPaint是WM_PAINT消息的消息处理函数,在OnPaint中调用OnDraw,一般来说,用户自己的绘图代码应放在OnDraw中。
    • OnPaint()是CWnd的类成员,负责响应WM_PAINT消息。
    • OnDraw()是CView的成员函数,没有响应消息的功能。
    • 当在View类里添加了消息处理OnPaint()时,OnPaint()就会覆盖掉OnDraw()。

2.4 扩展知识

  • MFC中后缀名为Ex的函数都是扩展函数。
  • 在MFC中,以Afx为前缀的函数都是全局函数,可以在程序的任何地方调用它们。
  • OnInitXXXX函数,如OnInitDlg是每种窗口对应的初始化函数

2.5 对话框

2.5.1 基于对话框的项目

2.5.2 模态窗口

1 CDialog dlg;
2 dlg.DoModal();

2.5.3 非模态窗口

1 CDialog dlg; //此对话框定义如果放在某一函数体内,则当程序离开此函数体会导致变量失效
2 dlg.Create(IDD_DIALOG_SHOW); //IDD_DIALOG_SHOW是对话框模板资源的ID,参数二是父窗口,缺省值是主程序窗口
3 dlg.ShowWindow(SW_SHOWNORMAL);

2.5.4 Dialog的属性

  1. 边框,常见是对话框外框,还可选择None
  2. 样式,常见是Popup,内嵌时选中Child

2.5.5 WM_CLOSE

对话框点击右上角的关闭按键会触发WM_CLOSE消息,重写其对应的OnClose事件可以改变其默认行为

2.6 常用控件

2.6.1 静态文本框CStatic

Static Text一般只用来显示文本信息,且一般不需要连接变量。
如果需要连接变量,需要修改ID,因为所有的静态文本框的缺省ID都是IDC_STATIC

常用接口

接口
功能
CWnd::SetWindowText
设置控件内容
CWnd::GetWindowText
获取控件内容
CStatic::SetBitmap
设置位图(后缀为bmp的图片)

静态文本框可以加载bmp图片

1 //设置静态控件窗口风格为位图居中显示
2 m_label.ModifyStyle(0xf, SS_BITMAP | SS_CENTERIMAGE);
3 //通过路径获取bitmap句柄
4 #define HBMP(filepath,width,height)(HBITMAP)LoadImage(AfxGetInstanceHandle(),filepath,IMAGE_BITMAP,width,height,LR_LOADFROMFILE|LR_CREATEDIBSECTION)
5 //静态控件设置bitmap
6 m_label.SetBitmap(HBMP(TEXT("./1.bmp"), 300, 250));

2.6.2 普通按键CButton

Button

常用接口

接口
功能
CWnd::SetWindowText
设置控件内容
CWnd::GetWindowText
获取控件内容
CWnd::EnableWindow
设置控件是否可用

2.6.3 编辑框CEdit

Edit Control,默认只支持单行,不接收回车,在控件上按下回车会跳转到OnOK(),会导致当前窗口被关闭。
解决此bug:重写OnOK方法,

关联变量

EditControl控件可以关联控件变量和值变量
  1. 关联控件变量,使用SetWindowText和GetWindowText来操作控件文本
  2. 关联值变量,使用UpdateData()操作控件文本,当参数为TRUE时将控件本文同步到值变量;当参数为FALSE时将值变量的值同步到控件文本
关联值变量时可以选择值变量的类型,参考文本的显示需求

常用属性设置

属性
含义
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 只读

常用接口

接口
功能
CWnd::SetWindowText
设置控件内容
CWnd::GetWindowText
获取控件内容

2.6.4 组合框CComboBox

Combo Box是下拉框

常用属性设置

属性
含义
data
设置内容,不同内容间用英文的分号“;”分隔
type
显示风格,选择DropList则下拉框的item不能通过键盘修改
Sort
True 内容自动排序

常用接口

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

常用事件

CBN_SELCHANGE,当下拉框选择改变时会触发此事件

2.6.5 列表控件CListCtrl

List Control控件
设置列表风格时常选择LVS_EX_GRIDLINES 和 LVS_EX_FULLROWSELECT

常用属性设置

view->Report(报表方式)

常用接口

接口
功能
CListCtrl::SetExtendedStyle
设置列表风格
CListCtrl::GetExtendedStyle
获取列表风格
CListCtrl::InsertColumn
插入某列内容,主要用于设置标题
CListCtrl::InsertItem
在某行插入新项内容
CListCtrl::SetItemText
设置某行某列的子项内容
CListCtrl::GetItemText
获取某行某列的内容

2.6.5 树控件CTreeCtrl

Tree Control的节点可以设置父子关系,且节点可以设置图标

节点图标设置

  1. 导入图标资源:将图标资源放入项目中的res文件夹,并在资源视图中右键icon->添加资源中添加相应资源。
  2. 加载图标,可以在OnInitXXX方法中完成
     1 //加载图标 
     2 HICON icon[3];
     3 icon[0] = AfxGetApp()->LoadIconW(IDI_ICON1); 
     4 icon[1] = AfxGetApp()->LoadIconW(IDI_ICON2); 
     5 icon[2] = AfxGetApp()->LoadIconW(IDI_ICON3); 
     6 //定义图像列表对象,需要非临时对象 
     7 CImageList m_imageList; 
     8 //创建图像列表对象的空间 m_imageList.Create(30,30,ILC_COLOR32,3,3); //参数一二是图标的横纵像素,参数三是像素级别,参数四和参数五一般保存一致,参数四是列表中最初包含的图标数 
     9 //给图像列表添加图片 
    10 for (int i = 0; i < 3; i++) 
    11 { 
    12     //图片列表加载图标 
    13     m_imageList.Add(icon[i]); 
    14 }
    15 //树控件设置图片列表 
    16 m_treeCtrl.SetImageList(&m_imageList, TVSIL_NORMAL); 
    17 //给树创建节点 
    18 //根节点,父节点,子节点 
    19 HTREEITEM root = m_treeCtrl.InsertItem(TEXT("中国"), 0, 0, NULL); //参数一是节点文本,参数二是节点未选择状态下的图标(ImageList中的序号),参数三是节点被选中状态下的图标,参数四是父节点,参数五是子节点 
    20 HTREEITEM fathter = m_treeCtrl.InsertItem(TEXT("北京"), 1, 1, root); 
    21 HTREEITEM son = m_treeCtrl.InsertItem(TEXT("海淀"), 2, 2, fathter);

     

常用属性设置

has buttons
True 有展开按钮
has lines
True 有展开线
lines at root
True 根节点有展开线

常用接口

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

常用事件

TVB_SELCHANGED

2.6.6 标签控件CTabCtrl

Tab Control可以将一些对话框内嵌在tab页内,只要创建非模态对话框,并且在非模态对话框Create时设定父对象为TabCtrl控件

常用接口

接口
功能
CWnd::GetClientRect()
 
CTabCtrl::GetItemRect
 
CDialog::SetWindowPos
 
CTabCtrl::InsertItem
设置标题头
CWnd::ShowWindow
可选SW_SHOW和SW_HIDE
CTabCtrl::GetCurFocus
返回被选中的标签下标
CTabCtrl::GetCurSel
获取当前选中
CTabCtrl::SetCurSel
获取选中项

2.7 静态拆分窗口

  1. 首先定义需要的视图类,可以是自定义类,也可以是模板类,但是他们的基类必须是视图类
  2. 使用CSplitterWnd类型对象
1 CSplitterWnd m_splitter;
  1. 重写框架类的OnCreateClient方法
 1 // 创建静态拆分窗口,1行2列,CSplitterWnd::CreateStatic
 2     m_spliter.CreateStatic(this, 1, 2); 
 3 // 创建视图:CSplitterWnd::CreateView 
 4 //0, 0 : 放在第0行第0列的位置 
 5 //RUNTIME_CLASS(CSelectView) :需要头文件#include "SelectView.h", CSelectView在SelectView.h中声明 
 6 // CSize(250, 500):指定视图宽度和高度 
 7 //pContext : 为OnCreateClient()最后一个形参
 8     m_spliter.CreateView(0, 0, RUNTIME_CLASS(CSelectView), CSize(200, 500), pContext); 
 9 //0, 1: 放在第0行第1列的位置 
10 //CDispalyView,需要头文件#include "DispalyView.h"
11     m_spliter.CreateView(0, 1, RUNTIME_CLASS(CDispalyView), CSize(600, 500), pContext); 
12 //return CFrameWnd::OnCreateClient(lpcs, pContext); 
13     return TRUE;    

2.8 挂载窗口

 1 CCreateContext   Context; 
 2 //CUserDlg类需要包含头文件#include "UserDlg.h"
 3     Context.m_pNewViewClass = RUNTIME_CLASS(CUserDlg);
 4     Context.m_pCurrentFrame = this; 
 5     Context.m_pLastView = (CFormView *)m_spliter.GetPane(0, 1);
 6     m_spliter.DeleteView(0, 1); 
 7     m_spliter.CreateView(0, 1, RUNTIME_CLASS(CUserDlg), CSize(600,500), &Context); 
 8     CUserDlg *pNewView = (CUserDlg *)m_spliter.GetPane(0, 1);
 9     m_spliter.RecalcLayout(); 
10     pNewView->OnInitialUpdate(); 
11     m_spliter.SetActivePane(0, 1);

一些全局

  1. 加载图标
1 HICON icon = AfxGetApp()->LoadIconW(IDR_XXX); //IDR_XXX是图标模板资源的ID
  1. 设置窗口图标
1 //设置图标,IDI_ICON_WIN为图标资源ID,此为WINAPI函数 
2 //如果是修改主界面图标,则在OnCreate中 
3     SetClassLong(m_hWnd, GCL_HICON,(LONG),AfxGetApp()->LoadIconW(IDI_ICON_WIN)); 
4 //m_hWnd是窗口句柄,只要是CWnd树下的类都具有此成员变量
  1. 设置窗口大小和居中显示
1 MoveWindow(0,0,800,500);
2 CenterWindow();
  1. CTreeView中有CTreeCtrl成员,可以使用GetTreeCtrl()获取
  2. 在SDI程序中以对话框开始,在XXXApp::InitInstance方法中,在CWinApp::InitInstance();前创建模态窗口

一些基于向导的操作

  1. 在资源视图,在具体的资源上右键,添加类、添加变量、类向导
  2. 在属性界面,消息界面可以创建消息对应的向导
  3. 对于菜单编辑,可以在具体的菜单节点上“右键”-“添加事件处理程序”定义菜单节点的点击响应函数
  4. 在添加类时,可以选择修改基类;在添加变量时,有些控件可以选择添加值变量和控件变量
  5. 添加变量时发生的事:
    • 在所属类的声明中声明了对应变量
    • 在所属类定义中,DoDataExchange方法中关联变量和模板
1     DDX_Control(pDX, IDC_COMBO1, m_Cbx); 
2     DDX_Text(pDX, IDC_EDIT1, m_price1);
    • 在所属类的构造函数定义的列表初始化中初始化
  1. 点击控件,然后输入文字,即替换控件的文本;双击控件,会创建并跳转到点击控件的响应函数
  2. 修改SDI程序的主界面标题,其中包含主标题和副标题。
    • 主标题在Doc类中修改,在OnNewDocument方法中使用SetTitle方法
    • 副标题在框架类(Form)中修改,在OnCreate方法中使用SetTitle方法
  1. 继承View创建的类,可能会提示View基类未定义,此时要包含头文件# include
 
 
posted @ 2022-07-07 23:31  幻cat  阅读(227)  评论(0编辑  收藏  举报