WTL学习笔记——(5)工具条与状态条

2023-05-16

一、对第三部分的介绍

自从作为Windows 95的通用控件出现以来,工具条和状态条就变成了很普遍的事物。由于MFC支持浮动的工具条从而使它们更受欢迎。随着通用控件的更新,Rebars(最初 被称为Coollbar)使得工具条有了另一种展示方式。在第三部分,我将介绍WTL对这些控制条的支持和如何在你的程序中使用它们。

二、主窗口的工具条和状态条

CFrameWindowImpl有三个HWND类型的成员变量在窗口创建时被初始化,我们已经见过m_hWndClient,它是填充主窗口客户区的“视图”窗口的句柄,现在我们遇到了另外两个:

  • m_hWndToolBar: 工具条或Rebar的窗口句柄
  • m_hWndStatusBar: 状态条的窗口句柄

CFrameWindowImpl只支持一个工具条,也没有像MFC那样的可多点停靠的工具条,如果你想使用多个工具条又不想修改CFrameWindowImpl的内部代码,你就需要使用Rebar。我将介绍它们二者并演示如何使用应用程序向导添加工具条和Rebar。

CFrameWindowImpl::OnSize()消息响应函数调用了UpdateLayout(),UpdateLayout()做两件事: 从新定位所有控制条和改变视图窗口的大小使之填充整个客户区。实际工作是由UpdateBarsPosition()完成 的,UpdateLayout()只是调用了该函数。实现的代码相当简单,向工具条和状态条发送WM_SIZE消息,由这些控制条的默认窗口处理过程将它 们定位到主窗口的顶部或底部。

当你告诉应用程序向导给你的窗口添加工具条和状态条时,向导就在CMainFrame::OnCreate()中添加了创建它们的代码。现在我们来看看这些代码,当然是为了再写一个时钟程序。

三、向导为工具条和状态条生成得代码

我们将开始一个新的工程,让向导为主窗口创建工具条和状态条。首先创建一个名为WTLClock2的新工程,在向导的第一页,选SDI并使“生成CPP文件”检查框被选中:

1

在第二页,取消Rebar使向导仅仅创建一个普通的工具条:

3

从第二部分的程序中复制相应的代码,新程序看起来是这样的:

3

四、CMainFraCMainFrame 如何创建工具条和状态条
在这个例子中,向导向CMainFrame::OnCreate()函数添加了更多的代码,这些代码的作用就是创建控制条并通知CUpdateUI更新工具条上的按钮。

这是新添加的代码的开始部分,CFrameWindowImpl::CreateSimpleToolBar()函数使用资源 IDR_MAINFRAME创建工具条并将其句柄赋值给m_hWndToolBar,下面是CreateSimpleToolBar()函数的代码:

参数:

nResourceID
工具条资源得ID。如果使用默认值0作为参数,程序将使用DECLARE_FRAME_WND_CLASS宏指定得资源,这里使用的IDR_MAINFRAME是向导生成的代码。
dwStyle
工具条的类型或样式。默认值ATL_SIMPLE_TOOLBAR_STYLE被定义为TBSTYLE_TOOLTIPS,子窗口和可见三种风格的结合,这使得鼠标移到按钮上时工具条会弹出工具提示。
nID
工具条的窗口ID,通常都会使用默认值。

CreateSimpleToolBar()首先检查是否已经创建了一个工具条,然后调用CreateSimpleToolBarCtrl()函数 创建工具条控制,CreateSimpleToolBarCtrl()返回的工具条控制句柄保存在m_hWndToolBar中。 CreateSimpleToolBarCtrl()负责读出资源并创建相应的工具条按钮,然后返回工具条窗口的句柄。这部分的代码相当长,我不在这里做 具体介绍,如果你对此感兴趣得话何以在atlframe.h中找到这些代码。

OnCreate()函数接下来会调用CFrameWindowImpl::CreateSimpleStatusBar()函数,此函数创建状态条并将句柄存在m_hWndStatusBar,下面是该函数的代码:

显示在状态条的文字是从字符串资源中装载的,这个函数的参数是:

nTextID
用于在状态条上显示的字符串的资源ID,向导生成的ATL_IDS_IDLEMESSAGE对应的字符串是“Ready”。
dwStyle
状态条的样式。默认值包含了SBARS_SIZEGRIP风格,这使得状态条的右下角会显示一个改变窗口大小的标志。
nID
状态条的窗口ID,通常都会使用默认值。

