【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的图,每一个类中都保存了这样一个结构,相当于链表,我们有当前的类信息,就可以得到所有父类信息:
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,//行数,这个值必须不超过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)
      

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.hqwc.cn/news/61935.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

pytest fixture 高级使用

一、fixture中调用fixture 举例&#xff1a; 输出&#xff1a; 说明&#xff1a;登录fixture 作为参数传递到登出方法中&#xff0c;登录方法的返回值就可以被登出方法使用 二、在fixture中多参数的传递&#xff08;通过被调用函数传参&#xff09; 举例&#xff1a; 输出&a…

开源力量再现,国产操作系统商业化的全新探索

文章目录 1. 开源运动的兴起2. 开源力量的推动3. 国产操作系统的崭露头角3.1 国产操作系统有哪些 4.国产操作系统的商业化探索5.开源力量对国产操作系统商业化的推动 操作系统作为连接硬件、中间件、数据库、应用软件的纽带&#xff0c;被认为是软件技术体系中最核心的基础软件…

bye 我的博客网站

Bye&#x1f64b;&#x1f64b;&#x1f64b;&#xff0c;我的博客网站。在我的服务器上运行了9个月之久的博客网站要和大家Bye了。 背景 可能很多人不知道我的这个博客网站的存在&#xff0c;好吧&#xff0c;最后一次展示它了&#xff0c;博客网站地址在这里&#xff0c;它…

vs2022+qt6.24+Cef编译

1.QCefView源码下载地址 https://github.com/cefview/qcefview2.目录层级关系如下&#xff1a; 3.下载CefViewCore git pull --regit pull --recurse-submodules上面命令失败直接用下面的命令 git clone gitgithub.com:CefView/CefViewCore.git4.编译QCefView准备工作 a.准…

10-数据结构-队列(C语言)

队列 目录 目录 队列 一、队列基础知识 二、队列的基本操作 1.顺序存储 ​编辑 &#xff08;1&#xff09;顺序存储 &#xff08;2&#xff09;初始化及队空队满 &#xff08;3&#xff09;入队 &#xff08;4&#xff09;出队 &#xff08;5&#xff09;打印队列 &…

typecho 全站开启Https证书访问

原文地址:https://zhuoyue360.com/jyjl/107.html typecho 全站开启Https证书访问 https://zhuoyue360.com/ 网站已经很久没更新了, 最近决定重新把博客捡起来. 今天把ssl证书倒腾了一下,做个小记录! 1. 前提步骤 拥有SSL证书已将域名解析到服务器上&#xff0c;并配置了 Ngi…

uni-app使用vue语法进行开发注意事项

目录 uni-app 项目目录结构 生命周期 路由 路由跳转 页面栈 条件编译 文本渲染 样式渲染 条件渲染 遍历渲染 事件处理 事件修饰符 uni-app 项目目录结构 组件/标签 使用&#xff08;类似&#xff09;小程序 语法/结构 使用vue 具体项目目录如下&#xff1a; 生命…

如何在docker部署一个python项目

导语&#xff1a; 我之前已经实现了在服务器上直接部署一个文件&#xff0c;但是那种部署方式有个明显的缺陷&#xff1a;我如果需要在其他机器部署该项目时&#xff0c;需要重新配置项目所依赖的环境。因此我们需要一种只需要配置一次环境依赖&#xff0c;就可以在其他机器上随…

日期切换

组件&#xff1a;<template><div class"time-picker"><el-radio-group size"small" v-model"timeType" change"changePickerType"><el-radio-button label"hour" v-if"isShow">时</el…

【gridsample】地平线如何支持gridsample算子

文章目录 1. grid_sample算子功能解析1.1 理论介绍1.2 代码分析1.2.1 x,y取值范围[-1,1]1.2.2 x,y取值范围超出[-1,1] 2. 使用grid_sample算子构建一个网络3. 走PTQ进行模型转换与编译4. 走QAT进行模型转换与编译 实操以J5 OE1.1.60对应的docker为例 1. grid_sample算子功能解析…

Rust语法:变量,函数,控制流,struct

文章目录 变量可变与不可变变量变量与常量变量的Shadowing标量类型整数 复合类型 函数控制流if elseloop & whilefor in structstruct的定义Tuple Structstruct的方法与函数 变量 可变与不可变变量 Rust中使用let来声明变量&#xff0c;但是let声明的是不可变变量&#x…

android APP内存优化

Android为每个应用分配多少内存 Android出厂后&#xff0c;java虚拟机对单个应用的最大内存分配就确定下来了&#xff0c;超出这个值就会OOM。这个属性值是定义在/system/build.prop文件中. 例如&#xff0c;如下参数 dalvik.vm.heapstartsize8m #起始分配内存 dalvik.vm.…