【MFC】10.MFC六大机制:RTTI(运行时类型识别),动态创建机制,窗口切分,子类化-笔记

news2024/11/24 20:08:34

运行时类信息(RTTI)

C++:

##是拼接

#是替换成字符串

// RTTI.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#include <iostream>
#include <afxwin.h>

#ifdef _DEBUG
#define new DEBUG_NEW
#endif

CWinApp theApp;

int main()
{
	//CListBox是MFC自带的控件类
	CListBox* pListBox = new CListBox;
	//GetRuntimeClass方法返回运行时类信息
	CRuntimeClass* pRuntimeClass = pListBox->GetRuntimeClass();
	std::cout << pRuntimeClass->m_lpszClassName << std::endl;

	//运行时类的IsDeriverFrome方法可以判断该类是否继承于某类
	if (pRuntimeClass->IsDerivedFrom(RUNTIME_CLASS(CWnd))) {
		std::cout<<"CListBox类继承于CWnd类"<<std::endl;
	}
	if (pRuntimeClass->IsDerivedFrom(RUNTIME_CLASS(CView))) {
		std::cout << "CListBox类继承于视图类" << std::endl;
	}

	//运行时类的m_pfnGetBaseClass方法可以获取父类的信息
	CRuntimeClass* pParentClass = pRuntimeClass->m_pfnGetBaseClass();
	if (pParentClass->IsDerivedFrom(RUNTIME_CLASS(CWnd))) {
		std::cout << "运行时类的父类继承于CWnd类" << std::endl;
	}

	//动态创建一个对象:
	CWnd* pWnd = (CWnd*)pParentClass->m_pfnCreateObject();

	return 0;
}

这是MFC提供的运行时类信息的使用,如果我们自己创建一个类,如果想用这些方法,必须要满足三个条件:

  • 这个类必须继承于CObject类
  • 类内必须声明DECLARE_DYNAMIC
  • 类外必须实现IMPLENENT_DYNAMIC

我们来看看是如何实现的:

拆分宏
DECLARE_DYNAMIC(SHape)
public: 
	//静态的结构体
	//本来是static const CRuntimeClass class##class_name;,拼接之后:
	static const CRuntimeClass classSHape; 
	//虚函数
	virtual CRuntimeClass* GetRuntimeClass() const; 

//IMPLEMENT_DYNAMIC(SHape,CObject)
IMPLEMENT_RUNTIMECLASS(SHape, CObject, 0xFFFF, NULL, NULL)

AFX_COMDAT const CRuntimeClass SHape::classSHape = 
{ 
		"SHape", 
		sizeof(class SHape), 
		0xFFFF, 
		NULL, 
		RUNTIME_CLASS(CObject),//返回父类静态结构体的地址
		NULL,
		NULL 
}; 

CRuntimeClass* SHape::GetRuntimeClass() const 
{ 
	return RUNTIME_CLASS(SHape);
}


struct CRuntimeClass
{
	LPCSTR m_lpszClassName;					//类名称
	int m_nObjectSize;						//类大小
	UINT m_wSchema; 						//类版本
	CObject* (PASCAL* m_pfnCreateObject)(); //动态创建才会使用 暂时NULL函数指针
	CRuntimeClass* m_pBaseClass;			//父类信息
	CRuntimeClass* m_pNextClass;      		//NULL
	const AFX_CLASSINIT* m_pClassInit;		//NULL
}

#define RUNTIME_CLASS(class_name) _RUNTIME_CLASS(class_name)
((CRuntimeClass*)(&SHape::classSHape))
}

这里给出RTTI的图,每一个类中都保存了这样一个结构,相当于链表,我们有当前的类信息,就可以得到所有父类信息:
RTTI

动态创建机制

如果想在MFC中实现动态创建:

  1. 也必须继承与CObject类
  2. 类内声明DECLARE_DYNCREATE
  3. 类外实现IMPLEMENT_DYNCREATE