CreateSimpleStatusBar()调用另外一个重载函数创建状态条:

这个重载的版本首先检查是否已经创建了状态条,然后调用CreateStatusWindow()创建状态条,状态条的句柄存放在m_hWndStatusBar中。

五、显示和隐藏工具条和状态条

CMainFrame类也有一个视图菜单,它有两个命令:显示/隐藏工具条和状态条,它们的ID是ID_VIEW_TOOLBAR和 ID_VIEW_STATUS_BAR。CMainFrame类有这两个命令的响应函数,分别显示和隐藏相应的控制条,下面是 OnViewToolBar()函数的代码:

这些代码翻转控制条的显示状态,相应的翻转View|Toolbar菜单上的检查标记,然后调用UpdateLayout()重新定位控制条并改变视图窗口的大小。

六、工具条和状态条的内在特征

MFC的框架提供了很多好的特性,例如工具条按钮的工具提示和菜单项的掠过式帮助。WTL中相对应的功能实现在CFrameWindowImpl类中。下面的屏幕截图显示了工具提示和掠过式帮助。

4 5

CFrameWindowImplBase类有两个消息相应函数用来实现这些功能,OnMenuSelect()处理WM_MENUSELECT消 息,它像MFC那样查找掠过式帮助的字符串:首先装载与菜单资源ID相同的字符串资源,在字符串中查找 /n 字符,使用/n之前的内容作为掠过帮助的内容。OnToolTipTextA() 和 OnToolTipTextW() 函数分别响应 TTN_GETDISPINFOA消息和TTN_GETDISPINFOW消息,提供工具条按钮的工具提示。这两个处理函数和 OnMenuSelect()函数一样装载相应的字符串,只是使用/n后面的字符串。(边注:OnMenuSelect()和 OnToolTipTextA()函数对于DBCS字符是不安全的,因为它在查找/n字符时没有检查DBCS字符串的头部和尾部)下面是工具条及其关联的 帮助字符串的例子:

6

七、创建不同样式的工具条

如果你不喜欢在工具条上显示3D按钮(尽管从可用性观点来看平面的界面元素是件糟糕的事情),你可以通过改变 CreateSimpleToolBar()函数的参数来改变工具条的样式。例如,你可以在CMainFrame::OnCreate()使用如下代码创 建一个IE风格的工具条:

如果你使用向导为你的程序添加了manifest文件,它就会在Windows XP系统上使用6.0版的通用控件,你不能选择按钮的类型,工具条会自动使用平面按钮即使你创建工具条时没有添加TBSTYLE_FLAT风格。

八、工具条编辑器

正如我们前面所见,向导为我们的程序创建了几个默认的按钮,当然只有About按钮有事件处理。你可以像在MFC的工程中一样使用工具条编辑器修改 工具条资源,CreateSimpleToolBarCtrl()用这个工具条资源创建工具条。下面是向导生成的工具条在编辑器中的样子:

7

对于我们的时钟程序,我们添加四个按钮,两个按钮用来改变视图窗口的颜色,另外两个用来显示/隐藏工具条和状态条。下面是我们的新工具条:

8

这些按钮是:

  • IDC_CP_COLORS: 将视图窗口颜色改为CodeProject网站的颜色
  • IDC_BW_COLORS: 将视图窗口颜色改为黑白颜色
  • ID_VIEW_STATUS_BAR: 显示或隐藏状态条
  • ID_VIEW_TOOLBAR: 显示或隐藏工具条

前两个按钮都有相应的菜单项,它们都调用视图类的一个新函数SetColor(),向这个函数传递前景颜色和背景颜色,视图窗口用这两个参数改变窗 口的显示。响应这两个按钮的处理函数与响应相应的菜单项的处理函数在使用COMMAND_ID_HANDLER_EX宏上没有区别,你可以查看例子工程的 代码了解这些消息处理的细节。在下一节我将介绍状态条和工具条按钮的UI状态更新,使它们能够反映工具条或状态条当前的状态。

九、工具条按钮的UI状态更新

向导生成的代码已经为CMainFrame添加了对View|Toolbar和View|Status Bar两个菜单项的Check和Uncheck的UI更新处理。这和第二章的程序一样:对CMainFrame类的两个命令使用UI更新的宏:

我们的时钟程序的工具条按钮与对应的菜单项有相同的ID,所以第一步就是为每个宏添加UPDUI_TOOLBAR标志:

