如果你是在Windows 平台上做GUI开发,MFC (微软基础类库)是一个很好的选择,毕竟Windows累计用户群庞大,市场接收程度高。
但是,学习MFC 不仅仅要学习用MFC,还要学习MFC的框架设计思想。
很多公司在一些做了很久的项目上,往往都是有自己的类库、自己的框架,我们只需要在其基础上不断的完善和扩展。如果你不了解类库,你是根本无从下手。这也是我们学习类库、框架设计的原因。
如果仅仅会用MFC的话,可能在找工作的时候,一旦工作内容离开了MFC,就什么也不会了。学习任何东西都是一样的,学的是方法,而不仅仅是某个技术本身,因为技术肯定不停地更新的,你今天学的现在能用上,但是谁也不能保证以后会怎么样,但是万变不离其宗,懂得了方法,学什么都一样。
MFC六大关键技术包括:
MFC Initialization —— MFC程序的初始化过程
RTTI(Runtime Type Information)—— 运行时类型识别
Dynamic Creation —— 动态创建
Persistence ——永久保存(串行化、序列化)
Message Mapping —— 消息映射
Message Routing —— 消息传递
要想熟练掌握Window 应用程序的开发,首先需要理解Windows 平台下程序运行的内部机制,如果想要更好的学习掌握MFC,必须要先了解Windows 程序的内部运行机制,为我们扫清学习路途中的第一个障碍,为进一步学习MFC 程序打下基础。
1)SDK 和 API
SDK : 软件开发工具包(software development kit),第三方写好的东西
API 函数: Windows 操作系统提供给应用程序的接口(Application programming interface)
常用的头文件:
afx.h 将各种mfc头文件包含在内
afwin.h 包含了各种mfc窗口类。包含了afx.h和windows.h
afext.h 提供了扩展窗口类的支持,例如工具栏,状态栏等
2)窗口和句柄
窗口:标题栏、菜单栏、系统菜单、最小化框、最大化框、可调边框
句柄:就是各种各样的资源(窗口、图标、光标等),系统创建这些资源时,会为它们分配内存,并返回标识这些资源的标识号
3)消息与消息队列
操作系统-》消息队列-》应用程序-》分发消息-》操作系统-》窗口函数(回调函数)
消息映射
4) WinMain 函数(参数传递顺序, 从右至左,以此入栈)
当 Windows 操作系统启动一个程序时,它调用的就是该程序的WinMain 函数(实际是由插入到可执行文件中的启动代码调用的)。
WinMain 是 Windows 程序的入口函数,与DOS 程序的入口函数 main 的作用相同,当 WinMain 函数结束或者返回时,Windows 应用程序结束。
经验之谈:
以Afx开头可以确定为MFC 库中的全局函数
以 ::(匿名空间) 开头可以确定为win32 的API 函数, 库函数可以直接调
一个完整的Win32 程序(#include
#include
//6、处理消息(窗口过程)
LRESULT CALLBACK WindowPro( HWND hwnd,
UINT uMsg,
WPARAM wParam,
LPARAM lParam)
{
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
//传参的传递顺序,从右至左 以此入栈,并且在函数返回前 清空堆栈
int WINAPI WinMain(
HINSTANCE hInstance,//应用程序实例句柄
HINSTANCE hPrevInstance,//上一个应用程序句柄,在win32环境下,参数一般为NULL,不起作用了
LPSTR lpCmdLine, //char* argv[]
int nShowCmd)//显示命令 最大化,最小化
{
//1、设计窗口
WNDCLASS wc;
wc.cbClsExtra = 0;//类的额外的内存
wc.cbWndExtra = 0;//窗口额外的内存
wc.hbrBackgroud = (HBRUSH)GetStockObject(WHITE_BRUSH); // 设置背景
wc.hCursor = LoadCursor(NULL, IDC_HAND);//设置光标
wc.hIcon = LoadIcon(NULL, IDI_ERROR);//图标
wc.hInstance = hInstance;//应用程序实例句柄, 传入winMain中的形参即可
wc.lpfnWndProc = WindowProc;//回调函数 窗口过程
wc.lpszClassName = TEXT("WIN");//指定窗口类名称
wc.lpszMenuName = NULL;//菜单名称
wc.style = 0; //显示风格 0 代表默认风格
//2、注册窗口
RegisterClass(&wc);
//3、创建窗口
CreateWindow(wc.lpszClassName, TEXT("WINDOWS"), WS_OVERLAPPEDWINDOW);
//4、显示和更新
ShowWindow(hwnd, SW_SHOWNORNAL);
UpdateWindow(hwnd);
//5、通过循环取消息
MSG msg;
while (1)
{
if (GetMessage(&msg, NULL, 0, 0) == FALSE)
{
break;
}
//翻译消息
TranslateMessage(&msg);
//分发消息
DispatchMessage(&msg);
}
return 0;
}
创建钩子
HHOOK SetWindowsHookEx(
int idHook, //钩子类型(WH_CBT)窗口创建消息
HOOKPROC lpfn, //钩子处理函数
HINSTANCE hMod, // 应用程序实例句柄
DWORD dwThreadId //线程ID
);
钩子处理函数
LRESULT CALLBACK CBTProc(
int nCode, //钩子码(HCBT_CREATEWND)
WPARAM wParam, //刚刚创建成功窗口句柄
LPARAM lParam //...
);
更改窗口处理函数
LONG_PYR SetWindowLongPtr(
HWND hWnd, //窗口句柄
int nIndex, //GWLP_WNDPROC
LONG_PTR dwNewLong //新的窗口处理函数名(函数地址)
);
mfc 头文件 afxwin.h
自定义类 继承 与 CWinApp 应用程序类 MyApp app 应用程序对象,有且仅有一个
程序如果 InitInstance
入口里 创建窗口
窗口类 MyFrame 继承于 CFrameWnd
MyFrame 构造中 Create (NULL, 标题)
创建窗口对象
显示 和 更新
单文档视图架构程序
CWinApp - 应用程序类,负责管理应用程序的流程
CFramWnd - 框架窗口类,负责管理框架窗口
CView - 视图窗口类, 负责显示数据
CDocument - 文档类, 负责管理数据
多文档视图架构程序
CWinApp - 应用程序类
CMDIFrameWnd - 多文档主框架窗口类
CMDChildWnd - 多文档子框架窗口类
CView - 视图窗口类,显示数据
CDocument - 文档类, 管理数据
对话框应用程序
CWinApp - 应用程序类
CDialog - 对话框窗口类
消息映射是一个将消息和成员函数相互关联的表。比如,框架窗口接收到一个鼠标左击消息,MFC 将搜索该窗口的消息映射,如果存在一个处理 WM_LBUTTONDOWN 消息的处理程序,如果存在一个处理WM_LBUTTONDOWN 消息的处理程序,然后就调用OnLButtonDown。
类内声明宏
DECLARE_MESSAGE_MAP()
类外实现宏
BEGIN_MESSAGE_MAP()//开始
END_MESSAGE_MAP()//结束
CString str;
str.Format(TEXT(“x = %d, y = %d”), point x, point y);
5) 利用向导创建
Visual C++ -》 MFC 应用 -》单个文档,MFC 标准
创建生成四个文件:
Cmfc_GuideView 显示 相片,覆盖相框 视类
Cmfc_GuideApp
Cmfc_GuideDoc
CMainFrame
视图-》类视图(上边框双击显示.h, 下边框双击显示.cpp)
1、添加事件: 上边框 选中 右键 属性(消息 、 事件) => 可添加可删除
左键点击: WM_LBUTTONDOWN
MainFrame 相当于相框 在这个里面添加消息 会被 View类 给覆盖掉
View 相当于相片
注释快捷键: 选中你要注释的内容 ctrl + k, ctrl + c
取消注释快捷键: 选中你要注释的内容 ctrl + k, ctrl + u
实例代码:显示编辑框坐标
CString str;
str.Format(TEXT(“x = %d, y = %d”), point.x, point.y);
MessageBox(str);
画图: view 类 里面有两个 OnDraw 、 OnPaint
OnDraw:
pDC->TextOutW(100, 100, TEXT(“为了部落”));//显示位置 + 内容
OnPaint: (消息创建)
dc->TextOutW(100, 100, TEXT(“为了联盟”));//显示位置 + 内容
OnCreate 与 Create 的区别:
Create() 负责注册并产生窗口, 像动态创建控件中的Create() 一样,窗口创建之后会向操作系统发送 WM_CREATE 消息。
OnCreate() 不产生窗口,只是在窗口显示前设置窗口的属性如风格、位置等。
OnCreate() 是消息WM_CREATE 的消息响应函数。
创建窗口:Create() -》 发送消息:WM_CREATE -》 响应消息:On_Create()
MFC 中后缀名为 Ex 的函数 都是扩展函数
在 MFC 中, 以 Afx 为前缀的函数都是全局函数, 可以在程序的任何地方调用它们。
复习:
TEXT自适应编码的转换
统计字符串长度 strlen()
统计宽字节的字符串长度 wcslen()
char * 与 CString 之间的转换
char * p3 = “ccc”;
CString str = CString(p3);
CStringA tmp;
tmp = str;
char* pp = tmp.GetBuffer();
==================================================
string 与 char* .c_str()
对话框是一种特殊类型的窗口,绝大多数Windows 程序都通过对话框与用户进行交互,在Visual C++中,对话框即可以单独组成一个简单的应用程序,又可以成为文档/视图结构程序的资源。
模态对话框:创建出来框之后,不能对其他的对话框进行出来(有关联)
非模态对话框:创建出来框之后,可以对其他对话框进行操作(之间没有关联)
工程创建: 基于对话框-》 MFC 标准-》后续操作默认即可
生成两个文件:
CMy01DiagApp 继承于 CVinApp 应用程序类
CMy01DiagDlg 对话框类 也是我们的视类
双击 .rc 文件 -》 资源视图 选择UI 界面 -》 右键 -》属性
Caption 对话框标题
Button 按钮 修改名称:1、右键属性 -》 Caption 2、直接修改(双击无法编辑按钮名称,会创建消息)
模态对话框 ID : IDD_EXEC
非模态对话框 ID : IDD_Show
再创建一个对话框:资源视图 -》 Diaglog -》 插入 Diaglog(E) -》 选择IDD_DIALOG2 -》 修改ID
现在还只是设计完成UI 界面, 还得添加 cpp, h 文件
一个总的对话框,两个按钮:模态对话框,非模态对话框
模块对话框 -》 UI 界面右键 -》添加类 -》类名: CDlgEXEC -》完成
会在类视图中,生成一个 CDlgExec 类
总的对话框:(添加事件的三种方式)
1、UI界面右键 属性-》 控件事件(闪电图标) -》BN_CLICKED
2、右键-》添加事件处理程序(A)
3、直接双击
//第一步,引用头文件 CDlgExec.h
//第二步,在处理事件中添加程序
CDlgExec dlg;//阻塞
dlg.DoModal();
也得将另一个非模态对话框 添加类 CDlgShow
双击主对话框中的,非模态对话框的按钮,添加处理事件:
//CDiaglogDlg.cpp 中 添加头文件
CDlgShow dlg;//局部变量,非阻塞,出现的现象是一闪而过
//解决方法:作为成员变量,或者new 一个
dlg.Create(IDD_Show);//创建窗口只能创建一次,多次会崩溃
//解决方法:放到构造函数中,或者初始化函数中CDialogDlg.cpp -》 OnInitDialog() -》 在此添加额外的初始化代码
dlg.ShowWindow(SW_SHOWNORMAL);//显示
***补充知识:设定项目启动项
解决方案01 CDiaglog -》 属性 -》 当前选定内容
静态文本控件:Static Text
修改标签文本:1、右键 属性 -》 Caption 2、直接写
UI界面布局:
1、添加一个标签,名称:呵呵
2、添加两个按钮: 一个命名为 哈哈, 另一个命名为 呵呵
标签: 添加变量,ID_STATIC 不能添加变量 需要改 ID名 ID_TEXT,不以 STATIC 结尾即可
两个按钮:添加处理事件
添加变量为 Control 访问权限 private 变量名:m_text
//设置标签文本内容:
m_text.SetWindowTextW(TEXT("呵呵"));
//获取标签文本内容:
CString str;
m_text.GetWindowTextW(str);
//用static text 设置图片
m_pic.ModifyStyle(0xf, ss_bitmap | ss_centerimage);
#define HBMP(filepath, height) (HBITMAP)LoadImage(AfxGetInstanceHandle(), filepath, IMAGE_BITMAP, width, height, LR_LOADFROMFILE | LR_CREATEDIBSECTION)
//宽高设置
CRect rect;
m_pic.GetWindowRect(rect);
//静态控件设置bitmap
m_pic.SetBitmap(HBMP(TEXT("./1.bmp"), m_pic.Width(), m_pic.Height()));
//设置点击状态
m_btn.EnableWindow(FALSE);//禁用
编辑框:Edit Control
单行和多行都是这个控件
输入东西之后回车,会关闭窗口,这算是一个bug
解决方法:类视图 -》 属性 -》重写(添加On_OK)-》进行注释即可
设置多行: Multiline True
想要回车换行:Want Return True
水平无限输 Auto HScroll True
垂直无限输 Auto VScroll True
水平滚动条 Horizontal Scroll
垂直滚动条 Vertical Scroll
类向导: 可以查看所有变量的名称
exit(0); // 退出程序
//退出当前对话框
//CDiaglog::OnOK();
编辑框:使用方式2
编辑框 -》 右键属性 -》添加变量 -》 类别 Value 变量类型 CString 访问 Private
//设置内容
m_text = TEXR(“哈哈”);
//将变量内容 同步到控件中
UpdateData(FALSE);
===============================
//将控件内容 同步到变量中
UpdateData(TRUE);
//获取内容
MessageBox(m_text);
下拉框 Combo Box
添加选项:属性 Data 用分号进行分割
不允许编辑:属性 Type Drop List
排序: 属性 Sort
右键-》添加变量-》 Control CComBox 点击完成
初始化:
m_cbx.AddString(TEXT(“唐僧”));//添加下拉框
m_cbx.AddString(TEXT(“猪八戒”));
m_cbx.AddString(TEXT(“沙僧”));
m_cbx.AddString(TEXT(“孙悟空”));
m_cbx.SetCurSel(0);//设置默认选项
m_cbx.InsertString(4, TEXT(“白龙马”));//插入 插到最后
m_cbx.DeleteString(2);//删除 删除沙僧
CString str;
m_cbx.GetLBText(1, str);//获取下标索引值的具体内容
MessageBox(str);
//更换选项 触发事件 CBN_SELCHANGE
int index = m_cbx.GetCurSel();//获取当前索引位置
CString str;
m_cbx.GetLBText(index , str);//获取下标索引值的具体内容
MessageBox(str);
列表 list control
显示方式: 属性 View Report(报表模式) 、Icon、List
添加变量 -》 control
CString str[] = {TEXT(“姓名”), TEXT(“性别”),TEXT(“年龄”)};
for(int i = 0; i < 3; ++i)
{
//设置表头 参数:1 索引 2 内容 3 对齐方式 4 列宽度
m_list.InsertColumn(i, str[1], LVCFMT_LEFT, 100);
}
//设置正文
//表头不算正文里的内容, 索引从0 开始
m_list.InsertItem(0, TEXT(“张三”));
//给这个Item 插入其他列的值
m_list.SetItemText(0, 1, Text(“男”));
m_list.SetItemText(0, 2, Text(“23”));
//设置属性 整行选中状态 显示网格
m_list.SetExtendedStyle(m_list.GetExtendedStyle() | LVS_EX_FULLROWSELECT | LVS_EX_GRIDLINES)
树控件 CTreeControl
类似于以前QQ 用户名的一个排列
Has Lines 有虚线连接
Has Buttons 有这个折叠的小按钮
Lines At Root 根节点也要有这个线
添加变量 -> control
Icon ->添加资源 -》ICON -> 导入
//1、设置图标
HICON icons[4];
icons[0] = AfxGetApp()->LoadIconW(IDI_ICON1);
icons[1] = AfxGetApp()->LoadIconW(IDI_ICON2);
icons[2] = AfxGetApp()->LoadIconW(IDI_ICON3);
icons[3] = AfxGetApp()->LoadIconW(IDI_ICON4);
CImageList list;//需要作为成员变量才能显示图片
//创建图片集合
list.Create(30 , 30, ILC_COLOR32, 4, 4);
//添加具体图片
for(int i = 0; i < 4; ++i)
{
list.Add(icons[i]);
}
m_tree.SetImageList(&list, TVSIL_NORMAL);
//2、设置节点
HTREEITEM root = m_tree.InsertItem(TEXT(“根节点”), 0,0,NULL);
HTREEITEM parent = m_tree.InsertItem(TEXT(“父节点”), 1,1,root );
HTREEITEM sub1= m_tree.InsertItem(TEXT(“子节点”), 2,2,parent );
HTREEITEM sub2= m_tree.InsertItem(TEXT(“子节点”), 3,3,parent );
//设置默认选项
m_tree.SelectItem(sub1);
//切换选项触发事件
UI 控件-》属性 -》控件事件-》TVN_SELCHANGED
//获取当前项
HTREEITEM item = m_tree.GetSelectedItem();
CString name = m_tree.GetItemText(item);
标签页 TabControl
创建 对话框 DIALOG1
取出边框 Border: None
显示方式 style: child
类似模态与非模态
ui 设计: 创建两个对话框,和一个tab 页
两个对话框 需要创建类,并且设置边框,以及出现方式
而tab 页是 创建变量
m_tab.AddPage(TEXT(“系统管理”), &dlg1, IDD_DLALOG1);
m_tab.AddPage(TEXT(“系统设置”), &dlg2, IDD_DLALOG2);
//显示
m_tab.Show();
创建项目
配置项目
图标的配置
由于微软在VS2013 中不建议再使用 C/C++ 的传统函数 scanf、strcpy等,所以直接使用这些库函数会提示 C4996 错误。 加宏就可以解决这样的问题 _CRT_SECURE_NO_WARNINGS 预定义添加
设置图标(相册上 MainFrm.cpp)
SetClassLong(m_hWnd, GCL_HICON, (LONG)AfxGetApp()->LoadIconW(IDI_ICON_WIN));
设置标题
setTitle(TEXT(“销售管理系统”));// mainFrm 右标题
setTitle(TEXT(“销售管理系统”));//CSaleSystemDoc -> OnNewDocument() 左标题
//设置窗口大小及位置
MoveWindow(0, 0, 800, 500);
//设置居中显示
CenterWindow();
创建一个登陆对话框:编辑框 用户 user ,密码 password,登陆按钮,取消按钮
同步到控件中
UpdataData(FALSE);
控件同步到变量
UpdataData(TRUE);
初始化 对话框里面的内容:
CLOGOIN-> 属性 重写 ->OnInitDialog()
解决编辑框点击回车直接进入的问题:
CLOGOIN->属性 重写 -》 OnOK()
解决点击× 按钮,直接显示界面问题:
CLOGOIN->属性 消息-》 WM_CLOSE -> 直接退出 exit(0);
静态拆分窗口:
自定义两个类; CSelectView 和 CDisplayView