2409wtl,切换视图

news/2024/9/17 7:38:43/ 标签: wtl

原文

介绍

我从一个基于SDI(单文档接口)WTL向导的应用开始,添加了一些从控件继承窗口一些对话框窗口(表单视图),然后才发现我必须,使SDI框架动态加载和卸载子窗口.

本文演示了两个可用来完成的技术:在SDI应用中的视图动态切换.这是我使用的两个.

技术

1技术:第一个方法涉及按需析构和重建视图实例.这更简单,且在不介意析构和重建窗口对象时效果很好.

2:按需创建视图,然后用Win32函数SetWindowLongPtr直接更改内部标识,在后续选择保留这些视图.
现已弃用SetWindowLong.

背景

回到SDI,在叫m_hWndClient的公共变量中,主框架存储子视图的(HWND)句柄.

不要

一开始可能会想重新赋值新的子窗口给框架的m_hWndClient然后更新布局来解决问题.

//在你的`框架`类中的某个位置
this->m_hWndClient = m_hWndNewView; //
//不管用!!`m_hWndNewView`是我想切换到的视图的句柄
UpdateLayout();

可惜,这不管用,主要是因为框架不知道提供句柄的窗口.

“把戏”

解决的技巧,是了解窗口隐式引用框架窗口中的第一个不是控制栏但恰好是子视图"面板".

MFC中,此面板标识为AFX_IDW_PANE_FIRST.如果你在ATL(atlres.h)查看,有一个名字相似的定义,叫ATL_IDW_PANE_FIRST.但两者有相同"0xE900"值.

如前,可析构当前的子"视图",创建新视图,然后再赋值新视图的句柄(隐式设置第一个面板ID)–技术#1;

或,可显式更改两个视图的ID,这样ATL_IDW_PANE_FIRST不再是当前视图ID,然后用一些直接的窗口调用给新视图赋值此ID.

有趣的是,技术#1不需要切换ID,所以我猜测,当你创建第二个视图时,它肯定有不同内部ID,框架或窗口会重新给ID赋值"0xE900".

如果不切换ID,而只按其父窗柄该框架创建第二个视图,则只要它存在,该框架就会继续按其子视图引用第一个视图.

技巧#1:析构并重建视图

BitmapView示例中查找名为TogglePrintPreview()的函数.

使用不同版本,这样示例代码演示和源码匹配.我还扩展它,这样可支持多个视图.步骤大致为:
1,创建新视图
2,给框架的m_hWndClient赋值新视图窗柄
3,显示新视图
4,析构旧视图
5,更新窗口
6,可选:更新框架的预翻译消息方法以包含新视图的覆盖.

 //`视图`只是一个枚举,这使得更加方便定位`视图`.对想切换到的`每个视图/对话框类`,都有个定义的`视图`枚举.
//可用简单的整数,成员窗口句柄或区分请求视图和当前视图.enum VIEW {BASIC, DIALOG, EDIT, NONE};
//成员视图,,
CBasicView m_view; //向导继承的基本视图
CEditView m_edit; //向导继承的基本对话框,
CBasicDialog m_dlg; //向导继承的基本编辑控件视图
...
void SwitchView(VIEW view)
{//旧视图和新视图的指针CWindow *pOldView, *pNewView;//取当前窗口/视图,在下面定义pOldView = GetCurrentView(); ////取/创建请求的视图pNewView = GetNewView(view); //在下面定义//检查请求的视图是当前视图还是默认视图if(!pOldView || !pNewView || (pOldView == pNewView))return; //闲着显示/隐藏隐藏旧窗口//显示新窗口删除旧视图pOldView->ShowWindow(SW_HIDE); //pNewView->ShowWindow(SW_SHOW); //pOldView->DestroyWindow();//要求`框架`更新客户UpdateLayout();
}

GetCurrentView()是一个比较m_hWndClient每个视图的句柄,然后返回匹配的视图,并转换为CWindow的助手函数.这样:

//取当前视图的助手方法~不可用`MFC`的`GetActiveView()`!
CWindow* GetCurrentView()
{if(!m_hWndClient)return NULL;if(m_hWndClient == m_view.m_hWnd)return (CWindow*)&m_view;else if(m_hWndClient == m_dlg.m_hWnd)return (CWindow*)&m_dlg;else if(m_hWndClient == m_edit.m_hWnd)return (CWindow*)&m_edit;elsereturn NULL;
}