还需要添加两个函数响应工具条按钮的更新,但幸运的是向导已经为我们做了,所以如果此时编译这个程序,菜单项和工具条按钮都会更新。

十、使一个工具条支持UI状态更新

如果查看CMainFrame::OnCreate()的代码你就会发现一段新的代码,这段代码设置了两个菜单项的初始状态:

UIAddToolBar()将工具条的窗口句柄传给CUpdateUI,所以当需要更新按钮的状态时CUpdateUI会向这个窗口发消息。另一个重要的调用位于OnIdle()中:

当消息队列中没有消息等待时CMessageLoop::Run()就会调用OnIdle(),UIUpdateToolBar()遍历UI更新 表,寻找那些带有UPDUI_TOOLBAR标志又被UISetCheck()之类的函数改变了状态的的界面元素(当然是工具条),相应的改变按钮的状 态。需要注意得是如果更新弹出式菜单的状态就不需要做以上两步,因为CUpdateUI响应WM_INITMENUPOPUP消息,只有接到此消息时才更 新菜单状态。

如果查看例子代码就会发现它也演示了如何更新框架窗口的菜单条上的顶级菜单项的状态。有一个菜单项是执行Start和Stop命令,起到开始和停止 时钟的作用,当然这需要做一些不平常的事情:菜单条上的菜单项总是处于弹出状态。为了完整的介绍CUpdateUI我将它们也加进例子代码中,要了解它们 可以查找对UIAddMenuBar()和UIUpdateMenuBar()两个函数的调用。

十一、使用Rebar代替简单的工具条

CFrameWindowImpl也支持使用Rebar控件,使你的程序看起来像IE,使用Rebar也是在程序中使用多个工具条的一个方法(译者 加:前面讲过,另一个方法就是修改WTL的源代码)。要使用Rebar需要在向导的第二页选上支持Rebar的检查框,如下所示:

1

第二个例子工程WTLClock3就使用了Rebar控件,如果你正在跟着例子代码学习,那现在就打开WTLClock3。

你首先会注意到创建工具条的代码有些不同,出现这种感觉是因为我们在程序中使用了rebar。以下是相关的代码:

代码从创建工具条开始,只是使用了不同的风格,也就是ATL_SIMPLE_TOOLBAR_PANE_STYLE,它定义在atlframe.h 文件中,与ATL_SIMPLE_TOOLBAR_STYLE风格相似,只是附加了一些诸如CCS_NOPARENTALIGN之类的风格,这是使工具条 作为Rebar的子窗口能够正常工作所必需的风格。

下一行代码是调用CreateSimpleReBar()函数,该函数创建Rebar控件并将句柄存到m_hWndToolBar中。接下来调用AddSimpleReBarBand()函数为Rebar创建一个条位并告诉Rebar这个条位上是一个工具条。

2

CMainFrame::OnViewToolBar()函数也有些不同,它只隐藏Rebar上工具条所在的条位而不是隐藏m_hWndToolBar(如果隐藏m_hWndToolBar将隐藏整个Rebar而不仅仅是工具条)。

如果你使用多个工具条,只需像向导为我们生成的关于第一个工具条的代码那在OnCreate()创建它们并调用 AddSimpleReBarBand()添加到Rebar就行了。CFrameWindowImpl使用标准的Rebar控件,不像MFC那样支持可停 靠的工具条,你所能作得就是排列这些工具条在Rebar中的位置。

十二、多窗格的状态条

WTL另有一个状态条类实现多窗格的状态条,与MFC的默认的状态条一样有CAPS,LOCK和NUM LOCK指示器,这个类就是CMultiPaneStatusBarCtrl,在WTLClock3例子工程中演示了如何使用这个类。这个类支持有限的UI更新,当弹出式菜单被显示时有“Default”属性的窗格会延伸到整个状态条的宽度用于显示菜单的掠过式帮助。

第一步就是在CMainFrame中声明一个CMultiPaneStatusBarCtrl类型的成员变量:

接着在OnCreate()中创建状态条并这只UI更新:

就像CreateSimpleStatusBar()函数做得那样,我们也将状态条的句柄存放在m_hWndStatusBar中。

下一步就是调用CMultiPaneStatusBarCtrl::SetPanes()函数建立窗格:

参数:

pPanes
存放窗格ID的数组
nPanes
窗格ID数组中元素的个数(译者加:就是窗格数)
bSetText
如果是true,所有的窗格被立即设置文字,这一点将在下面解释。

