• MFC基础入门


    1 MFC入门

    1.1 为什么学习MFC

    Windows平台上做GUI开发,MFC是一个不错的选择。

    学习MFC不仅可以学习到MFC本身,而且可以学习MFC框架的设计思想。

    1.2 Windows消息机制

    基本概念解释

    SDK:软件开发工具包(Software Development Kit),一般都是一些被软件工程师用于为特定的软件包、软件框架、硬件平台、操作系统等建立应用软件的开发工具的集合。

    API函数:Windows操作系统提供给应用程序编程的接口。Windows应用程序API是通过C语言实现的,所有主要的Windows函数都在Windows.h头文件中进行了声明。Windows操作系统提供了1000多种API函数。

    窗口:一个Windows应用程序至少有一个窗口,称为主窗口。窗口是屏幕上的一块矩形区域,是Windows应用程序与用户进行交互的接口。一个应用程序窗口通常都包含标题栏、菜单栏、系统菜单、最小化框、最大化框、可调边框,有的还有滚动条。窗口可分为客户区和非客户区。窗口可以有一个父窗口,有父窗口的称为子窗口。

    在Windows应用程序中,窗口是通过窗口句柄来标识的。要对某个窗口进行操作,首先就要得到这个窗口的句柄。

    句柄:在Windows程序中,有各种各样的资源(窗口、图标、光标、画刷等),系统在创建这些资源时会为它们分配内存,并返回这些资源的标识号,即句柄。例如,有图标句柄、光标句柄、画刷句柄。

    消息与消息队列:Windows程序设计是一种完全不同于传统DOS方式的程序设计方法。它是一种事件驱动方式的程序设计模式,主要基于消息。

    每一个Windows应用程序开始执行后,系统会为该程序创建一个消息队列,这个消息队列用来存放该程序创建的窗口的消息。

    例如,用户在窗口中画图时,按下鼠标左键。此时,操作系统会感知到这一事件,于是将该事件包装成一个消息,投递到应用程序的消息队列中,等待应用程序的处理。

    然后,应用程序通过一个消息循环不断从消息队列中取出消息,并进行响应。在这个处理过程中,操作系统也会给应用程序“发送消息”。所谓“发送消息”,实际上是操作系统调用程序中一个专门负责处理消息的函数,这个函数称为窗口过程。

    WinMain函数:当Windows操作系统启动一个程序时,它调用的是该程序的WinMain函数(实际上是由插入到可执行文件中的启动代码调用的)。Winmain是Windows程序的入口点函数,与DOS程序的入口点函数main作用相当,当WinMain函数结束或返回时,Windows应用程序结束。

    Windows编程模型

    一个简单但完整Win32程序(#include <windows.h>),该程序实现的功能是创建一个窗口,并在该窗口中响应键盘及鼠标消息,程序实现步骤为:

    • WinMain函数的定义
    int WINAPI WinMain(
    	HINSTANCE hInstance,    // 应用程序实例句柄
    	HINSTANCE hPrevInstace, // 上一个应用程序实例,在win32环境中,默认为NULL
    	LPSTR lpCmdLine,        // 命令行参数 char * argv[]
    	int nShowCmd            // 窗口显示样式
    )
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 创建一个窗口
    // 1.设计窗口
    WNDCLASS wc;                                            // 窗口类变量
    wc.cbClsExtra = 0;                                      // 类的额外内存
    wc.cbWndExtra = 0;                                      // 窗口的额外内存
    wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); // 设置背景
    wc.hCursor = LoadCursor(NULL, IDC_HAND);                // 设置光标,若第一个参数为NULL,代表使用系统提供的光标
    wc.hIcon = LoadIcon(NULL, IDI_ERROR);                   // 设置图标,若第一个参数为NULL,代表使用系统提供的光标
    wc.hInstance = hInstance;                               // 应用程序实例句柄,为WinMain第1个形参
    wc.lpfnWndProc = WinProc;	                            // 回调函数 窗口过程函数名字
    wc.lpszClassName = TEXT("MyWin");	                    // 类的名字
    wc.lpszMenuName = NULL;	                                // 没有菜单
    wc.style = 0;	                                        // 显示风格,填0,使用默认风格
    
    // 2.注册窗口类
    RegisterClass(&wc);
    
    // 3.创建窗口
    HWND  hWnd = CreateWindow(TEXT("MyWin"), TEXT("测试"), WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL);
    
    // 4.显示及更新
    ShowWindow(hWnd, SW_SHOWNORMAL);
    
    UpdateWindow(hWnd);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    HWND CreateWindow(
    	LPCTSTR lpClassName,   //类名
    	LPCTSTR lpWindowName,  //标题名
    	DWORD dwStyle,         //指定创建的窗口的样式 WS_OVERLAPPEDWINDOW
    	int x,                 //默认值 CW_USEDEFAULT
    	int y,                 //指定窗口左上角的x,y坐标
        int nWidth,			   //默认值 CW_USEDEFAULT
    	int nHeight,           //指定窗口的宽度,高度
    	HWND hWndParent,       //指定被创建窗口的父窗口句柄
    	HMENU hMenu,           //指定窗口菜单的句柄
    	HINSTANCE hInstance,   //窗口所属的应用程序实例的句柄
    	LPVOID lpParam);       //通常设置为NULL
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 建立消息循环
    // 5.通过循环取消息
    MSG msg;
    
    while (1) {
    
    	if (GetMessage(&msg, NULL, 0, 0) == FALSE) {
    
    		break;
    	}
    
    	else {
    		TranslateMessage(&msg);   //翻译
    		DispatchMessage(&msg);    //把收到的消息传到窗口回调函数进行分析和处理
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    /*
     * Message structure
     */
    typedef struct tagMSG {
        HWND        hwnd;    //消息所属窗口
        UINT        message; //消息标识符
        WPARAM      wParam;  //指定消息的附加信息
        LPARAM      lParam;  //指定消息的附加信息
        DWORD       time;    //标识一个消息产生时的时间
        POINT       pt;      //表示产生这个消息时光标或鼠标的坐标
    #ifdef _MAC
        DWORD       lPrivate;
    #endif
    } MSG, *PMSG, NEAR *NPMSG, FAR *LPMSG;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    BOOL GetMessage(
    	LPMSG lpMsg,        //指向一个消息结构体
    	HWND hWnd,          //指定接收哪一个窗口的消息。设置为NULL,用于接收属于调用线程的所有窗口的窗口消息
    	UINT wMsgFilterMin, //指定消息的最小值
    	UINT wMsgFilterMax);//指定消息的最大值
    	//如果wMsgFilterMin和wMsgFilterMax都设置为0, 则接收所有消息。
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 编写窗口过程函数
    LRESULT CALLBACK WinProc( // CALLBACK和WINAPI 作用一样
    	HWND hWnd,			  // 信息所属的窗口句柄
    	UINT uMsg,		      // 消息类型
    	WPARAM wParam,	      // 附加信息(如键盘哪个键按下)
    	LPARAM lParam	      // 附加信息(如鼠标点击坐标)
    );
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    完整示例代码

    要创建一个win32项目,在VS2019中点击Windows桌面向导

    在这里插入图片描述
    在这里插入图片描述
    选择应用程序类型为桌面应用程序,并勾选空项目

    在这里插入图片描述
    注意,添加新建项是.c文件

    在这里插入图片描述

    # include <windows.h>
    
    LRESULT CALLBACK WinProc( // CALLBACK和WINAPI 作用一样
    	HWND hWnd,		      // 消息所属的窗口句柄
    	UINT uMsg,		      // 具体消息名称
    	WPARAM wParam,	      // 附加信息(如键盘哪个键按下)
    	LPARAM lParam	      // 附加信息(如鼠标点击坐标)
    )
    {
    	switch (uMsg)
    	{
    		case WM_KEYDOWN:         //键盘按下
    			MessageBox(hWnd, TEXT("键盘按下"), TEXT("键盘"), MB_OK);
    			break;
    		case WM_CLOSE:
    			//所有以Window结尾的方法不会进入到消息队列去,而是直接执行
    			DestroyWindow(hWnd); // DestroyWindow 发送另一个消息 WM_DESTROY
    			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);
    			MessageBox(hWnd, buf, TEXT("鼠标左键按下"), MB_OK);
    			break;
    		}
    		case WM_PAINT:          //绘图事件
    		{
    			PAINTSTRUCT ps;     //绘图结构体
    			HDC hdc = BeginPaint(hWnd, &ps);
    
    			TextOut(hdc, 100, 100, TEXT("HELLO!"), strlen("HELLO"));
    
    			EndPaint(hWnd, &ps);
    			break;
    		}
    		default:
    			//以windows默认方式处理
    			return DefWindowProc(hWnd, uMsg, wParam, lParam);
    	}
    	return 0;
    }
    
    
    // 程序入口函数
    // #define WINAPI  __stdcall 参数的传递顺序:从右到左依次入栈,并在函数返回前清空堆栈
    int WINAPI WinMain(
    	HINSTANCE hInstance,    // 应用程序实例句柄
    	HINSTANCE hPrevInstace, // 上一个应用程序实例,在win32环境中,默认为NULL
    	LPSTR lpCmdLine,        // 命令行参数 char * argv[]
    	int nShowCmd            // 窗口显示样式
    )
    
    {
    	// 1.设计窗口
    	WNDCLASS wc;                                            // 窗口类变量
    	wc.cbClsExtra = 0;                                      // 类的额外内存
    	wc.cbWndExtra = 0;                                      // 窗口的额外内存
    	wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); // 设置背景
    	wc.hCursor = LoadCursor(NULL, IDC_HAND);                // 设置光标,若第一个参数为NULL,代表使用系统提供的光标
    	wc.hIcon = LoadIcon(NULL, IDI_ERROR);                   // 设置图标,若第一个参数为NULL,代表使用系统提供的光标
    	wc.hInstance = hInstance;                               // 应用程序实例句柄,为WinMain第1个形参
    	wc.lpfnWndProc = WinProc;	                            // 回调函数 窗口过程函数名字
    	wc.lpszClassName = TEXT("MyWin");	                    // 类的名字
    	wc.lpszMenuName = NULL;	                                // 没有菜单
    	wc.style = 0;	                                        // 显示风格,填0,使用默认风格
    
    	// 2.注册窗口类
    	RegisterClass(&wc);
    
    	// 3.创建窗口
    	HWND  hWnd = CreateWindow(TEXT("MyWin"), TEXT("测试"), WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL);
    
    	// 4.显示及更新
    	ShowWindow(hWnd, SW_SHOWNORMAL);
    
    	UpdateWindow(hWnd);
    
    	// 5.通过循环取消息
    	MSG msg;
    
    	while (1) {
    
    		if (GetMessage(&msg, NULL, 0, 0) == FALSE) {
    
    			break;
    		}
    
    		else {
    			TranslateMessage(&msg);   //翻译
    			DispatchMessage(&msg);    //把收到的消息传到窗口回调函数进行分析和处理
    		}
    	}
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101

    执行结果

    在这里插入图片描述

    1.3 MFC入门

    微软基础类库(英语:Microsoft Foundation Classes,简称MFC)是一个微软公司提供的类库(class libraries),以C++类的形式封装了Windows API,并且包含一个应用程序框架,以减少应用程序开发人员的工作量。其中包含的类包含大量Windows句柄封装类和很多Windows的内建控件和组件的封装类。
    MFC把Windows SDK API函数包装成了几百个类,MFC给Windows操作系统提供了面向对象的接口,支持可重用性、自包含性以及其他OPP原则。MFC通过编写类来封装窗口、对话框以及其他对象,引入某些关键的虚函数(覆盖这些虚函数可以改变派生类的功能)来完成,并且MFC设计者使类库带来的总开销降到了最低。

    编写第一个MFC应用

    mfc.h

    #include <afxwin.h> //mfc头文件
    
    class MyApp: public CWinApp // CWinApp应用程序类
    {
    public:
    	virtual BOOL InitInstance();
    
    };
    
    class MyFrame : public CFrameWnd
    {
    public:
    	MyFrame();
    
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    mfc.cpp

    #include "mfc.h"
    
    MyApp app;                            // 有且只有一个全局的应用程序类对象
    
    BOOL MyApp::InitInstance()            // 程序入口地址
    {
    	MyFrame* frame = new MyFrame;     // 1.创建框架类对象
    
    	frame->ShowWindow(SW_SHOWNORMAL); // 2.显示窗口
    	frame->UpdateWindow();            // 3.更新窗口
    
    	m_pMainWnd = frame;               // 4.保存框架类对象指针(保存指向应用程序的主窗口的指针)
    
    	return TRUE;                      // 初始化正常返回TRUE
    }
    
    MyFrame::MyFrame()
    {
    	Create(NULL, TEXT("mfc"));
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    编译项目,报错

    C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.29.30133\atlmfc\include\afx.h(24,1): fatal error C1189: #error:  Building MFC application with /MD[d] (CRT dll version) requires MFC shared dll version. Please #define _AFXDLL or do not use /MD[d]
    
    • 1

    解决方法:打开项目属性页,在MFC的使用中选择在共享DLL中使用MFC

    在这里插入图片描述
    成功运行

    在这里插入图片描述

    程序执行流程:

    1. 程序开始时,先实例化应用程序对象(有且只有一个)
    2. 执行程序的入口函数InitInstance()
    3. 给框架类MyFrame对象动态分配空间(自动调用它的构造函数),在其构造函数内部,通过CWnd::Create创建窗口
    4. 框架类对象显示窗口CWnd::ShowWindow
    5. 框架类对象更新窗口CWnd::UpdateWindow
    6. 保存框架类对象指针CWinThread::m_pMainWnd
    CFrameWnd::Create
    
    BOOL Create(
         LPCTSTR lpszClassName,                 // 如果为NULL,使用预定义的缺省CFrameWnd属性
         LPCTSTR lpszWindowName,                // 指向代表窗口名的以空终止的字符串,用作标题条的文本。
         DWORD dwStyle = WS_OVERLAPPEDWINDOW,   // 指定窗口风格属性
         const RECT &rect = rectDefault,        // 定义窗口大小和位置
         CWnd* pParentWnd = NULL,               // 指定框架窗口的父窗口,对最高层框架窗口来说应为NULL
         LPCTSTR lpszMenuName = NULL,           // 指定与窗口一起使用的菜单资源名
         DWORD dwExStyle = 0,                   // 指定窗口扩展的风格属性
         CCreateContext* pContext = NULL        // 指向CCreateContext结构的指针
         );
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    CWnd::ShowWindow
    
    BOOL ShowWindow( int nCmdShow );
    
    //SW_SHOWNORMAL 激活并显示窗口。如果窗口是最小化或最大化的,则Windows恢复它原来的大小和位置。 
    //返回值:如果窗口原来可见,则返回非零值;如果CWnd原来是隐藏的,则返回0。
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    消息映射机制

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

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

    1. 所操作类中,声明消息映射宏
    2. 通过放置标识消息的宏来执行消息映射,相应的类将在对BEGIN_MESSAGE_MAPEND_MESSAGE_MAP的调用之间处理消息
    3. 对应消息处理函数分别在类中声明,类外定义

    mfc.h

    #include <afxwin.h> //mfc头文件
    
    class MyApp: public CWinApp // CWinApp应用程序类
    {
    public:
    	virtual BOOL InitInstance();
    
    };
    
    class MyFrame : public CFrameWnd
    {
    public:
    	MyFrame();
    
    	afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
    	afx_msg void OnChar(UINT, UINT, UINT);
    	afx_msg void OnPaint();
    
    	// 声明宏 提供消息映射机制
    	DECLARE_MESSAGE_MAP()
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    mfc.cpp

    #include "mfc.h"
    
    MyApp app;                            // 有且只有一个全局的应用程序类对象
    
    BOOL MyApp::InitInstance()            // 程序入口地址
    {
    	MyFrame* frame = new MyFrame;     // 1.创建框架类对象
    
    	frame->ShowWindow(SW_SHOWNORMAL); // 2.显示窗口
    	frame->UpdateWindow();            // 3.更新窗口
    
    	m_pMainWnd = frame;               // 4.保存框架类对象指针(保存指向应用程序的主窗口的指针)
    
    	return TRUE;                      // 初始化正常返回TRUE
    }
    
    BEGIN_MESSAGE_MAP(MyFrame, CFrameWnd) // 开始
    
    	ON_WM_LBUTTONDOWN()
    	ON_WM_CHAR()
    	ON_WM_PAINT()
    
    END_MESSAGE_MAP()                     // 结束
    
    
    MyFrame::MyFrame()
    {
    	Create(NULL, TEXT("mfc"));
    }
    
    void MyFrame::OnLButtonDown(UINT, CPoint point) {
    
    	//TCHAR buf[1024];
    	//wsprintf(buf, TEXT("x = %d, y = %d"), point.x, point.y);
    
    	//MessageBox(buf);
    
    	// MFC中使用字符串 CStirng
    	CString str;
    	str.Format(TEXT("x = %d, y = %d"), point.x, point.y);
    
    	MessageBox(str);
    }
    
    void MyFrame::OnChar(UINT key, UINT, UINT) {
    
    	CString str;
    	str.Format(TEXT("按下了%c键"), key);
    
    	MessageBox(str);
    }
    
    void MyFrame::OnPaint() {
    
    	CPaintDC dc(this);
    
    	dc.TextOutW(100, 100, CString("NJ"));
    	// 画椭圆
    	dc.Ellipse(100, 100, 150, 150);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60

    运行结果

    在这里插入图片描述

    Windows字符集

    • 多字节字符集(8位的ANSI字符集)

    在Windows98以及以前的版本使用8位ANSI字符集,它类似于我们程序员熟悉的ASCII字符集。

    char sz[] = "ABCDEFG";
    char *psz = "ABCDEFG";
    int len = strlen(sz);
    
    • 1
    • 2
    • 3
    • 宽字符集(16位的Unicode字符集)

    在WindowsNT和Windows2000后开始使用16位的Unicode字符集,它是ANSI字符集的一个超集。Unicode适用于国际市场销售的应用程序,因为它包含各种各样来自非U.S.字母表的字符,比如中文,日文,韩文,西欧语言等。

    //在字符串前加字母L表示将ANSI字符集转换成Unicode字符集。
    wchar_t wsz[] = L"ABCDEFG"; 
    wchar_t *pwsz = L"ABCDEFG";
    int len = wcslen(wsz); //测试宽字节字符串的长度
    
    • 1
    • 2
    • 3
    • 4
    • TEXT(_T)宏

    MFC中的TEXT宏可以自动适应字符类型,如果定义了预处理器程序符号_UNICODE,那么编译器将使用Unicode字符,如果没用定义该预处理器程序符号,那么编译器将使用ANSI字符。

    MessageBox(TEXT("鼠标左键"));
    MessageBox(_T("鼠标左键"));
    
    • 1
    • 2
    • TCHAR类型

    如果定义了_UNICODE符号TCHAR将变为wchar_t类型。如果没用定义_UNICODE符号,TCHAR将变为普通古老的char类型。

    char * 与 CString之间的转换

    // char * -> CString
    char* p = "ccc";
    CString str = CString(p);
    
    // CString -> char *
    CStringA tmp;
    tmp = str;
    char* pp = tep.GetBuffer();
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    1.4 用向导生成一个MFC应用

    用VS2019创建新项目时,选择MFC应用

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    项目创建好后,点击视图 → \to 类视图

    在这里插入图片描述
    直接运行,显示窗口

    在这里插入图片描述
    打开任务管理器,可以看见进程

    在这里插入图片描述
    双击类名,即可打开.h文件

    在这里插入图片描述
    双击类中成员函数,即可打开对应.cpp文件

    在这里插入图片描述

    文档/视图结构体系

    MFC应用程序框架结构的基石是文档/视图体系结构,它定义了一种程序结构,这种结构依靠文档对象保存应用程序的数据,并依靠视图对象控制视图中显示的数据,把数据本身与它的显示分离开。

    数据的存储和加载由文档类来完成,数据的显示和修改则由视类来完成MFC在类CDocumentCView中为稳定视图提供了基础结构。CWinAppCFrameWnd和其他类与CDocumentCView合作,把所有的片段连在了一起。

    CView类也派生于CWnd类,框架窗口是视图窗口的一个父窗口。主框架窗口(CFrameWnd)是整个应用程序外框所包括的部分,即粗框以内的内容,而视类窗口只是主框架中空白的地方。

    在这里插入图片描述

    因此,框架窗口是视窗口的父窗口,那么视类窗口就应该始终覆盖在框架类窗口之上。就好比框架窗口是一面墙,视类窗口就是墙纸,它始终挡在这面墙前边。也就是说,所有操作,包括鼠标单击、鼠标移动等操作都只能由视类窗口捕获。

    添加消息处理

    在消息列表中找到WM_LBUTTONDOWN消息

    在这里插入图片描述
    那么文件中会自动发生三处改动

    • 头文件中声明消息处理函数

    在这里插入图片描述

    • 对应cpp文件中添加消息宏

    在这里插入图片描述

    • 添加消息处理函数实现

    在这里插入图片描述

    在函数定义中添加如下代码

    void CmfcGuideView::OnLButtonDown(UINT nFlags, CPoint point)
    {
    	// TODO: 在此添加消息处理程序代码和/或调用默认值
    
    	CView::OnLButtonDown(nFlags, point);
    
    	CString str;
    	str.Format(TEXT("x = %d, y = %d"), point.x, point.y);
    
    	MessageBox(str);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    可以达到和之前一样的效果

    在这里插入图片描述

    MFC框架中一些重要的函数

    • CmfcGuideApp::InitInstance函数:应用程序类的一个虚函数,MFC应用程序的入口

    • CMainFrame::PreCreateWindow函数

    当框架调用CreateEx函数创建窗口时,会首先调用PreCreateWindow函数。

    通过修改传递给PreCreateWindow的结构体类型参数CREATESTRUCT,应用程序可以更改用于创建窗口的属性。在产生窗口之前让程序员有机会修改窗口的外观

    最后再调用CreateWindowEx函数完成窗口的创建。

    • CMainFrame::OnCreate函数

    OnCreate是一个消息响应函数,是响应WM_CREATE消息的一个函数,而WM_CREATE消息是由Create函数调用的。一个窗口创建(Create)之后,会向操作系统发送WM_CREATE消息,OnCreate()函数主要是用来响应此消息的。

    • CmfcGuideView::OnDraw函数

    通常我们不必编写OnPaint处理函数。当在View类里添加了消息处理OnPaint()时,OnPaint()就会覆盖掉OnDraw()

    拓展知识点

    • MFC中后缀名为Ex的函数都是扩展函数
    • MFC中,以Afx为前缀的函数都是全局函数,可以在程序的任何地方调用它们

    2 基于对话框编程

    2.1 创建基于对话框的MFC应用

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    点击完成,却报错!

    在这里插入图片描述
    依照博客:VS 2019 解决对COM组件的调用返回了错误HRESULT E_FAIL

    以管理员身份打开Developer Command Prompt for VS 2019

    在这里插入图片描述
    找到VS2019安装目录

    在这里插入图片描述
    cd 到文件位置后,输入:

    gacutil -i Microsoft.VisualStudio.Shell.Interop.11.0.dll
    
    • 1

    在这里插入图片描述
    显示:程序集已成功添加到缓存中,表示已成功!

    再依照上述步骤新建基于对话框的MFC应用程序即可。奈何还是无用依旧报错。在网上偶然看见说,项目名称使用全英文,试了下,不报错了。

    在这里插入图片描述
    双击.rc文件打开资源视图( 菜单栏 → \to 视图 → \to 其他窗口 → \to 资源视图,同样可以打开)

    在这里插入图片描述
    双击第二个选项IDD_CDIALOGTEST_DIALOG

    在这里插入图片描述
    即可看见对话框,也一般称为设计界面,打开菜单栏 → \to 视图 → \to 工具箱,可以进行选择控件拖拽至对话框中

    在这里插入图片描述

    点击运行便弹出程序窗口

    在这里插入图片描述

    另外,打开类视图,可以看见一共有三个类

    类名作用
    CAboutDlg版本信息对话框,从CDialogEx继承过来
    CCDialogTestApp应用程序类,从CWinApp继承过来
    CCDialogTestDlg对话框类,从CDialogEx继承过来

    在这里插入图片描述
    其中对话框类中有两个重要的方法函数

    • DoDataExchange:该函数主要完成对话框数据的交换和校验
    • OnInitDialog:相当于对对话框进行初始化处理

    2.3 模态对话框

    选中对话框,右击属性,可以修改对话框的标题栏等。ctrl a + delete可选中对话框中所有控件并删除

    菜单栏 → \to 视图 → \to 工具箱,拖拽1个按钮进对话框

    在这里插入图片描述

    如何修改按钮的文字?方法一:属性中修改;方法二:直接选中但不双击,直接打字进行修改。(下图为方法一)

    在这里插入图片描述

    接下来,实现点击按钮,弹出一个对话框

    但此时没有多余对话框,咋办?当然先创建出一个对话框:右击Dialog,点击插入Dialog

    在这里插入图片描述
    修改其IDIDD_EXEC,注意一般为大写

    在这里插入图片描述
    此时,模态对话框是有了。可以在模态对话框中删除默认控件,并添加一个按钮以显示模态对话框弹出。

    但是在主对话框中点击按钮是没有反应的。点击模态对话框 → \to 右击 → \to 添加类

    在这里插入图片描述

    在这里插入图片描述
    如此,类视图中多了一个自定义类CDlgExec

    在这里插入图片描述
    方式一:在主对话框中添加按钮处理函数:创建对话框,并以模态方式运行

    在这里插入图片描述
    方式二:选中模态对话框按钮控件,右击选择添加事件处理程序

    在这里插入图片描述
    方式三:直接双击模态对话框按钮控件(因此,和Qt中双击是修改控件文字的操作是不一样的)

    添加头文件

    #include "CDlgExec.h"
    
    • 1
    // 模态对话框按钮点击事件
    void CCDialogTestDlg::OnBnClickedButton1()
    {
    	// TODO: 在此添加控件通知处理程序代码
    
    	CDlgExec dlg;
    	dlg.DoModal(); //以模态方式运行
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    在这里插入图片描述

    2.4 非模态对话框

    与上文类似,先在主对话框中拖入一个按钮,并修改其文字为:非模态对话框

    再插入一个对话框,并更改其IDIDD_SHOW,并拖入一个按钮,修改文字:非模态对话框弹出

    只有对话框可不行,还得为对话框添加类,CDlgShow

    在这里插入图片描述
    当然,你也就会发现,类试图中多了一个类(其实每个窗口都会对应一个类)

    在这里插入图片描述
    在主对话框中,双击非模态对话框控件,进入该按钮的点击事件处理函数,编写程序

    #include "CDlgShow.h"
    
    • 1
    // 非模态对话框点击事件
    void CCDialogTestDlg::OnBnClickedButton2()
    {
    	// TODO: 在此添加控件通知处理程序代码
    
    	CDlgShow dlg;
    	dlg.Create(IDD_SHOW);
    	dlg.ShowWindow(SW_SHOWNORMAL);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    运行程序,会发现对话框一闪而过。原因:dlg是局部变量,函数运行完就会消失。

    解决办法:将dlg变量放在类CCDialogTestDlg的私有变量中,成为成员变量即可。(记得,添加头文件)

    在这里插入图片描述

    // 非模态对话框点击事件
    void CCDialogTestDlg::OnBnClickedButton2()
    {
    	// TODO: 在此添加控件通知处理程序代码
    
    	//CDlgShow dlg;
    	dlg.Create(IDD_SHOW);
    	dlg.ShowWindow(SW_SHOWNORMAL);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    在这里插入图片描述
    关闭非模态对话框后,当在主对话框中再次点击非模态对话框按钮时,会报错

    在这里插入图片描述
    原因:Create函数只能创建一次,再次创建就会出错。

    解决办法:将创建窗口的语句放在OnInitDialog下,可以保证只创建一次非模态对话框的窗口

    在这里插入图片描述
    如此一来,在主窗口初始化时便创建了非模态对话框,点击按钮只是显示出来

    3 常用控件

    3.1 静态文本框 CStaticText

    在同一解决方案下继续新建MFC项目,创建好后设定启动项

    在这里插入图片描述
    在这里插入图片描述
    静态文本框是最简单的控件,它主要用来显示文本信息,不能接受用户输入,一般不需要连接变量,也不需要处理消息。

    MFC中的静态文本框等同于Qt中的QLabel。它与上述按钮控件一样,同样有两种方式修改文本,不再赘述。

    在这里插入图片描述
    再拖两个按钮控件上去

    • 点击按钮1,可将静态文本框设置为:呵呵
    • 点击按钮2,可弹出显示窗口,显示静态文本框中内容

    在这里插入图片描述
    Qt中任何控件都有ObjectName,但MFC中不存在,但是MFC可以关联变量。选中控件,右击 → \to 添加变量

    由于XXX_STATIC静态ID是不能关联变量,故需把ID修改后,再关联变量

    在这里插入图片描述
    修改ID后就可以添加为控件添加变量了

    在这里插入图片描述
    修改访问类变量访问权限为private,添加类变量名称为m_text,点击完成

    在这里插入图片描述
    创建好控件对应的类变量后,自然可以在按钮1的点击处理函数中编程

    // 点击按钮,使得哈哈哈哈变为呵呵
    void CCStaticTextTestDlg::OnBnClickedButton1()
    {
    	// TODO: 在此添加控件通知处理程序代码
    	
    	// 设置文本
    	m_text.SetWindowTextW(TEXT("呵呵"));
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    运行程序

    在这里插入图片描述
    在按钮2的点击处理函数中编程

    // 点击按钮,获取文本中内容,并且弹出
    void CCStaticTextTestDlg::OnBnClickedButton2()
    {
    	// TODO: 在此添加控件通知处理程序代码
    
    	CString str;
    	m_text.GetWindowTextW(str);
    	MessageBox(str);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    在这里插入图片描述
    再拖入一个静态文本框控件。右击 → \to 添加变量,别忘了改一下控件ID

    在主对话框类CCStaticTextTestDlgOnInitDialog初始化函数中添加代码

    //设置静态控件窗口风格为位图居中显示
    m_pic.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_pic.GetWindowRect(rect);
    
    //静态控件设置bitmap
    m_pic.SetBitmap(HBMP(TEXT("./1.bmp"), rect.Width(), rect.Height()));
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    在这里插入图片描述

    另外,按钮控件不但可以处理点击事件,也可以像静态文本框控件这样去关联变量,并且可以不用修改其ID。关联之后在窗口类中就存在一个私有变量,从而可以借助该变量去设置按钮的文本、属性等。常用接口如下:

    接口功能
    CWnd::SetWindowTextW设置控件内容
    CWnd::GetWindowTextW获取控件内容
    CWnd::EnableWindow设置控件是否变灰
    void CCStaticTextTestDlg::OnBnClickedButton3()
    {
    	// TODO: 在此添加控件通知处理程序代码
    
    	m_btn.SetWindowTextW(TEXT("o_o"));
    
    	CString str;
    	m_btn.GetWindowTextW(str);
    	MessageBox(str);
    
    	m_btn.EnableWindow(FALSE);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    在这里插入图片描述

    3.2 编辑框 CEdit

    继续新建项目,CEditCtrlTest,在对话框中拖入控件Edit Control

    在这里插入图片描述
    运行程序后,在编辑框中输入按回车,会自动退出对话框。若是想设置为多行编辑框,右击 → \to 属性

    在这里插入图片描述
    在这里插入图片描述
    另外,该控件可以设置属性中Auto VScroll垂直滚动达到类似txt上下滚动的效果

    在这里插入图片描述
    若是想再实现一个一模一样的编辑框,按住ctrl拖动一下即可。在工具箱中拖入两个按钮以实现功能。

    在这里插入图片描述
    为两个编辑框分别添加变量:m_edit1m_edit2

    在这里插入图片描述

    当对话框中控件较多时,同时添加变量较多时,该如何方便得查看类中的所有变量呢?右击对话框 → \to 类向导 → \to 成员变量

    在这里插入图片描述

    先在编辑框中添加一下默认文本内容,在OnInitDialog函数中编写初始化程序

    // TODO: 在此添加额外的初始化代码
    m_edit1.SetWindowTextW(TEXT("MFC"));
    
    • 1
    • 2

    copy按钮添加处理事件程序

    void CCEditCtrlTestDlg::OnBnClickedButton1()
    {
    	// TODO: 在此添加控件通知处理程序代码
    	CString str;
    	m_edit1.GetWindowText(str);
    	m_edit2.SetWindowTextW(str);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    在这里插入图片描述
    close按钮添加事件处理程序

    void CCEditCtrlTestDlg::OnBnClickedButton2()
    {
    	// TODO: 在此添加控件通知处理程序代码
    
    	// 退出程序
    	exit(0);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    但是一般而言,我们只希望关闭当前对话框,不希望关闭整个程序

    void CCEditCtrlTestDlg::OnBnClickedButton2()
    {
    	// TODO: 在此添加控件通知处理程序代码
    
    	// 退出当前对话框
    	CDialog::OnOK();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    再回过头解决一下小bug:单行编辑框点击回车就退出的问题。

    解决办法:重写OnOk,注释掉其中代码

    在这里插入图片描述
    为了介绍编辑框另一个使用方法——关联基本类型变量,再拖入一个编辑框和两个按钮。并为编辑框添加变量,但此时注意,类别选择Value

    在这里插入图片描述
    添加设置内容控件的点击处理函数

    void CCEditCtrlTestDlg::OnBnClickedButton3()
    {
    	// TODO: 在此添加控件通知处理程序代码
    
    	m_text = L"hhh";
    	UpdateData(FALSE);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    在这里插入图片描述

    添加获取内容控件的点击处理函数

    void CCEditCtrlTestDlg::OnBnClickedButton4()
    {
    	// TODO: 在此添加控件通知处理程序代码
    	
    	UpdateData(TRUE);
    	MessageBox(m_text);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    在这里插入图片描述

    3.4 组合框 CComboBox

    继续在同一解决方案下创建新项目,ComboBoxCtrl。在对话框中拖入Combo Box控件,经如下设置后运行程序,可显示下拉框的效果

    在这里插入图片描述

    在这里插入图片描述
    但是一般在开发中,不会通过属性去设置。组合框常用接口如下:

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

    使用接口自然需要关联变量,为下拉框控件关联变量

    在这里插入图片描述
    在主对话框类的OnInitdialog函数中添加下拉框初始化代码

    // TODO: 在此添加额外的初始化代码
    m_cbx.AddString(TEXT("韩立"));
    m_cbx.AddString(TEXT("墨居仁"));
    m_cbx.AddString(TEXT("银月"));
    m_cbx.AddString(TEXT("紫灵"));
    
    m_cbx.SetCurSel(2);
    
    m_cbx.InsertString(3, TEXT("小瓶"));
    
    m_cbx.DeleteString(1);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    在这里插入图片描述
    组合框常用的事件为:CBN_SELCHANGE,当选择组合框某一项时,自动触发此事件。

    在控件属性中选择该事件,或者双击控件进入事件处理函数

    void CComboBoxCtrlDlg::OnCbnSelchangeCombo1()
    {
    	// TODO: 在此添加控件通知处理程序代码
    
    	int index = m_cbx.GetCurSel();
    
    	CString str;
    	m_cbx.GetLBText(index, str);
    	MessageBox(str);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    在这里插入图片描述

    3.5 列表控件 CListCtrl

    继续新建项目,ListCtrl。在工具箱中拖入一个列表控件,并设置属性:view → \to Report

    在这里插入图片描述
    添加变量

    在这里插入图片描述
    列表控件常用接口如下

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

    去窗口初始化函数中添加初始化代码

    // TODO: 在此添加额外的初始化代码
    
    CString str[] = { TEXT("姓名"), TEXT("性别"), TEXT("年龄") };
    
    for (int i = 0; i < 3; i++) {
    	// 设置表头 参数1->索引  2->内容  3->对齐方式 4->列宽  
    	m_list.InsertColumn(i, str[i], LVCFMT_LEFT, 150);
    }
    
    CString name[] = { L"韩立", L"紫灵", L"元瑶", L"冰风", L"巧倩" };
    CString gender[] = { L"男", L"女", L"女" , L"女" , L"女" };
    CString age[] = { L"24", L"22", L"23" , L"24" , L"25" };
    
    // 设置正文 表头不算正文
    for (int i = 0; i < 5; i++) {
    	int j = 0;
    	m_list.InsertItem(i, name[i]);
    	// 设置这个item其他列的数据
    	m_list.SetItemText(i, ++j, gender[i]);
    	m_list.SetItemText(i, ++j, age[i]);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    在这里插入图片描述
    可以通过代码继续设置列表属性

    m_list.SetExtendedStyle(m_list.GetExtendedStyle() | LVS_EX_FULLROWSELECT | LVS_EX_GRIDLINES);
    
    • 1

    在这里插入图片描述

    3.6 树控件 CTreeCtrl

    继续新建MFC应用,CTreeCtrl。在主对话框中拖入控件CTreeCtrl,先设置一下属性

    在这里插入图片描述
    接着给树控件关联变量

    在这里插入图片描述
    该控件常用接口如下

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

    编程前的准备工作:把ico资源文件放在项目res文件夹中

    在这里插入图片描述
    ico资源文件放好位置后,在资源视图 → \to Icon → \to 添加资源。注意,VS2019需要将资源文件改成.bmp后缀的才可以进行添加

    在这里插入图片描述

    OnInitDialog函数中添加控件初始化代码

    // TODO: 在此添加额外的初始化代码
    
    // 树控件使用
    // 1.设置图标
    // 准备HICON图标
    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; // 这个需要写到类成员变量中,否则出函数变量会被释放
    //30, 30: 图片的宽度和高度	ILC_COLOR32:样式	3, 3:有多少图片写多少
    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("子节点1"), 2, 2, parent);
    HTREEITEM sub2 = m_tree.InsertItem(TEXT("子节点2"), 3, 3, parent);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    在这里插入图片描述
    树控件常用事件为:TVN_SELCHANGED,当选择某个节点时,自动触发此事件

    在这里插入图片描述

    void CCTreeCtrlDlg::OnTvnSelchangedTree1(NMHDR* pNMHDR, LRESULT* pResult)
    {
    	LPNMTREEVIEW pNMTreeView = reinterpret_cast<LPNMTREEVIEW>(pNMHDR);
    	// TODO: 在此添加控件通知处理程序代码
    	*pResult = 0;
    
    	HTREEITEM selItem;
    	//获得选择项
    	selItem = m_tree.GetSelectedItem();
    	//获取选中的内容
    	CString cs = m_tree.GetItemText(selItem);
    	MessageBox(cs);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    在这里插入图片描述

    3.7 标签页的使用

    新建MFC项目,CtabCtrl。将默认控件删除,并拖入CTabCtrl控件。

    TabSheet.hTabSheet.cpp放在项目文件同级目录,并且添加到工程目录中。

    在这里插入图片描述
    uiTab Control 关联Control类型(CTabSheet

    在这里插入图片描述

    两个标签页都需要各自添加对话框,并设置相应属性

    在这里插入图片描述
    在这里插入图片描述
    自定义类:点击对话框模板 → \to 右击 → \to 添加类(MyDlg1MyDlg2)

    主对话框类中,定义自定义类对象,需要相应头文件

    在这里插入图片描述
    主对话框类中 OnInitDialog() 做初始化工作

    // TODO: 在此添加额外的初始化代码
    
    m_tab.AddPage(TEXT("系统管理"), &dlg1, IDD_DIALOG1);
    m_tab.AddPage(TEXT("系统设置"), &dlg2, IDD_DIALOG2);
    
    m_tab.Show();
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    在这里插入图片描述

  • 相关阅读:
    C 与 C++ 的真正区别在哪里?
    尚好房 11_Session共享
    【力扣】剑指offer第二天
    JSP application对象简介说明
    从零开始打造一款基于SpringBoot+SpringCloud的后台权限管理系统
    【斗破年番】蝎毕岩决战小医仙,魂殿铁护法出场,彩鳞再霸气护夫
    完美收官 | IOTE第十八届国际物联网展精彩落幕,美格智能参展回顾
    VSCODE环境搭建
    使用python做了一个pdf转word的小工具
    <Java编程工具JDK、IDEA安装及环境配置教程>——《Java》
  • 原文地址:https://blog.csdn.net/Star_ID/article/details/125238025