玩转通用控件位图和工具栏






4.45/5 (9投票s)
一个 WTL 混合类,用于创建工具栏控件,使用通用控件位图而不是本地资源。
引言
我多年来一直在 CodeProject 潜水,但这是我的第一次贡献。这是一个混合类,旨在帮助一个 CFrameWindowImpl
派生类使用标准的 WIN32 通用控件位图来构建工具栏。这个类可以用来避免让精益求精的 WTL 应用程序因为像打印、保存等常见工具栏按钮的位图资源而变得臃肿。虽然这似乎是很多麻烦但没什么用,但我是一个 WTL 新手,我想看看我是否能让我的 WTL 应用程序更精简一些。另外,出于各种其他原因,我喜欢使用系统资源和控件而不是自定义对象。例如,如果一个新的 Windows 版本发布时资源被更新了,一个继承自系统的应用程序会自动继承新的外观,而一个定义自己资源的应用程序通常必须重新编译才能避免看起来过时。
背景
通用控件位图常量以及这些位图中各个图像的索引在 Platform SDK 中有多种位置,具体取决于 SDK 版本。我知道的两个位置是:
- 用户界面服务/Shell 和通用控件/通用控件/工具栏控件/工具栏控件参考/工具栏标准按钮图像索引值
- 用户界面服务/Windows 控件/单个控件信息/工具栏控件/工具栏控件参考/常量/工具栏标准按钮图像索引值
在某些情况下,这些常量被错误地记录了。它们定义在 Platform SDK 的 <commctrl.h> 文件中,该文件也是定义 TBBUTTON
结构类型的地方。
typedef struct _TBBUTTON { int iBitmap; int idCommand; BYTE fsState; BYTE fsStyle; #ifdef _WIN64 BYTE bReserved[6]; // padding for alignment #elif defined(_WIN32) BYTE bReserved[2]; // padding for alignment #endif DWORD_PTR dwData; INT_PTR iString; } TBBUTTON, NEAR* PTBBUTTON, *LPTBBUTTON;
它包含一些未记录的填充,其大小根据目标平台而异。为了简化 TBBUTTON
数组初始化语句,我在 SysToolbarCtrl.h 中定义了 TBBPADDING
宏,以及对应于工具栏图标的按钮宏。
为了让演示比一个简单的带有无操作工具栏的框架窗口更有趣一些,我“偷”了(好吧,也许“借用”是一个更温和的说法)一些别人的想法:
- Leon Finker 的 IShellBrowser 文章
- Ed Gadziemski 的 Pixview 示例应用程序,遗憾的是它似乎在 CodeProject 上已不再可用。我使用了他的
CPix
类,它使用IPicture
来加载和渲染图像文件。我想关于如何使用IPicture
肯定还有很多其他例子。
使用代码
使用此代码有四个简单步骤:
- 这可能不需要再说,但我还是会说:在您的主窗口类的预编译指令中包含头文件 SysToolbarCtrl.h。
#include "SysToolbarCtrl.h"
- 将
CFrameWindowImpl
和CSysToolbarCtrl
包含在您的主窗口的继承列表中。class CMainFrame : public CFrameWindowImpl<CMainFrame>, public CSysToolbarCtrl<CMainFrame>, ...
- 不要在您的主窗口类的
OnCreate
方法(或您通常创建工具栏的任何技术)中调用CreateSimpleToolBarCtrl
,而是声明并初始化一个TBBUTTON
数组,然后像下面这样调用CreateSysToolbarCtrl
:// init toolbar button array, including separators, if any. TBBUTTON tbb[] = { // File|New STBB_STD_FILENEW, // File|Save STBB_STD_FILESAVE, // separator STBB_SEPARATOR, // View|Parent folder STBB_VIEW_PARENTFOLDER(ID_VIEW_PARENTFOLDER), // separator STBB_SEPARATOR, // Help|About STBB(STD_HELP, ID_APP_ABOUT, IDB_STD_SMALL_COLOR) }; // create the toolbar control HWND hWndToolBar = CreateSysToolbarCtrl(m_hWnd, tbb, sizeof(tbb)/sizeof(TBBUTTON), &m_CmdBar);
代码使用了一些我定义的宏来指定工具栏按钮。宏有三种基本形式,它们都以
STBB
(System Tool Bar Button,系统工具栏按钮)作为前缀:STBB_IMG_ID
,其中IMG_ID
是按钮在 <commctrl.h> 中定义的图像标识符。这种形式使用 <atlres.h> 中定义的命令 ID 来为按钮分配一个默认命令 ID;STBB_IMG_ID(CMD_ID)
,其中IMG_ID
如上所述,而CMD_ID
是应与该工具栏按钮关联的命令 ID。当 <atlres.h> 文件中没有与按钮图像对应的标准命令 ID 时使用此形式,例如VIEW_PARENTFOLDER
的情况;STBB(IMG_ID, CMD_ID, BITMAP_ID)
,其中IMG_ID
和CMD_ID
如前所述,而BITMAP_ID
是 <commctrl.h> 中定义的包含由IMG_ID
标识的按钮图像的位图 ID。当您想覆盖特定工具栏按钮的默认命令 ID 时,可以使用此形式,就像我在演示中为“帮助|关于”所做的那样。
使用您喜欢的资源编辑器像平常一样定义菜单。您可以将您的资源编辑器定义的命令 ID 包含在系统工具栏中。如果 <atlres.h> 预定义了任何可以在菜单中使用的命令 ID,也可以使用它们。不用说,指定的命令 ID 应该在应用程序的消息映射中有一个关联的处理程序,并且可能还有一个对应的菜单项。在演示中,我只为其中两个按钮(“查看|父文件夹”和“帮助|关于”)提供了一个处理程序,但这应该足以让您大致了解它是如何工作的。
您也可以将
CCommandBarCtrl
的指针作为参数传递,CreateSysToolbarCtrl
会将位图放置在相应的菜单项上。如果您不使用命令栏,传递NULL
(默认值)将导致CreateSysToolbarCtrl
忽略此参数。 - 最后但同样重要的是,不要忘记从您的资源脚本(*.rc 文件)中删除对不再需要的工具栏位图的所有引用。您可能希望手动使用一个简易文本编辑器而不是 Visual C++ 来执行此操作。简单地注释掉不需要的行应该就可以。
/////////////////////////////////////////////////////////////////////// // // Bitmap // // IDR_MAINFRAME BITMAP MOVEABLE PURE "res\\Toolbar.bmp"
现在,如果您尝试在 Visual C++ IDE 中打开工具栏,它会发出警告并询问您是否要为工具栏创建位图。请回答“否”!
关注点
STD_HELP
图像是传统上用于上下文帮助的箭头-问号。经典黄色问号在哪里?- 在我查看过的任何系统中,都没有
VIEW_VIEWMENU
的图像,但它在 <commctrl.h> 中。怎么回事? - 对于有进取心的读者来说,还有其他系统位图可用于制作热/冷图像工具栏。当然,代码需要进行一些修改,这超出了本文的范围。
- 是的,我知道,当我的
IShellBrowser
实现从IShellView
窗口获取选定项的 PIDL 时,我可能是在作弊。那是因为我不知道“正确”的方法。 - 我找到了一种将树形控件与 shell 文件夹视图同步的方法,但这是一个未记录的技巧,我没有包含在本文中。它确实只是一个强制方法,但如果有人感兴趣,请给我发电子邮件,我会把代码发给您。