窗格ID可以是ID_DEFAULT_PANE,此ID用于创建支持掠过式帮助的窗格,窗格ID也可以是字符串资源ID。对于非默认的窗格WTL装载这个ID对应的字符串并计算宽度,并将窗格设置为相应的宽度,这和MFC使用的逻辑是一样的。

bSetText控制着窗格是否立即显示相关的字符串,如果是true,SetPanes()显示每个窗格的字符串,否则窗格就被置空。

下面是我们对SetPanes()的调用:

IDPANE_STATUS对应的字符串是“@@@@”,这样应该有足够的宽度(希望是)显示两个时钟状态字符串“Running”和“Stopped”。和MFC一样,你需要自己估算窗格的宽度,IDPANE_CAPS_INDICATOR对应的字符串是“CAPS”。

十三、窗格的UI状态更新

为了更新窗格上的文本,我们需要将相应的窗格添加到UI更新表:

这个宏的第一个参数是窗格的索引而不是ID,这很不幸,因为如果你重新排列了窗格,你要记得更新UI更新表。

由于我们在调用SetPanes()是第三个参数是false,所以窗格初始是空的。我们下一步要做得就是将时钟状态窗格的初始文本设为“Running”

和前面一样,第一个参数是窗格的索引。UISetText()是状态条唯一支持的UI更新函数。

最后,在CMainFrame::OnIdle()中添加对UIUpdateStatusBar()函数的调用,使状态条的窗格能够在空闲时间被更新:

当你使用UIUpdateStatusBar()时CUpdateUI的一个问题就暴露出来了--菜单项的文本在调用UISetText()后没有 改变!如果你在看WTLClock3工程的代码,时钟的开始/停止菜单项被移到了Clock菜单,在菜单项命令的响应处理函数中设置菜单项的文本。无论如 何,如果当前调用的是UIUpdateStatusBar(),那么对UISetText()的调用就不会起作用。我没有研究这个问题是否可以被修复,所 以如果你打算改变菜单的文本,你需要留意这个地方。

最后,我们需要检查CAPS LOCK键的状态,更新相应的两个窗格。这些代码是通过OnIdle()被调用的,所以程序会在每次空闲时间检查它们的状态。

第一次调用UISetText()时将从字符串资源中装载“CAPS”字符串,但是在CString的构造函数中使用了一个机灵的窍门(有充分的文档说明)。

在完成所有的代码之后,状态条看起来是这个样子:

1

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