class SHape : public CObject
{
public:
	DECLARE_DYNCREATE(SHape)
};
IMPLEMENT_DYNCREATE(SHape,CObject)

class CLine : public SHape
{
public:
	DECLARE_DYNCREATE(CLine)
};
IMPLEMENT_DYNCREATE(CLine, SHape)

需要注意的是,动态创建宏中包含了动态信息的宏

使用:

int main()
{

	HMODULE hModule = ::GetModuleHandle(nullptr);
	AfxWinInit(hModule, nullptr, ::GetCommandLine(), 0);

	//定义直线类:
	CLine line;

	//方法内部this指针指向line
	//该方法用于判断是否继承与某个类
	if (line.IsKindOf(RUNTIME_CLASS(SHape))) {
		std::cout << "是图形" << std::endl;
	}
	if (line.IsKindOf(RUNTIME_CLASS(CWnd))) {
		std::cout << "是窗口" << std::endl;
	}
	else {
		std::cout << "不是窗口" << std::endl;
	}

	CObject* pLine = RUNTIME_CLASS(CLine)->CreateObject();
	if (pLine->IsKindOf(RUNTIME_CLASS(SHape))) {
		std::cout << "创建成功" << std::endl;
	}

	return 0;
}

动态创建包括了类信息

//函数跟踪
BOOL CObject::IsKindOf(const CRuntimeClass* pClass) const
{
	//拿到链表头节点
	CRuntimeClass* pClassThis = this->GetRuntimeClass();
	
	pClassThis->IsDerivedFrom(参数是判断的结构体地址)
	{
		while (pClassThis != NULL)
		{
			if (pClassThis == 参数)
				return TRUE;

			if (pClassThis->m_pfnGetBaseClass == NULL)
				break;
			//获取父类静态结构体地址
			pClassThis = pClassThis->m_pBaseClass;
		}
	}
	}


#define DECLARE_DYNCREATE(class_name) \
	DECLARE_DYNAMIC(class_name) \
	static CObject* PASCAL CreateObject();
	
CObject* PASCAL CLine::CreateObject() 
		{ 
				return new CLine; 
		} \
	IMPLEMENT_RUNTIMECLASS(CLine, SHape, 0xFFFF, class_name::CreateObject, NULL)

AFX_COMDAT const CRuntimeClass class_name::class##class_name = 
{ 
		"CLine", 
		sizeof(class CLine),
		0xFFFF, 
		pfnNew, 
		RUNTIME_CLASS(base_class_name), 
		NULL,
		class_init 
}; 

视图分割

CSplitterWnd:专门负责窗口切分

创建对话框,视图:Create函数

重载父类框架类的虚函数CFrandWnd::OnCreateClient,这个函数专门用于切分

动态创建:Create函数