GetNewView(VIEWview)是一个返回请求视图,并转换为CWindow助手函数.
在此过程中,它会按需创建视图对象,并给框架的m_hWndClient赋值其句柄.这样:

//取/创建新视图的助手方法
CWindow* GetNewView(VIEW view)
{CWindow* newView = NULL;//现在设置请求的视图switch(view){case BASIC://如果不存在,则创建它,并按`框架`的`m_hWndClient`设置`引用`if(m_view.m_hWnd == NULL)m_view.Create(m_hWnd);m_hWndClient = m_view.m_hWnd;newView = (CWindow*)&m_view;break;case DIALOG:if(m_dlg.m_hWnd == NULL)m_dlg.Create(m_hWnd);m_hWndClient = m_dlg.m_hWnd;newView = (CWindow*)&m_dlg;break;case EDIT:if(m_edit.m_hWnd == NULL)m_edit.Create(m_hWnd);m_hWndClient = m_edit.m_hWnd;newView = (CWindow*)&m_edit;break;}return newView;
}

1,上面函数是直接的.SwitchView(VIEWview)首先调用GetCurrentView()以取当前视图的引用.
2,然后,它调用GetNewView(VIEWview)来取请求视图的引用,并在必要创建该视图.它还给框架的m_hWndClient赋值新视图的句柄.

3,如果新视图或旧视图无效彼此相等(即用户已按自身更改当前视图),则它不会操作.

4,然后SwitchView(VIEWview)隐藏旧视图,并显示新视图.

5,最后,它析构了旧的视图.最后一步隐式按ATL_IDW_PANE_FIRST更改新视图的内部ID.

如上,还应该考虑更新框架预翻译消息覆盖,以确保视图可处理消息自己的预翻译消息.

预翻译消息实质上允许框架和/或视图预览消息,并在翻译和分发消息前处理它们.返回以避免翻译和分发消息.

大多数应用不会覆盖预翻译消息,除非需要特殊处理消息,如子类化许多控件时.

也即,WTL向导自动在CWindowImplCDialogImpl视图中生成预翻译消息函数,且还会添加从主机的预翻译消息消息路由它们期望代码,这是必须确保从主机路由消息视图另一个原因.

以下是我修改框架预翻译消息以使视图查看消息的方法:

