【MFC】10.MFC六大机制:RTTI(运行时类型识别),动态创建机制,窗口切分,子类化-笔记
运行时类信息(RTTI)
C++:
##是拼接
#是替换成字符串
// RTTI.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#include <iostream>
#include <afxwin.h>#ifdef _DEBUG
#define new DEBUG_NEW
#endifCWinApp 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; //NULLconst AFX_CLASSINIT* m_pClassInit; //NULL
}#define RUNTIME_CLASS(class_name) _RUNTIME_CLASS(class_name)
((CRuntimeClass*)(&SHape::classSHape))
}
这里给出RTTI的图,每一个类中都保存了这样一个结构,相当于链表,我们有当前的类信息,就可以得到所有父类信息:
动态创建机制
如果想在MFC中实现动态创建:
- 也必须继承与CObject类
- 类内声明DECLARE_DYNCREATE
- 类外实现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,//行数,这个值必须不超过16int nCols,//列数,这个值必须不超过16dwStyle,//指定窗口的风格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,//分隔器窗口的最大行数,这个值不能超过2int nMaxCols,//分隔器窗口的最大列数,这个值不能超过2SIZE 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)
- 动态切分,Create方法:
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子类化
这里我们把第一个视图换成一个窗口(对话框)
- 创建一个对话框
- 为该对话框添加一个类:CMyFormView
- 主cpp中,包含刚才添加的类的头文件
- 然后把第一个改为我们创建的类
//我们自己的视图类: 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;} }
- 需要把我们创建的对话框修改为子窗口类,否则会报错
-
DDX/DDV虚函数:
就是空间绑定变量/数据,调用UpdataTA(),交互数据用
控件绑定变量
值绑定变量
相关文章:

【MFC】10.MFC六大机制:RTTI(运行时类型识别),动态创建机制,窗口切分,子类化-笔记
运行时类信息(RTTI) C: ##是拼接 #是替换成字符串 // RTTI.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。 // #include <iostream> #include <afxwin.h>#ifdef _DEBUG #define new DEBUG_NEW #endifCWinApp th…...

ui设计师个人年终工作总结2020最新范文5篇
ui设计师个人年终工作总结(一) 我于20年x育人x日成为公司的试用员工,安排在技术研发中心担任UI界面设计一职,现将我的工作情况分为三个阶段来总结如下汇报: 一、萌芽阶段 记得初次应聘时,我对公司的认识仅仅局限于行业之一&#x…...

开源数据库Mysql_DBA运维实战 (修改root密码)
MySQL——修改root密码的4种方法 本文以windows为例为大家详细介绍下MySQL修改root密码的4种方法,大家可以可以根据的自己的情况自由选择,希望对大家有所帮助 方法1: 用SET PASSWORD命令 首先登录MySQL。 格式:mysql> set pass…...
javap获取Kotlin方法JNI方法签名
获取Kotlin方法签名和JAVA不一样的地方就是需要使用Kotlin 命令行编译器生成.class文件: 编写一个Kotlin类,添加JNI方法: class TestLib {external fun init(callBack: CallBack)interface CallBack{fun onData(count:Int,data:String)} }在…...

ARM-M0内核MCU,内置24bit ADC,采样率4KSPS,传感器、电子秤、体脂秤专用,国产IC
ARM-M0内核MCU 内置24bit ADC ,采样率4KSPS flash 64KB,SRAM 32KB 适用于传感器,电子秤,体脂秤等等...

【STM32】FreeRTOS消息队列和信号量学习
一、消息队列(queue) 队列是一种用于实现任务与任务之间,任务与中断之间消息交流的机制。 注意:1.数据的操作是FIFO模式。 2.队列需要明确数据的大小和队列的长度。 3.写和读都会出现堵塞。 实验:创建一个消息队列…...

初始C语言(6)——详细讲解表达式求值以及其易错点
系列文章目录 第一章 “C“浒传——初识C语言(1)(更适合初学者体质哦!) 第二章 初始C语言(2)——详细认识分支语句和循环语句以及他们的易错点 第三章 初阶C语言(3)——…...