静态创建:CreateStatic函数

  • 静态分割(在窗口创建的时候就已经分割好了)

    在我们的框架类中:

    class CMyFrameWnd:public CFrameWnd{
    public:
      CSplitterWnd spWnd;
      CSplitterWnd spWnd1;
      virtual BOOL OnCreateClient(LPCREATESTRUCT* lpcs,CCreateContext* pContext){
        spWnd.CreateStatic(this,2,1);
        spWnd1.CreateStatic(this,1,2,WS_CHILD|WS_CISIBLE,IdFromRowCol(0,0));
        
        return true;
      }
      }
    }
    

    我们这样写完了之后,发现运行不起来,这是因为我们只是创建了框架,但是没有创建视图

    我们需要给视图类添加动态创建机制,我们重新写代码:

    //我们自己的视图类:
    class MyView:public CView{
      DECLARE_DYNREATE(MyView);
      virtual void OnDrow(DCD* pDC){
        
      }
    }
    IMPLEMENT_DYNCREATE(MyView,CView)
    //我们自己的框架窗口类:
    class CMyFrameWnd:public CFrameWnd{
    public:
      CSplitterWnd spWnd;
      CSplitterWnd spWnd1;
      virtual BOOL OnCreateClient(LPCREATESTRUCT* lpcs,CCreateContext* pContext){
        spWnd.CreateStatic(this,2,1);
        spWnd1.CreateStatic(&spWnd,1,2,WS_CHILD|WS_CISIBLE,spWnd,IdFromRowCol(0,0));
        //创建视图:
        spWnd1.CreateView(0,0,RUNTIME_CLASS(MyVIew),CSize(50,50),pContext);
        spWnd1.CreateView(0,1,RUNTIME_CLASS(CTreeView),CSize(50,50),pContext);
        
        spWnd.CreateView(1,0,RUNTIME_CLASS(CHtmlView),CSize(50,50),pContext);
       
        //设置行信息:
        spWnd.SetRowInfo(0,200,100);
        //设置列信息
        spWnd1.SetColumnInfo(0,200,100);
        spWnd1.SetColumnInfo(1,200,100);
        
        CHtmlView* html = (CHtmlView*)spWnd.GetPane(1,0);
        
        html->Navigate(L"e:/");
        return true;
      }
    }
    

    现在我们创建了三个窗口

    这些控件都被MFC接管了,Win32 EDIT处理函数,微软定义的,

    如果想要处理消息,就要使用Win32子类化(笔记在本篇最后面)

    • 现在我们想处理树视图上的消息


    • Create方法:

        virtual BOOL OnCreateClient(LPCREATESTRUCT lpcs,CCreateContext* pContext){
          swWnd.CreateStatic(CWnd* pParentEnd,//分隔器窗口的父框架窗口句柄
          int nRows,//行数,这个值必须不超过16
          int nCols,//列数,这个值必须不超过16
          dwStyle,//指定窗口的风格
          nID//此窗口的ID,如果这个分隔器窗口不是嵌套在另一个分隔器窗口中的,则这个ID可以是AFX_IDW_PANE_FIRSH
          )
      
    • 获取窗口ID:CSplitterWnd::IdFromRowCol方法:

      int IdFromRowCol(int row,int col);//参数:行数,列数
      返回值:返回此窗格的子窗口的ID
      
    • 在分隔器中创建一个窗格:CreateVIew方法:

      virtual BOOL CreateView(int row,int col,CRuntimeClass* pVIewClass,SIZE sizeInit,CCreateContext* pCOntext);
      参数:行数,列数,运行时类信息,初始尺寸,用来创建此试图的创建环境的指针
      
  • 动态分隔:

    //我们自己的视图类:
    class MyView:public CView{
      DECLARE_DYNREATE(MyView);
      virtual void OnDrow(DCD* pDC){
        
      }
    }
    IMPLEMENT_DYNCREATE(MyView,CView)
    //我们自己的框架窗口类:
    class CMyFrameWnd:public CFrameWnd{
    public:
      CSplitterWnd spWnd;
      CSplitterWnd spWnd1;
      virtual BOOL OnCreateClient(LPCREATESTRUCT* lpcs,CCreateContext* pContext){
        CCreateContext Context;
        Context.m_pNewViewClass = RUNTIME_CLASS(MyView);
        spWnd.Create(this,2,2,CSize(50,50),&Context);
        return true;
      }
    }
    
    • 动态切分,Create方法:
      BOOL CSplitterWnd::Create(
        CWnd* pParentWnd,//分隔器父窗口的句柄
        int nMaxRows,//分隔器窗口的最大行数,这个值不能超过2
        int nMaxCols,//分隔器窗口的最大列数,这个值不能超过2
        SIZE sizeMin,//指出显示一个窗格所需的最小尺寸
        CCreateCOntext* pContext,//指向一个CCreateContext结构的指针
        DWORD dwStyle = WS_CHILD|WS_VISIBLE|WS_HSCROLL|WS_VSCROLL|SPLS_DYNAMIC_SPLIT,//窗口风格
        UINT nID = AFX_IDW_PANE_FIRST//此窗口的子窗口ID。如果这个分隔器窗口不是嵌套在另一个分隔器窗口中的,则这个ID可以是AFX_IDW_PANE_FIRST
        )
      

Win32子类化

在win32编辑框,可以设置属性,风格,字母不可见,****等

现在有一个需求:只允许输入小写字母和数字

C++中,只要继承,重写虚函数,在交给父类处理

在Win32中,添加一个编辑框,和一个按钮,

添加一个全局变量,保存原来的edit消息处理函数

WNDPROC OldProc;

然后添加按钮回调函数:

LRESULT CALLBACK MyProc(HWND hDlg,UINT uMsg,WPARAM wParam,LPARAM lParam){
  if(uMsg == WM_CHAR){
    if(wParam>='a'&&wParam<='z'||wParam>='0'&&wParam<='9'){
      //交给原来的处理函数处理
      return OldProc(hDlg,uMsg,wParam,lParam);
    }else{
      return 0;
    }
  }
}

消息处理函数中:

if(LOWORD(wParam)==IDC_BUTTON1){
  //要先找到窗口句柄:
  HWND hEdit = GetDlgItem(hDlg,IDC_EDIT1);
  //修改原来的过程函数(原来是操作系统默认的
  //OldProc = (WNDPROC)SetWindowLogn(hEdit,GWL_WNDPROC,(LONG)MyProc);
  //第二种方式,之前创建的编辑框不好使,但是之后创建的对话框,可以使用
  //这个子类化方式实际上是改了类的回调,就是EDIT类的回调,之后创建的EDIt类就好使了
  OldProc = (WNDPROC)SetWindowLogn(hEdit,GCL_WNDPROC,(LONG)MyProc);
}

第二种方式:

再添加一个按钮

HISRANCE hInst;

消息回调:

if(LPWORD(wParam)==IDC_BUTTON2){
  CreateWindow(L"EDIT","ads",WS_CHILD|WS_VISIBLE,100,100,100,100,hDlg,NULL,hInst);
}

MFC子类化

这里我们把第一个视图换成一个窗口(对话框)

  1. 创建一个对话框
  2. 为该对话框添加一个类:CMyFormView
  3. 主cpp中,包含刚才添加的类的头文件
  4. 然后把第一个改为我们创建的类
    //我们自己的视图类:
    class MyView:public CView{
      DECLARE_DYNREATE(MyView);
      virtual void OnDrow(DCD* pDC){
        
      }
    }
    IMPLEMENT_DYNCREATE(MyView,CView)
    //我们自己的框架窗口类:
    class CMyFrameWnd:public CFrameWnd{
    public:
      CSplitterWnd spWnd;
      CSplitterWnd spWnd1;
      virtual BOOL OnCreateClient(LPCREATESTRUCT* lpcs,CCreateContext* pContext){
        spWnd.CreateStatic(this,2,1);
        spWnd1.CreateStatic(&spWnd,1,2,WS_CHILD|WS_CISIBLE,spWnd,IdFromRowCol(0,0));
        //创建视图:
        spWnd1.CreateView(0,0,RUNTIME_CLASS(MyFormView),CSize(50,50),pContext);
        spWnd1.CreateView(0,1,RUNTIME_CLASS(CTreeView),CSize(50,50),pContext);
        
        spWnd.CreateView(1,0,RUNTIME_CLASS(CHtmlView),CSize(50,50),pContext);
       
        //设置行信息:
        spWnd.SetRowInfo(0,200,100);
        //设置列信息
        spWnd1.SetColumnInfo(0,200,100);
        spWnd1.SetColumnInfo(1,200,100);
        
        CHtmlView* html = (CHtmlView*)spWnd.GetPane(1,0);
        
        html->Navigate(L"e:/");
        return true;
      }
    }
    
  5. 需要把我们创建的对话框修改为子窗口类,否则会报错
  • DDX/DDV虚函数:

    就是空间绑定变量/数据,调用UpdataTA(),交互数据用

    控件绑定变量

    值绑定变量

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/865243.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

MySQL8的下载与安装-MySQL8知识详解

本文的内容是mysql8的下载与安装。主要讲的是两点&#xff1a;从官方网站下载MySQL8安装和从集成环境安装MySQL8。 一、从官方网站下载MySQL8.0安装 MySQL8.0官方下载地址是&#xff1a;&#xff08;见图&#xff09; 官方正式版的最新版本是8.0.34&#xff0c;也推出了创新版…

极致鸿蒙2.0——华为MatePad系列安装AidLux,一个自带vscode的Python编译环境

看着刚刚人入手的华为鸿蒙系统MatePad11平板&#xff0c;是如此的美轮美奂&#xff0c;但是总感觉少了点什么&#xff0c;少了点什么呢&#xff1f;是编程环境&#xff0c;我爱MatePad&#xff0c;也爱编程&#xff0c;那如果可以在MatePad上编程&#xff0c;会发生什么&#x…

链表有无环以及确定入环口详解

142.环形链表 II 给定一个链表的头节点 head &#xff0c;返回链表开始入环的第一个节点。 如果链表无环&#xff0c;则返回 null。 如果链表中有某个节点&#xff0c;可以通过连续跟踪 next 指针再次到达&#xff0c;则链表中存在环。 为了表示给定链表中的环&#xff0c;评测…

win10强制卸载奇安信天擎

1、win r 打开运行 2、输入msconfig进入系统配置面板 3、点击引导&#xff0c;修改安全引导配置项 4、重启系统&#xff08;桌面会变成纯黑背景&#xff0c;符合预期&#xff0c;莫紧张&#xff09; 5、删除安装的文件夹 若是安装天擎时选择的自定义安装&#xff0c;则配置…

【java】mybatis-plus代码生成

正常的代码生成这里就不介绍了。旨在记录实现如下功能&#xff1a; 分布式微服务环境下&#xff0c;生成的entity、dto、vo、feignClient等等api模块&#xff0c;需要和mapper、service、controller等等分在不同的目录生成。 为什么会出现这个需求&#xff1f; mybatis-plus&am…

一文带你彻底了解java 网络编程的基本概念

一文带你彻底了解java 网络编程的基本概念 主题&#xff1a;探索Java网络编程&#xff1a;构建连接世界的桥梁 作者&#xff1a;Stevedash 发布日期&#xff1a;2023年8月11日 15点18分 &#xff08;PS&#xff1a;这一篇文章作为总章&#xff0c;今天着重讲“Socket套接字编…

小程序如何设置电子票

电子票是一种方便快捷的票务管理方式&#xff0c;可以帮助商家实现电子化的票务管理&#xff0c;提升用户体验。下面介绍&#xff1a;如何在小程序内&#xff0c;设置电子票以及用电子票购买商品。 1. 设置电子票套餐。可以新建一个商品&#xff0c;商品标题写&#xff1a;XX电…

玩赚音视频开发高阶技术——FFmpeg

随着移动互联网的普及&#xff0c;人们对音视频内容的需求也不断增加。无论是社交媒体平台、电商平台还是在线教育&#xff0c;都离不开音视频的应用。这就为音视频开发人员提供了广阔的就业机会。根据这些年来网站上的音视频开发招聘需求来看&#xff0c;音视频开发人员的需求…

Linux——基础IO(1)

目录 0. 文件先前理解 1. C文件接口 1.1 写文件 1.2 读文件 1.3 输出信息到显示器 1.4 总结 and stdin & stdout & stderr 2. 系统调用文件I/O 2.1 系统接口使用示例 2.2 接口介绍 2.3 open函数返回值 3. 文件描述符fd及重定向 3.1 0 & 1 & 2 3.2…

【2023 华数杯全国大学生数学建模竞赛】 A题 隔热材料的结构优化控制研究 问题分析及完整论文

【2023 华数杯全国大学生数学建模竞赛】 A题 隔热材料的结构优化控制研究 问题分析及完整论文 1 题目 A 题 隔热材料的结构优化控制研究 新型隔热材料 A 具有优良的隔热特性&#xff0c;在航天、军工、石化、建筑、交通等高科技领域中有着广泛的应用。 目前&#xff0c;由单…

如何通过CRM系统进行成功的客户生命周期管理?

吸引新客户&#xff0c;提供无与伦比的服务或商品&#xff0c;以及建立成功的客户关系&#xff0c;是每个企业努力追求的目标。然而&#xff0c;实现这些目标需要的不仅仅是良好的愿景&#xff0c;还需要实施客户生命周期管理流程。 什么是客户生命周期管理&#xff1f; 客户…

安科瑞物联网表在虚拟电厂的应用

安科瑞 崔丽洁 应用场景 一般应用于控制中心 功能 能计量当前组合有功电能&#xff0c;正向有功电能&#xff0c;反向有功电能&#xff0c;正向无功电能&#xff0c;反向无功电能&#xff1b; ADW300支持RS485通讯、LORA通讯、NB、4G及Wifi通讯&#xff1b; 三套时段表,一年可以…

gazebo与PX4联合仿真

自主无人机定义 功能框架     开发流程     项目代码框架   项目流程 gazebo的文件类型 仿真的类型 ⮚ SITL 全称为Software in the loop&#xff0c;即软件在换仿真。 ⮚ 仿真首先分为软件在环仿真&#xff08;SITL&#xff09;和硬件在环仿真&#xff08;HITL&a…

自举电容的工作原理

一&#xff0e;异步自举 1.1异步Buck的自举环路组成 上图为芯片的典型应用拓扑&#xff0c;Cboot就是我们说的自举电容。为了能清楚的理解自举电容的原理&#xff0c;我们需要深入到Buck芯片内部&#xff0c;去看个究竟。 上图即为异步Buck芯片LMR16006的内部架构。 ①Q1&…

一百五十一、Kettle——Linux上安装的kettle8.2开启carte服务

一、目的 kettle8.2在Linux上安装好可以启动界面、并且可以连接MySQL、Hive、ClickHouse等数据库后&#xff0c;准备在Linux上启动kettle的carte服务 二、实施步骤 &#xff08;一&#xff09;carte服务文件路径 kettle的Linux运行的carte服务文件是carte.sh &#xff08;二…

15-1_Qt 5.9 C++开发指南_Qt多媒体模块概述

多媒体功能指的主要是计算机的音频和视频的输入、输出、显示和播放等功能&#xff0c;Qt 的多媒体模块为音频和视频播放、录音、摄像头拍照和录像等提供支持&#xff0c;甚至还提供数字收音机的支持。本章将介绍 Qt 多媒体模块的功能和使用。 文章目录 1. Qt 多媒体模块概述2. …

Scratch 游戏 之 大地图引擎之摄像头控制

引子 上一期教程我们讲了如何制作一个可以侦测视角外碰撞的大地图引擎&#xff0c;这期我们就来制作一下摄像头限制。 首先&#xff0c;我们先要测试出地图的摄像头限制值&#xff1a; 如何测试呢&#xff0c;我们只需要在游戏中移动玩家角色&#xff0c;来求得四个值&#xff…

mqbroker.cmd闪退(mqnamesrv.cmd能正常启动)

解决&#xff1a; 用户目录下面store文件&#xff08;如&#xff1a;C:\Users\Administrator\store或C:\Users\你的用户名\store&#xff09;&#xff0c;删除文件里面所有文件&#xff0c;再次启动即可。

(5)所有角色数据分析页面的构建-5

所有角色数据分析页面&#xff0c;包括一个时间轴柱状图、六个散点图、六个柱状图(每个属性角色的生命值/防御力/攻击力的max与min的对比)。 """绘图""" from pyecharts.charts import Timeline from find_type import FindType import pandas …