//在`CMainFrame`中实现
virtual BOOL PreTranslateMessage(MSG* pMsg)
{if(CFrameWindowImpl<CMainFrame>::PreTranslateMessage(pMsg))return TRUE;if(m_hWndClient != NULL){//为当前视图调用`预翻译消息`取当前视图(转换为`CWindow*`)~函数,如上所示CWindow* pCurrentView = GetCurrentView(); //if(m_view.m_hWnd == pCurrentView->m_hWnd)return m_view.PreTranslateMessage(pMsg);else if(m_dlg.m_hWnd == pCurrentView->m_hWnd)return m_dlg.PreTranslateMessage(pMsg);else if(m_edit.m_hWnd == pCurrentView->m_hWnd)return m_edit.PreTranslateMessage(pMsg);}return FALSE;
}

在此,我首先确保该框架有效的子句柄,然后调用GetCurrentView()函数来返回CWindow*.然后,我用每个视图比较该CWindow*窗柄成员.

这样用视图来调用视图自己的预翻译消息.我不能CWindow来调用它,因为它没有实现预翻译消息.

你不需要路由消息到未实现预翻译消息视图.

即,预翻译消息CMessageFilter接口中的唯一方法,也是主框架实现的方法.但是,它不在CWindowImplCDialogImpl继承层次中.

即它不是隐式可用的,也不一定都需要.

技巧#2:析构并重建视图

此时,只需简单的更改SwitchView方法,即可在切换持久保存视图,而不是析构它们.

//删除旧视图
pOldView->DestroyWindow();
//用如下替换:
C++
//更改当前视图的`ID`,使它不是框架中的第一个子项pOldView->SetWindowLongPtr(GWL_ID, 0);
//按框架的第一个/子项,设置`面板新视图`
pNewView->SetWindowLongPtr(GWL_ID, ATL_IDW_PANE_FIRST);

就是这样!如上,框架使用第一个面板ID更新其客户视图,因此需要按ATL_IDW_PANE_FIRST以外的其他内容更改当前视图GWL_ID,然后按ATL_IDW_PANE_FIRST更改新视图的GWL_ID.

使用代码

可在任意地方调用SwitchView,只需使用与要求视图对应的视图枚举调用它即可.如SwitchView(BASIC)SwitchView(EDIT).

如果想使用实现,需要:
1,更新enum VIEW{}以包含每个视图的标识.
2,在每个视图的主框架中,添加成员变量.如CMyView m_myView.

3,更新GetNewView(VIEW)中的语句,以包含每个视图枚举和视图成员的例子.

4,更新GetCurrentView()以返回每个视图成员CWindow*引用.

5,(可选)更新框架的预翻译消息方法,以调用视图自己的预翻译消息方法.

实际上,一般想使用一个方法或另一个方法,但这为你提供了在同一个应用中同时使用这两个方法的选项.有效更改包括:

void SwitchView(VIEW view, BOOL bPreserve = FALSE) //默认析构
{...if(bPreserve){//使用技巧`#2`}else{//使用技巧`#1`}
}

结束语

见,SDIMultiView_src.


http://www.ppmy.cn/news/1522248.html

相关文章

指针作为函数参数详解

一级指针传参 形参指针的指向没有被改变 void test(int* p1) {*p1 8; }int main() {int a 5;int* p &a;test(p);printf("%d\n", a); }输出 8总结: 由代码和上图可知&#xff0c;实参p是个指针&#xff0c;其值为变量a的地址&#xff0c;将其传参给形参p1&…

webpack+lite-server 构建项目示例

首先安装以下库 npm install --save-dev webpack webpack-cli lite-server npm install --save-dev babel-loader babel/core babel/preset-env项目结构 webpack.config.js 配置 const path require("path");module.exports {entry: "./src/index.js",…

5G前传-介绍

1. 引用 知识分享系列一&#xff1a;5G基础知识-CSDN博客 5G前传的最新进展-CSDN博客 灰光和彩光_通信行业5G招标系列点评之二&#xff1a;一文读懂5G前传-光纤、灰光、彩光、CWDM、LWDM、MWDM...-CSDN博客 术语&#xff1a; 英文缩写描述‌BBU&#xff1a;Building Baseba…

华为云征文|Flexus云服务X实例安装ODBC驱动,在ODBC中建立MySQL数据库连接,通过QT连接云数据库

引出 4核12G-100G-3M规格的Flexus X实例使用测评第2弹&#xff1a;Flexus云服务X实例安装ODBC驱动&#xff0c;在ODBC中建立MySQL数据库连接&#xff0c;通过QT连接云数据库 什么是Flexus云服务器X实例 官方解释&#xff1a; Flexus云服务器X实例是新一代面向中小企业和开发…

基于发布-订阅模型的音视频流分发框架

有时需要同时网络推流和把流封装为某格式&#xff0c;或做一些其它操作。这就需要一个分发流的机制&#xff0c;把同一路流分发给多个使用者去操作&#xff0c;下面实现了一个简易的线程安全的音视频流分发框架。代码如下&#xff1a; avStreamHub.h #ifndef STREAMHUB_H #def…

算法专题一: 双指针

目录 前言1. 移动零&#xff08;easy&#xff09;2. 复写零&#xff08;easy&#xff09;3. 快乐数&#xff08;medium&#xff09;4. 盛水最多的容器&#xff08;medium&#xff09;5. 有效三角形的个数&#xff08;medium&#xff09;6. 和为 s 的两个数字&#xff08;easy&a…

Docker 进阶构建:镜像、网络与仓库管理

目录 三. docker镜像构建 1. docker镜像结构 2. 镜像运行的基本原理 3. 镜像获得方式 4. 镜像构建 5. Dockerfile实例 6. 镜像优化方案 6.1. 镜像优化策略 6.2. 镜像优化示例:缩减镜像层 6.3. 镜像优化示例:多阶段构建 6.4. 镜像优化示例:使用最精简镜像 四. docke…

网络安全服务基础Windows--第15节-CA与HTTPS理论

公钥基础设施&#xff08;Public Key Infrastructure&#xff0c;简称 PKI&#xff09;是指⼀套由硬件、软件、⼈员、策略和程序组成的系统&#xff0c;⽤于创建、管理、分发、使⽤、存储和撤销数字证书。PKI 的核⼼⽬的是通过使⽤公钥加密技术来确保电⼦通信的安全性。PKI 为数…

八月二十九日(day 39)docker6

1.前端&#xff08;nginx&#xff09; [rootlocalhost ~]# docker pull nginx //拉取nginx镜像 [rootlocalhost ~]# docker images REPOSITORY TAG IMAGE ID CREATED SIZE nginx latest 5ef79149e0ec 2 we…

springboot数据库连接由localhost改成IP以后访问报错500(2024/9/7

步骤很详细&#xff0c;直接上教程 情景复现 一.没改为IP之前正常 二.改完之后报错 问题分析 SQL没开启远程连接权限 解决方法 命令行登入数据库 mysql -u root -p切换到对应数据库 use mysql;设置root用户的连接权限允许其他IP连接数据库 update user set host % whe…

前端技术(六)—— AJAX详解

一、原生 AJAX 1. AJAX 简介 AJAX 全称为 Asynchronous JavaScript And XML&#xff0c;就是异步的 JS 和 XML。 通过 AJAX 可以在浏览器中向服务器发送异步请求&#xff0c;最大的优势&#xff1a;无刷新获取数据。 AJAX 不是新的编程语言&#xff0c;而是一种将现有的标准组…

C语言程序设计(初识C语言后部分)

留一片空白&#xff0c;随时浓墨重彩。 二十&#xff0c;结构体 结构体类型的声明 结构体初始化 结构体成员访问 结构体传参 1.结构体的声明 1&#xff09;结构的基础知识 结构是一些值的集合&#xff0c;这些值称为成员变量。结构的每个成员可以是不同类型的变量。 2&…

上海网站设计-网站手机端制作

随着移动互联网的迅猛发展&#xff0c;越来越多的人通过手机上网&#xff0c;这使得网站手机端的设计和制作变得尤为重要。在这种背景下&#xff0c;上海的网站设计行业迎来了新的机遇与挑战。 首先&#xff0c;网站手机端制作的必要性不容忽视。根据统计数据显示&#xff0c;手…

Flask框架 完整实战案例 附代码解读 【3】

Flask 是一个轻量级的可定制框架&#xff0c;使用Python语言编写&#xff0c;较其他同类型框架更为灵活、轻便、安全且容易上手。 前面已经写过项目从新建运行安装到测试部署的全流程&#xff0c;其中有写Flask框架从新建到部署全流程&#xff0c;但是只有部分代码。本篇主要是…

微软发布Phi-3.5 SLM,附免费申请试用

Phi-3 模型系列是Microsoft 小型语言模型 (SLM) 系列中的最新产品。 它们旨在具有高性能和高性价比&#xff0c;在语言、推理、编码和数学等各种基准测试中的表现均优于同类和更大规模的模型。Phi-3 模型的推出扩大了 Azure 客户的高质量模型选择范围&#xff0c;为他们编写和…

通信工程学习:什么是FEC前向纠错

FEC&#xff1a;前向纠错 FEC&#xff08;Forward Error Correction&#xff0c;前向纠错&#xff09;是一种增加数据通信可信度的技术&#xff0c;广泛应用于计算机网络、无线通信、卫星通信等多种数据传输场景中。其基本原理和特点可以归纳如下&#xff1a; 一、FEC前向纠错…

ArcGIS出图格网小数位数设置

1、比如要去掉格网后面的小数点&#xff0c;如何设置呢&#xff1f; 2、如下图设置。

linux 下一跳缓存,early demux(‌早期解复用)‌介绍

3.6版本以后的下一跳缓存 3.6版本移除了FIB查找前的路由缓存。这意味着每一个接收发送的skb现在都必须要进行FIB查找了。这样的好处是现在查找路由的代价变得稳定(consistent)了。3.6版本实际上是将FIB查找缓存到了下一跳(fib_nh)结构上&#xff0c;也就是下一跳缓存下一跳缓存…

Flink SQL 中常见的数据类型

Flink SQL 中常见的数据类型 目标 通过了解Flink SQL 中常见的数据类型,掌握正确编写Flink SQL 语句背景 Apache Flink 支持多种数据类型,这些数据类型被用于 Flink SQL 表达式、Table API 以及 DataStream API 中。以下是 Flink SQL 中常见的数据类型: 基本数据类型 Boo…

初识Linux · 进度条

目录 前言&#xff1a; 1 缓冲区和回车换行 2 进度条 前言&#xff1a; 我们目前学习了些许知识&#xff0c;已经足够支持我们写一个非常非常小的项目了&#xff0c;即进度条&#xff0c;相信大家都有过下载游戏&#xff0c;等待游戏更新完成的时候&#xff0c;那么此时就有…