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

运行时类信息(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.mfbz.cn/a/69138.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

ubuntu 安装 nvidia 驱动

ubuntu 安装 nvidia 驱动 初环境与设备查询型号查询对应的驱动版本安装驱动验证驱动安装结果 本篇文章将介绍ubuntu 安装 nvidia 驱动 初 希望能写一些简单的教程和案例分享给需要的人 环境与设备 系统&#xff1a;ubuntu 设备&#xff1a;Nvidia GeForce RTX 4090 查询型…

Tcp是怎样进行可靠准确的传输数据包的?

概述 很多时候&#xff0c;我们都在说Tcp协议&#xff0c;Tcp协议解决了什么问题&#xff0c;在实际工作中有什么具体的意义&#xff0c;想到了这些我想你的技术会更有所提升&#xff0c;Tcp协议是程序员编程中的最重要的一块基石&#xff0c;Tcp是怎样进行可靠准确的传输数据…

web-ssrf

目录 ssrf介绍 以pikachu靶场为例 curl 访问外网链接 利用file协议查看本地文件 利用dict协议扫描内网主机开放端口 file_get_content 利用file协议查看本地文件&#xff1a; fsockopen() 防御方式: ssrf介绍 服务器端请求伪造&#xff0c;是一种由攻击者构造形成…

CSP复习每日一题(四)

树的重心 给定一颗树&#xff0c;树中包含 n n n 个结点&#xff08;编号 1 ∼ n 1∼n 1∼n&#xff09;和 n − 1 n−1 n−1条无向边。请你找到树的重心&#xff0c;并输出将重心删除后&#xff0c;剩余各个连通块中点数的最大值。 重心定义&#xff1a; 重心是指树中的一…

链式二叉树统计结点个数的方法和bug

方法一&#xff1a; 分治&#xff1a;分而治之 int BTreeSize1(BTNode* root) {if (root NULL) return 0;else return BTreeSize(root->left)BTreeSize(root->right)1; } 方法二&#xff1a; 遍历计数&#xff1a;设置一个计数器&#xff0c;对二叉树正常访问&#…

dubbo之高可用

负载均衡 概述 负载均衡是指在集群中&#xff0c;将多个数据请求分散到不同的单元上执行&#xff0c;主要是为了提高系统的容错能力和对数据的处理能力。 Dubbo 负载均衡机制是决定一次服务调用使用哪个提供者的服务。 策略 在Dubbo中提供了7中负载均衡策略&#xff0c;默…

冒泡排序 简单选择排序 插入排序 快速排序

bubblesort 两个for循环&#xff0c;从最右端开始一个一个逐渐有序 #include <stdio.h> #include <string.h> #include <stdlib.h>void bubble(int *arr, int len); int main(int argc, char *argv[]) {int arr[] {1, 2, 3, 4, 5, 6, 7};int len sizeof(…

想要延长Macbook寿命?这六个保养技巧你必须get!

Mac作为我们工作生活的伙伴&#xff0c;重要性不需要多说。但在使用的过程中&#xff0c;我们总会因不当操作导致Mac出现各种问题。 要想它长久的陪伴&#xff0c;平时的维护与保养自然不能少&#xff0c;Mac的保养很重要的两点就是硬件保养和电脑系统保养&#xff0c;硬件保养…

【一】初步认识数据库

数据库概览数据库 缘起表(Table)的理解用表来定义数据库数据库系统的理解概念层次的理解实例层次的理解 数据库管理系统的理解从用户角度看从系统实现角度看典型的数据库管理系统 数据库语言数据库定义、操纵、控制语言数据库语言 VS 高级语言 内容回顾练习 数据库概览 走马观…

gitblit-使用

1.登入GitBlit服务器 默认用户和密码: admin/admin 2.创建一个新的版本库 点击图中的“版本库”&#xff0c;然后点击图中“创建版本库” 填写名称和描述&#xff0c;注意名称最后一定要加 .git选择限制查看、克隆和推送勾选“加入README”和“加入.gitignore文件”在图中的1处…

2023一带一路东盟工商领袖峰会在曼谷成功举行,发明家周初材被授予中泰友好交流大使

今年是共建“一带一路”倡议提出十周年。十年来&#xff0c;共建“一带一路”倡议从理念到行动&#xff0c;从愿景到现实&#xff0c;开展更大范围、更高水平、更深层次的区域合作&#xff0c;致力于维护全球自由贸易体系和开放型世界经济&#xff0c;推动文明交流互鉴&#xf…

openeuler服务器 ls 和ll 命令报错 command not found...

在openeuler服务器执行 ls 和ll 命令报错 command not found... 大概是系统环境变量导致的问题。 我在安装redis是否没有安装成功后就出现了这样的情况。编辑profile文件没有写正确&#xff0c;导致在命令行下ls 和 ll 等命令不能够识别。 重新设置一下环境变量。 export PAT…

【项目学习1】如何将java对象转化为XML字符串

如何将java对象转化为XML字符串 将java对象转化为XML字符串&#xff0c;可以使用Java的XML操作库JAXB&#xff0c;具体操作步骤如下&#xff1a; 主要分为以下几步&#xff1a; 1、创建JAXBContext对象&#xff0c;用于映射Java类和XML。 JAXBContext jaxbContext JAXBConte…

Oracle 开发篇+Java通过共享模式访问Oracle数据库

标签&#xff1a;共享服务器进程、shared server process释义&#xff1a;shared server process是Oracle的一种数据库连接技术&#xff0c;类似的还有专用模式和DRCP ★ 数据库配置 alter system set shared_server_sessions1 scopespfile; alter system set max_shared_serv…

最大限度增加销售额!亚马逊提醒卖家准备Q4季度促销库存!

亚马逊美国站发布公告称为了最大限度提高卖家销售额&#xff0c;确保您的亚马逊物流库存在第四季度的促销活动中按时到达亚马逊运营中心&#xff0c;亚马逊建议卖家检查补货库存并及时将库存送到运营中心&#xff0c;以下是公告内容&#xff1a; 为了最大限度地提高您的假期销…

Practices9(双指针)|283. 移动零、11. 盛最多水的容器、15. 三数之和

283. 移动零 1.题目&#xff1a; 给定一个数组 nums&#xff0c;编写一个函数将所有 0 移动到数组的末尾&#xff0c;同时保持非零元素的相对顺序。 请注意 &#xff0c;必须在不复制数组的情况下原地对数组进行操作。 示例 1: 输入: nums [0,1,0,3,12] 输出: [1,3,12,0,…

全网最细,Fiddler修改接口返回数据详细步骤实战,辅助接口测试...

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 在测试的过程中&a…

React入门学习笔记3

事件处理 通过onXxx属性指定事件处理函数(注意大小写) React使用的是自定义(合成)事件, 而不是使用的原生DOM事件——为了更好的兼容性 eg&#xff1a;οnclick》onClickReact中的事件是通过事件委托方式处理的(委托给组件最外层的元素)——为了更高效通过event.target得到发生…

新版Android Studio模拟器浮动

&#xff08;水一篇&#xff0c;但其实很多入门同学不知道&#xff09; 安装新版Andorid Studio后会发现模拟器是内嵌在AS中的&#xff0c;如何让她浮动

day5 STM32中断系统

中断的基本概念 在处理器中&#xff0c;中断是一个过程&#xff0c;即CPU正常执行程序的过程中&#xff0c;遇到外部或者内部的紧急时间需要处理&#xff0c;暂时终止当前程序的执行&#xff0c;转而去为处理紧急的时间&#xff0c;待处理完毕后再返回被打断的程序出继续往下执…
最新文章