【数据结构】树和二叉树
一、树的概念及结构 1、树的概念 树 是一种非线性的数据结构,它是由n(n>0)个有限结点组成一个具有层次关系的集合。把它叫做树是因 为它看起来像一棵倒挂的树,也就是说它是根朝上,而叶朝下的。 有一个特殊的结点&a…...
GPIO 配置 和 PINCTRL有啥区别
GPIO(通用输入/输出)和 PINCTRL(引脚控制器)是在嵌入式系统中用于管理和控制硬件引脚的关键概念。它们在硬件层面上起着不同的作用。 GPIO配置: GPIO 是一种通用的硬件接口,用于控制和读取数字信号。每个 …...
GPT法律领域
法律领域 LaWGPT Github: https://github.com/pengxiao-song/LaWGPT 简介:基于中文法律知识的大语言模型。 数据:基于中文裁判文书网公开法律文书数据、司法考试数据等数据集展开,利用Stanford_alpaca、self-instruct方式生成对话问答数据…...
【C++11保姆级教程】Type aliases(类型别名)、alignof and alignas(类型对齐))
文章目录 前言一、类型别名(Type aliases)1.1类型别名是什么?1.2使用方法1.3实际使用1.4优势 二、类型对齐(alignof and alignas)2.1类型对齐的概念2.2类型对齐快速理解2.3具体使用2.4示例代码 总结 前言 在C11标准中…...

地址解析协议-ARP
ARP协议 无论网络层使用何种协议,在实际网络的链路上传输数据帧时,最终必须使用硬件地址 地址解析协议(Address Resolution Protocol,ARP):完成IP地址到MAC地址的映射,每个主机都有一个ARP高速缓…...
Java线程
文章目录 一、Thread类1.1创建线程1.2Thread类中的一些构造方法1.3Thread类中的一些属性1.4线程的终止/打断1.5线程等待1.6获取当前线程的引用1.7休眠当前线程 二、线程的状态 一、Thread类 线程是操作系统的概念,操作系统内核实现了线程这样的机制,系统…...

C语言如何实现DES加密与解密
C语言实现DES加密解密 #include "des.h" //移位表 static Table_size const shiftTable[NumberOfKeys] {1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1}; //E扩展表 static Table_size const eTable[des_key_pc2_standard]{32, 1, 2, 3, 4, 5, 4, 5, 6, …...
【笔记】优先队列(priority_queue/set)
目录 大根堆 小根堆 set(小根堆) 大根堆 题目链接:洛谷 P3243 菜肴制作 题目描述 知名美食家小 A 被邀请至 ATM 大酒店,为其品评菜肴。ATM 酒店为小 A 准备了 n 道菜肴,酒店按照为菜肴预估的质量从高到低给予 1 到…...

看看安森美深力科NSI45090JDT4G 是如何点亮汽车内外照明系统解决方案
关于线性恒流调节器(CCR):是一种用于控制电流的稳定输出。它通常由一个功率晶体管和一个参考电流源组成。CCR的工作原理是通过不断调节功率晶体管的导通时间来维持输出电流的恒定。当输出电流超过设定值时,CCR会减少功率晶体管的导…...
Linux进阶之Shell-sed
基本用法: sed 选项 “指令” 文件 常用选项: -e --它告诉sed将下一个参数解释为一个sed指令,只有当命令行上给出多个sed指令时使用 -f --后跟保存了sed指令的文件 -i --直接对内容进行修改,不加 i 时默认只是预…...
前端高频面试题 Day02
面试题 var 和 let const 的区别 var 是 ES5 及之前的语法,let const 是 ES6 语法var 和 let 是变量,可修改;const 是常量,不可修改var 有变量提升,let const 没有var 没有块级作用域,let const 有 &…...