WTL学习笔记——(5)工具条与状态条 的相关文章

  • 新专栏[飞控] 要开始学点新东西了.和第一步.硬件接线

    我淘宝买了便宜的无人机 stm32的板子 swd烧录接口 uart1串口通信 分两块 一块无人机 一块遥控器 无人机的板子不知上面俩接口 回去要看一下 下图是遥控器的这俩接口 SWD uart1 其中串口通信比较简单 可以直接连已经有的US
  • 部署环境从docker swarm迁移到k8s后kie-server的发布方式变化(二)

    正如后来的考虑 如果外接maven私库照理说是ok的 这样去掉volume的设计整个流程更加的自动化标准化 开搞 第一步先部署一个nexus yaml文件如下 apiVersion v1 kind Namespace metadata na
  • 讨逆檄文..

    五一之后 就要开始我们的征战旅程了 激动不已 说起来当代人是什么样就可以看到后代会是什么样 就像微博里说的 是个人都会让后代不要输起跑线 巨婴的缺点是什么是他什么都输不起 他会放大自己面临的困难 病态的 他没有为自己负责的意识 因为一直被负
  • k8s安装监控工具metrics-server

    我们需要监控cpu和内存的使用率 以便提供硬件资源的申请采购建议 也方便我们知道运行负荷 而不是糊里糊涂出了问题再去解决或者工具自动解决了而我们不知道 话说回来集群的好处就是低成本的达到高性能 性能不去监控就有点太不专业了 但 k8s居然不
  • 给k8s集群添加负载均衡的能力

    常识 k8s没有自带负载均衡能力 需云服务提供商来做负载均衡 或者自己装负载均衡控制器 负载均衡控制器有很多 这次装Ingress Nginx https kubernetes github io ingress nginx 文档里根据环境
  • NestJs框架快速入门(V6.5)

    Nest是构建高效可扩展的 Node js Web 应用程序的框架 默认使用JavaScript的超集TypeScript进行开发 环境准备 查看node和npm版本 node version v10 16 0 npm version 6
  • 公车艳遇_习惯累积沉淀_新浪博客

    今天坐b1我又跟女生们年轻小姑娘挤在了一起 用她们们是们凸凹优质的身材把我挤在门上脚都挨不着地 当然没这么严重 反正一路上脸都有点烫 一路上都不自觉的猥琐笑容 下了车还在压抑着笑 一低头就不小心笑出声
  • [转载]动物伦理,为了人的尊严_习惯累积沉淀_新浪博客

    原文地址 xff1a 动物伦理 xff0c 为了人的尊严 作者 xff1a 评论员李铁 道德就是拿火腿肠喂流浪狗喂到心碎流泪 xff0c 而完全不用去顾虑猪的感受 这是一位网友对爱狗人士拯救流浪狗的讥讽 xff0c 不少人觉得这句话绝妙 x
  • GitLab的安装部署

    微信公众号 xff1a 运维开发故事 xff0c 作者 xff1a double冬 本文主要讲述了GitLab安装部署的两种方式 xff0c 以及遇到的一些问题 一 GitLab Server的搭建 参考 xff1a https about
  • Linux常用的软件包管理命令

    RPM xff08 红帽软件包管理器 xff09 常用的 RPM 软件包命令 命令作用安装软件的命令格式rpm ivh filename rpm升级软件的命令格式rpm Uvh filename rpm卸载软件的命令格式rpm e file
  • 解决linux系统挂载NTFS格式磁盘的问题

    一般情况下 xff0c Linux是识别不了NTFS格式移动硬盘的 xff08 需要重编译Linux核心才能 xff0c 加挂NTFS分区 xff09 xff0c 这时候为了能让Linux服务器能够识别NTFS的移动硬盘 xff0c 就必须
  • Win10提示“某些设置由你的组织来管理”的解决办法

    Win10提示 某些设置由你的组织来管理 的解决办法 2015 12 23 来源 xff1a Windows 10 人气 xff1a 443 有IT圈网友询问 xff0c 在Win10设置中出现了 某些设置由你的组织来管理 的提示 xff0
  • Stoker的数据库学习之基本语句(二)

    数据库学习之基本语句 xff08 二 xff09 DQL语句 对数据库中的数据进行查询 xff08 简单查询 xff09 我们用下图的emp表来进行操作 xff1a 1 去除重复数据 DISTINCT 注意 xff1a 需要查询的所有数据完
  • sourcetree(mac)设置代理链接GitHub

    背景 xff1a 使用mac版的sourcetree上get代码 xff0c 由于不能使用https只能用ssh xff0c 可是常规使用win版本的操作在mac上不好使了 xff0c 总是提示上传失败 xff0c 请手动添加ssh 解决方
  • 机器学习系列(2):线性回归

    机器学习系列 xff08 2 xff09 xff1a 线性回归 一 线性回归模型二 目标函数2 1 目标函数2 2 目标函数的概率解释2 2 1 中心极限定理2 2 2 高斯分布2 2 3 极大似然估计与损失函数极小化等价 三 参数估计3
  • ECS的简单入门(一):概念

    官方文档 xff1a https docs unity3d com Packages com unity entities 64 0 9 manual ecs core html 视频资料 xff1a UUG Online直播回放 xff1
  • golang使用strconv包string/int/int64类型转换

    Go不会对数据进行隐式的类型转换 xff0c 只能手动去执行转换操作 strconv包提供了简单数据类型之间的类型转换功能 以下是常用的一些转换函数 xff1a 将 int 类型转成 string 类型 Itoa num span clas
  • ECS的简单入门(六):传统GameObject模式转换到ECS模式

    在前面的介绍中 xff0c 我们都是通过代码来实现物体在场景中的显示 xff0c 例如我们要显示一个Cube xff0c 通过创建Entity xff0c 添加RenderMesh等Component xff0c 设置相应的Material
  • 解决问题:Mac OS遇到 Operation not permitted 问题

    随之Mac OS的不断升级 xff0c 使用某些程序时会遇到Operation not permitted相关的问题 xff0c 形式多种多样 参考链接 xff1a http osxdaily com 2018 10 09 fix oper
  • Mac小问题总结

    1 MacOS升级后出现xcrun error invalid active developer path missing xcrun的问题 打开terminal xff0c 执行命令 xff1a xcode select install

随机推荐