概述
- MFC微软基础类库的作用在Windows平台做GUI开发使用
- MFC框架设计思想
Windows消息机制
- SDK 软件开发工具包(Software Development Kit)
SDK是软件开发人员为特定软件包、框架、硬件平台、操作系统等建立引用软件的开发工具的集合。
- API 应用程序编程接口(Application Programming Interface)
WindowsAPI函数是通过C实现的,主要在windows.h
头文件中进行了声明。
- 窗口和句柄
窗口是Windows应用程序中非常重要的元素,一个Windows应用程序至少要有一个窗口,称为主窗口。
窗口时屏幕上的一块矩形区域,是Windows应用程序与用户进行交互的接口,利用窗口可接受用户输入及显示输出。
窗口可分为客户区和非客户区,客户区市窗口的一部分。
窗口可有一个父窗口,有父窗口的窗口称为子窗口。
Windows应用程序中,窗口时通过窗口句柄(HWND)来标识,对窗口的操作时首先要得到窗口句柄。
句柄(HANDLE)是Windows程序的一个重要概念,在Windows程序中,有各种资源如窗口、图标、光标、画刷等。系统在创建这些资源时会为其分配内存,并返回标识资源的标识号即句柄。
- 消息和消息队列
Windows程序设计是一种完全不同于传统DOS的程序设计方法,是一种事件驱动方式的程序设计模式,主要是基于消息的。
每个Windows应用程序开始执行后,系统都会为该程序创建一个消息队列,这个消息队列用来存放该程序创建的窗口的消息。
例如,当用户在窗口中画图时,按下鼠标左键,此时操作系统会感知此事件,于是将事件包装成一个消息,投递到应用程序的消息队列中,等待应用程序的处理。然后,应用程序通过一个消息循环不断地从消息队列中取出消息,并进行响应。在这个处理过程中,操作系统会给应用程序发送消息,实际是操作系统调用程序中一个专门负责处理消息的函数,这个函数成为窗口过程。
- WinMain函数
当Windows操作系统启动一个程序时,它调用的就是该程序的WinMain()
函数,实际上是由插入到可执行文件中的启动代码调用的。
WinMain()
是Windows程序的入口函数,与DOS程序的入口点函数main()
的作用是相同的,但WinMain()
函数结束或返回时,Windows程序结束。
Windows编程模型
完整的Win32程序#include<windows.h>
实现的功能是创建一个窗口,并在该窗口中响应键盘及鼠标消息,程序实现步骤:
- WinMain函数定义
- 创建窗口
- 消息循环
- 编写窗口过程函数
VS2017配置
包含目录的路径,这里以SDk版本10.0.15063.0为例,添加路径如下:
C:\Program Files (x86)\Windows Kits\10\Include\10.0.15063.0\shared
C:\Program Files (x86)\Windows Kits\10\Include\10.0.15063.0\ucrt
C:\Program Files (x86)\Windows Kits\10\Include\10.0.15063.0\um
C:\Program Files (x86)\Windows Kits\10\Include\10.0.15063.0\winrt
库目录的路径,添加路径如下:
C:\Program Files (x86)\Windows Kits\10\Lib\10.0.15063.0\ucrt\x86
C:\Program Files (x86)\Windows Kits\10\Lib\10.0.15063.0\um\x86
windows.c
$ vim windows.c
//底层实现窗口的头文件
#include <windows.h>
/**
* 程序入口函数
* WINAPI 宏 代表 __stdcall,表示参数传递顺序,从右到左依次入栈,在函数返回前清空栈。
* HINSTACE H表示句柄
* HINSTACE hInstance 应用程序实例句柄
* HINSTACE hPrevInstance 上一个应用程序句柄,在win32环境下参数一般为NULL,不起作用。
* LPSTR lpCmdLine 代表 char * argv[]
* int nShowCmd 显示命令,即最大化、最小化、正常
*/
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
{
//1.设计窗口
//2.注册窗口
//3.创建窗口
//4.显示更新
//5.通过循环获取消息
//6.处理消息即窗口过程
//设计窗口
WNDCLASS wc;//Windows类
wc.cbClsExtra = 0;//类的额外内存
wc.cbWndExtra = 0;//窗口额外内存
wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);//设置背景
wc.hCursor = LoadCursor(NULL, IDC_HAND);//设置光标,参数1为NULL为系统提供的光标
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);//设置图标
wc.hInstance = hInstance;//应用程序实例句柄,传入实例句柄
wc.lpfnWndProc = WindowProc;//窗口过程,即回调函数
wc.lpszClassName = TEXT("WIN");//窗口类名
wc.lpszMenuName = NULL;//窗口菜单名称
wc.style = 0;//显示风格 0默认风格
//注册窗口
RegisterClass(&wc);
//创建窗口
// lpClassName 窗口类名
// lpWindowName 窗口标题名称
// dwStyle 窗口风格 混合风格
// x X坐标 默认值 CW_USERDEFAULT
// y Y坐标 默认值 CW_USERDEFAULT
// nWidth
// nHeight 默认值 CW_USERDEFAULT
// hWndParent 父窗口,顶层方式弹出为NULL
// hMenu 窗口菜单
// hInstance 实例句柄
// lpParam 附加值
HWND hwnd = CreateWindow(
wc.lpszClassName,
TEXT("WINDOWS"),
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
NULL,
NULL,
hInstance,
CW_USEDEFAULT
);//todo “CreateWindowExW”: 指针与实参 12 不匹配
//显示更新
ShowWindow(
hwnd,
SW_SHOWNORMAL//展示方式 普通方式
);
UpdateWindow(hwnd);
//通过循环获取消息
/*
每个消息都是一个结构体
HWND hwnd; 主窗口句柄
UINT message; 消息名称
WPARAM wParam; 附件消息 通常为键盘消息
LPARAM lParam; 附加消息 通常为鼠标左右键消息
DWORD time; 消息产生时间
POINT pt; 附加消息 鼠标坐标点消息
*/
MSG msg;
while (GetMessage(&msg, NULL, 0, 0)) {
/*
捕获消息 GetMessage()
_Out_ LPMSG lpMsg;//消息地hi
_In_opt_ HWND hWnd;//捕获窗口 NULL为捕获所有窗口
_In_ UNIT wMsgFilterMin;//最小的过滤消息 0表示捕获所有消息
_In_ UINT wMsgFilterMax;//最大的过滤消息 0表示捕获所有消息
*/
//if (GetMessage(&msg, NULL, 0, 0) == FALSE) {
// break;//若关闭窗口则退出死循环
//}
// 翻译消息
TranslateMessage(&msg);//例如针对键盘组合快捷键需翻译
// 分发消息
DispatchMessage(&msg);
}
return 0;
}
窗口处理过程
/**
* 处理窗口过程
* HWND hWnd 消息所属的窗口句柄
* UINT uMsg 具体的消息名称
* WPARAM wParam 键盘附加消息
* LPARAM lParam 鼠标附加消息
*/
LRESULT CALLBACK WindowProc(
HWND hWnd,
UINT uMsg,
WPARAM wParam,
LPARAM lParam
)
{
//根据不同消息做不同的处理
switch (uMsg) {
//关闭窗口 发送另一个消息WM_DESTROY
case WM_CLOSE:
DestroyWindow(hWnd); //所有xxxWindow为结尾的方法都不会进入消息队列而会直接执行
break;
//关闭窗口退出进程
case WM_DESTROY:
PostQuitMessage(0);
break;
//鼠标左键按下
case WM_LBUTTONDOWN:
{
int xPos = LOWORD(lParam);
int yPos = HIWORD(lParam);
char buf[1024];
wsprintf(buf, TEXT("X = %d Y = %d"), xPos, yPos);//todo 从“char [1024]”到“LPCWSTR”的类型不兼容
MessageBox(hWnd, buf, TEXT("TITLE"), MB_OK);
break;
}
//键盘按下
case WM_KEYDOWN:
MessageBox(hWnd, TEXT("KEYDOWN"), TEXT("TITLE"), MB_OK);
break;
//绘图
case WM_PRINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
TextOut(hdc, 100, 100, TEXT("PRINT"), strlen("PRINT"));
EndPaint(hWnd, &ps);
break;
}
}
//返回值使用默认处理方式
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
MFC
MFC(Microsoft Foundation Classes, 微软基础类库)是微软提供的类库(class libraries),以C++类的形式封装的WindowsAPI,包含一个应用程序框架,以减少应用程序开发人员的工作量。其中类包含大量Windows句柄封装类和Windows内建控件和组件的封装类。
MFC把Windows SDK API函数包装成几百个类,MFC给Windows操作系统提供面向对象的接口,支持可重用性、自包含性以及其他OPP原则。MFC通过编写类来封装窗口、对话框等其他对象,引入关键的虚函数(覆盖虚函数可改变派生类的功能)来完成,MFC设计者使类库带来的总开销降到了最低。
MFC快速入门
- MFC源文件后缀为
.cpp
,因为MFC是C++编写的。 - 编写MFC程序需包含
#include<afxwin.h>
头文件
$ vim MFCApp.h
// MFCApp.h: PROJECT_NAME 应用程序的主头文件
#pragma once
#ifndef __AFXWIN_H__
#error "在包含此文件之前包含“stdafx.h”以生成 PCH 文件"
#endif
// 主符号
#include "resource.h"
// CMFCAppApp:
// 有关此类的实现,请参阅 MFCApp.cpp
//应用程序类CWinApp派生类(子类)
class CMFCAppApp : public CWinApp
{
public:
CMFCAppApp();
// 重写
public:
//基类的虚函数,派生类只是重写,MFC程序的入口地址。
virtual BOOL InitInstance();
// 实现
//声明消息映射,必须在类声明中。
DECLARE_MESSAGE_MAP()
};
extern CMFCAppApp theApp;
MFC消息映射
消息映射是将消息和成员函数相互关联的表,例如框架窗口接收一个鼠标左击消息,MFC将搜索该窗口的消息映射,如果存在一个处理WM_LBUTTONDOWN
消息的处理程序,就调用OnLButtonDown
。
//声明消息映射,必须在类声明中。
DECLARE_MESSAGE_MAP()
// 定义消息宏,必须在类实现中。
BEGIN_MESSAGE_MAP(CMFCAppApp, CWinApp)
ON_COMMAND(ID_HELP, &CWinApp::OnHelp)
END_MESSAGE_MAP()
Windows 字符集
- 多字节中1个字符对应1个字节
- 中文 1个字符对应多个字节
- 宽字节 Unicode
- 多字节转为宽字节
L"test"
TEXT()
具有自适应编码转码TCHER()
具有自适应编码转码
统计字符串长度
//统计多字节长度
int num = 0;
char * p = "test";
num = strlen(p);
//统计宽字节字符串
wchar_t *p = L"test";
int num = wcslen(p);
char *
与CString
之间的转换
// char *转CString
char * p = "test";
CString str = CString(p);
//CString转char *
CString str = CString("test");
CStringA tmp;
tmp = str;
char * p = tmp.GetBuffer();
- MFC中后缀为
Ex
的函数都是扩展函数 - MFC中以
Afx
为前缀的都是全局函数
MFC基于对话框编程
对话框是一种特殊类型的窗口,大多数Windows程序都是通过对话框与用户进行交互。
模态对话框
- 创建对话框
资源视图>Dialog>右键>插入Dialog>重命名为 IDD_EXEC - 为对话框添加类
IDD_EXEC>右击>添加类>类命名为CDlgExec 头文件命名为DlgExec - 为按钮添加事件处理程序
// TODO: 在此添加控件通知处理程序代码
void CMfcDlg::OnBnClickedLogin()
{
//弹出模态对话框,具有堵塞功能。
CDlgExec dlg;
dlg.DoModal();
}
非模态对话框
- 创建对话框
资源视图>Dialog>右键>插入Dialog>重命名为 IDD_Show - 为对话框添加类
IDD_EXEC>右击>添加类>类命名为CDlgShow 头文件命名为DlgShow - 主对话框头文件添加私有属性
$ vim MfcDlg.h
#include "DlgShow.h"
// 类中添加私有属性
private:
CDlgShow dlg;
- 主对话框消息处理程序初始化中添加创建非模态框对话框
$ vim MfcDlg.cpp
BOOL CMfcDlg::OnInitDialog()
{
//创建非模态对话框
dlg.Create(IDD_SHOW);
}
- 事件处理程序中添加显示
// 控件通知处理程序代码
void CMfcDlg::OnBnClickedLogin()
{
//弹出非模态对话框
//CDlgShow dlg;
//dlg.Create(IDD_SHOW);//创建
dlg.ShowWindow(SW_SHOWNORMAL);//显示
}
静态文本 CStaticText
- 添加变量 以
STATIC
为结尾的ID是不可以添加变量的需修改ID - 设置内容
SetWindowTextW()
- 获取内容
GetWindowTextW()
void CMfcDlg::OnBnClickedSet()
{
text.SetWindowTextW(TEXT("Account"));
btnSet.SetWindowTextW(TEXT("禁用"));
btnSet.EnableWindow(FALSE);//禁用按钮
}
void CMfcDlg::OnBnClickedGet()
{
CString str;
text.GetWindowTextW(str);
MessageBox(str);
}
静态图片BMP
- 添加变量
- 初始化显示图片
//显示图片
img.ModifyStyle(0xf, SS_BITMAP | SS_CENTERIMAGE);//设置风格为16进制位图并居中显示
//获取图片路径获取bitmap句柄
#define HBMP(filepath, width, height)(HBITMAP)LoadImage(AfxGetInstanceHandle(), filepath, IMAGE_BITMAP, width, height, LR_LOADFROMFILE|LR_CREATEDIBSECTION)
//宽高按空间大小设置
CRect rect;
img.GetWindowRect(rect);
//设置bitmap
img.SetBitmap(HBMP(TEXT("./Image/plane.bmp"), rect.Width(), rect.Height()));
编辑框 EditControl
- 属性:
mutiline
多行、want return
换行 - 获取设置值:
GetWindowTextW()
和SetWindowText()W
- 单行点击回车会退出,重写
OnOK()
注释其中代码。对话框 - 退出当前对话框
CDialog::OnOk()
和CDialog::OnCancel()
// 源编辑框默认文本
editor_src.SetWindowTextW(TEXT("please input something"));
//点击复制按钮
void CMfcDlg::OnBnClickedBtnCopy()
{
CString str;
editor_src.GetWindowTextW(str);
editor_dst.SetWindowTextW(str);
}
退出的方式
void CMfcDlg::OnBnClickedExit()
{
//退出当前对话框
CDialog::OnOK();//点击确定退出对话框
//CDialog::OnCancel();//点击取消推出对话框
//exit(0);//退出所有对话框
}
添加变量时添加value
,本身关联的变量就是值。
UpdateData(TRUE);//将控件中的内容同步到变量中
void CMfcDlg::OnBnClickedBtnSetval()
{
editval = TEXT("setval");
UpdateData(FALSE);//将变量中的值同步到控件中
}
void CMfcDlg::OnBnClickedBtnGetval()
{
UpdateData(TRUE);//将控件中的内容同步到变量中
MessageBox(editval);
}
下拉框CComBox
data
属性中添加数据,逗号分隔。sort
属性排序。type
属性为DropList
则不可编辑- 添加元素
AddString()
- 删除元素
DeleteString()
- 插入元素
InsertString()
- 设置默认
SetCurSel()
- 获取当前索引
GetCurSel()
- 根据索引获取元素
GetLBText(int Index, CString str)
- 控件事件
OnCbnSelChangeXXX()
//下拉框添加元素
role.AddString(TEXT("管理员"));
role.AddString(TEXT("渠道商"));
role.AddString(TEXT("代理商"));
role.AddString(TEXT("员工"));
role.AddString(TEXT("玩家"));
//设置默认选项
role.SetCurSel(0);
//插入选项
role.InsertString(4, TEXT("会员"));
//删除选项
role.DeleteString(3);
//获取索引所对应的值
CString str;
role.GetLBText(1, str);
//MessageBox(str);
void CMfcDlg::OnCbnSelchangeComboRole()
{
//获取当前索引
int index = role.GetCurSel();
//获取值
CString str;
role.GetLBText(index, str);
MessageBox(str);
}
列表控件 CListCtrl
- 控件属性
view
设置为报表模式Report
- 添加表头
InsertColumn()
- 添加正文 从0开始
InsertItem(0,content)
- 添加正文其他列表
SetItemText(row, col, content)
- 设置风格 整行选中
LVS_EX_FULLROWSELECT
网格显示LVS_EX_GRIDLINES
/*列表控件*/
//设置属性 整行选中 网格显示
list.SetExtendedStyle(list.GetExtendedStyle()|LVS_EX_FULLROWSELECT|LVS_EX_GRIDLINES);
//设置表头
CString strArr[] = {TEXT("账号"), TEXT("昵称"), TEXT("邮箱")};
for (int i = 0; i < 3; i++) {
list.InsertColumn(i, strArr[i], LVCFMT_LEFT, 100);//参数 索引 内容 对齐 列宽
}
list.InsertItem(0, TEXT("a12l31c09e"));
list.SetItemText(0, 1, TEXT("Alice"));
list.SetItemText(0, 2, TEXT("alice520@gmail.com"));
//循环添加
int j;
for (int i = 0; i < 10; i++) {
j = 0;
CString str;
str.Format(TEXT("alice%d"), i);
list.InsertItem(i, str);
list.SetItemText(i, ++j, str);
list.SetItemText(i, ++j, str);
}
树形控件Tree Control
- 拖拽控件,命名ID为
IDC_TREE
- 设置私有
private
变量CTreeCtrl tree;
- 设置属性:
Has Buttons
设置按钮、Has Lines
设置线、Lines At Root
设置根节点线 - 准备图标:资源视图>Icon>右键>添加资源>Icon>导入,图标采用ico文件72像素。
- 初始化中设置图标
CMfcDlg::OnInitDialog()
//设置图标
//CImageList imglist;//图片集合,必须保存.h作为成员属性。
imglist.Create(30, 30, ILC_COLOR32, 6, 6);
//添加图标
HICON icons[6];
icons[0] = AfxGetApp()->LoadIconW(IDI_ICON1);
icons[1] = AfxGetApp()->LoadIconW(IDI_ICON2);
icons[2] = AfxGetApp()->LoadIconW(IDI_ICON3);
icons[3] = AfxGetApp()->LoadIconW(IDI_ICON4);
icons[4] = AfxGetApp()->LoadIconW(IDI_ICON5);
icons[5] = AfxGetApp()->LoadIconW(IDI_ICON6);
for (int i = 0; i < 6; i++) {
imglist.Add(icons[i]);
}
//设置图标
tree.SetImageList(&imglist, TVSIL_NORMAL);
//设置根节点
HTREEITEM root = tree.InsertItem(TEXT("ROOT"), 0, 1, NULL);
//设置父节点
HTREEITEM parent = tree.InsertItem(TEXT("PARENT"), 2, 3, root);
//设置子节点
HTREEITEM child1 = tree.InsertItem(TEXT("CHILD1"), 4, 5, parent);
HTREEITEM child2 = tree.InsertItem(TEXT("CHILD2"), 4, 5, parent);
//设置默认选项
tree.SelectItem(child1);
- 设置事件
OnTvnSelchanged
//树项目切换
void CMfcDlg::OnTvnSelchangedTree(NMHDR *pNMHDR, LRESULT *pResult)
{
LPNMTREEVIEW pNMTreeView = reinterpret_cast<LPNMTREEVIEW>(pNMHDR);
*pResult = 0;
//获取当前选中项
HTREEITEM item = tree.GetSelectedItem();
CString text = tree.GetItemText(item);
MessageBox(text);
}
标签页Tab Control
- 引用
TabSheet.h
和TabSheet.cpp
并添加到项目中
TabSheet.h
#if !defined(AFX_TABSHEET_H__42EE262D_D15F_46D5_8F26_28FD049E99F4__INCLUDED_)
#define AFX_TABSHEET_H__42EE262D_D15F_46D5_8F26_28FD049E99F4__INCLUDED_
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
// TabSheet.h : header file
//
/////////////////////////////////////////////////////////////////////////////
// CTabSheet window
#define MAXPAGE 16
class CTabSheet : public CTabCtrl
{
// Construction
public:
CTabSheet();
// Attributes
public:
// Operations
public:
// Overrides
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CTabSheet)
//}}AFX_VIRTUAL
// Implementation
public:
int GetCurSel();
int SetCurSel(int nItem);
void Show();
void Free();
void SetRect();
BOOL AddPage(LPCTSTR title, CDialog *pDialog, UINT ID);
virtual ~CTabSheet();
// Generated message map functions
protected:
LPCTSTR m_Title[MAXPAGE];
UINT m_IDD[MAXPAGE];
CDialog* m_pPages[MAXPAGE];
int m_nNumOfPages;
int m_nCurrentPage;
//{{AFX_MSG(CTabSheet)
afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};
/////////////////////////////////////////////////////////////////////////////
//{{AFX_INSERT_LOCATION}}
// Microsoft Visual C++ will insert additional declarations immediately before the previous line.
#endif // !defined(AFX_TABSHEET_H__42EE262D_D15F_46D5_8F26_28FD049E99F4__INCLUDED_)
TabSheet.cpp
// TabSheet.cpp : implementation file
//
#include "stdafx.h"
#include "TabSheet.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
/////////////////////////////////////////////////////////////////////////////
// CTabSheet
CTabSheet::CTabSheet()
{
m_nNumOfPages = 0;
m_nCurrentPage = 0;
}
CTabSheet::~CTabSheet()
{
}
BEGIN_MESSAGE_MAP(CTabSheet, CTabCtrl)
//{{AFX_MSG_MAP(CTabSheet)
ON_WM_LBUTTONDOWN()
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CTabSheet message handlers
BOOL CTabSheet::AddPage(LPCTSTR title, CDialog *pDialog, UINT ID)
{
if (MAXPAGE == m_nNumOfPages)
return FALSE;
m_nNumOfPages++;
m_pPages[m_nNumOfPages - 1] = pDialog;
m_IDD[m_nNumOfPages - 1] = ID;
m_Title[m_nNumOfPages - 1] = title;
return TRUE;
}
void CTabSheet::SetRect()
{
CRect tabRect, itemRect;
int nX, nY, nXc, nYc;
GetClientRect(&tabRect);
GetItemRect(0, &itemRect);
nX = itemRect.left;
nY = itemRect.bottom + 1;
nXc = tabRect.right - itemRect.left - 2;
nYc = tabRect.bottom - nY - 2;
m_pPages[0]->SetWindowPos(&wndTop, nX, nY, nXc, nYc, SWP_SHOWWINDOW);
for (int nCount = 1; nCount < m_nNumOfPages; nCount++)
m_pPages[nCount]->SetWindowPos(&wndTop, nX, nY, nXc, nYc, SWP_HIDEWINDOW);
}
void CTabSheet::Show()
{
for (int i = 0; i < m_nNumOfPages; i++)
{
m_pPages[i]->Create(m_IDD[i], this);
InsertItem(i, m_Title[i]);
}
m_pPages[0]->ShowWindow(SW_SHOW);
for (int i = 1; i < m_nNumOfPages; i++)
m_pPages[i]->ShowWindow(SW_HIDE);