MYSQL完全卸载、安装与账号创建、权限控制
一、卸载mysql CentOS 卸载 MySQL 1. 查看安装情况 使用以下命令查看当前安装mysql情况,查找以前是否装有mysql rpm -qa|grep -i mysql这里显示我安装的 MySQL 服务有有: 2. 停止 mysql 服务、删除之前安装的 mysql 删除命令:rpm -e –n…...
get与post如何拼接url与数据的灵活处理,循环的重要性。
get与post拼接url地址不同: let postData {method: "post",data: {op: "/api/setting/maintenanceperiod?period"this.authorizationCode,loadingConfig: {},data: {period:this.authorizationCode}}}; if(this.editData.id){let postData …...
Vim 调用外部命令学习笔记
Vim 外部命令集成完全指南 文章目录 Vim 外部命令集成完全指南核心概念理解命令语法解析语法对比 常用外部命令详解文本排序与去重文本筛选与搜索高级 grep 搜索技巧文本替换与编辑字符处理高级文本处理编程语言处理其他实用命令 范围操作示例指定行范围处理复合命令示例 实用技…...

VB.net复制Ntag213卡写入UID
本示例使用的发卡器:https://item.taobao.com/item.htm?ftt&id615391857885 一、读取旧Ntag卡的UID和数据 Private Sub Button15_Click(sender As Object, e As EventArgs) Handles Button15.Click轻松读卡技术支持:网站:Dim i, j As IntegerDim cardidhex, …...
在HarmonyOS ArkTS ArkUI-X 5.0及以上版本中,手势开发全攻略:
在 HarmonyOS 应用开发中,手势交互是连接用户与设备的核心纽带。ArkTS 框架提供了丰富的手势处理能力,既支持点击、长按、拖拽等基础单一手势的精细控制,也能通过多种绑定策略解决父子组件的手势竞争问题。本文将结合官方开发文档,…...

2.Vue编写一个app
1.src中重要的组成 1.1main.ts // 引入createApp用于创建应用 import { createApp } from "vue"; // 引用App根组件 import App from ./App.vue;createApp(App).mount(#app)1.2 App.vue 其中要写三种标签 <template> <!--html--> </template>…...
鱼香ros docker配置镜像报错:https://registry-1.docker.io/v2/
使用鱼香ros一件安装docker时的https://registry-1.docker.io/v2/问题 一键安装指令 wget http://fishros.com/install -O fishros && . fishros出现问题:docker pull 失败 网络不同,需要使用镜像源 按照如下步骤操作 sudo vi /etc/docker/dae…...
Web 架构之 CDN 加速原理与落地实践
文章目录 一、思维导图二、正文内容(一)CDN 基础概念1. 定义2. 组成部分 (二)CDN 加速原理1. 请求路由2. 内容缓存3. 内容更新 (三)CDN 落地实践1. 选择 CDN 服务商2. 配置 CDN3. 集成到 Web 架构 …...
Python ROS2【机器人中间件框架】 简介
销量过万TEEIS德国护膝夏天用薄款 优惠券冠生园 百花蜂蜜428g 挤压瓶纯蜂蜜巨奇严选 鞋子除臭剂360ml 多芬身体磨砂膏280g健70%-75%酒精消毒棉片湿巾1418cm 80片/袋3袋大包清洁食品用消毒 优惠券AIMORNY52朵红玫瑰永生香皂花同城配送非鲜花七夕情人节生日礼物送女友 热卖妙洁棉…...
基于Java Swing的电子通讯录设计与实现:附系统托盘功能代码详解
JAVASQL电子通讯录带系统托盘 一、系统概述 本电子通讯录系统采用Java Swing开发桌面应用,结合SQLite数据库实现联系人管理功能,并集成系统托盘功能提升用户体验。系统支持联系人的增删改查、分组管理、搜索过滤等功能,同时可以最小化到系统…...

AI病理诊断七剑下天山,医疗未来触手可及
一、病理诊断困局:刀尖上的医学艺术 1.1 金标准背后的隐痛 病理诊断被誉为"诊断的诊断",医生需通过显微镜观察组织切片,在细胞迷宫中捕捉癌变信号。某省病理质控报告显示,基层医院误诊率达12%-15%,专家会诊…...
C#学习第29天:表达式树(Expression Trees)
目录 什么是表达式树? 核心概念 1.表达式树的构建 2. 表达式树与Lambda表达式 3.解析和访问表达式树 4.动态条件查询 表达式树的优势 1.动态构建查询 2.LINQ 提供程序支持: 3.性能优化 4.元数据处理 5.代码转换和重写 适用场景 代码复杂性…...