简介:MFC CToolBar工具栏自定义图片及文字是一份面向Windows客户端开发者的MFC界面编程实例包,重点解决工具栏按钮图像加载、文字显示及停靠浮动等常见需求。压缩包共24个文件,以头文件(7个h)、实现文件(5个cpp)、资源脚本(rc/rc2)、图标ico及Visual Studio工程配置(sln/vcxproj)为主,整体仅324KB,结构精简,便于直接导入开发环境研读。文件分工明确:h/cpp对应类实现,rc定义工具栏与菜单资源,ico用于程序图标,txt为ReadMe说明。包内既包含完整的TimeClient示例项目,也附有独立的“工具栏图片素材及代码.rar”及使用文档,覆盖从创建工具栏、LoadBitmap加载位图、AddButtons添加按钮,到SetButtonInfo自定义按钮图像文字、DockControlBar/FloatToolBar控制停靠与浮动的完整流程。已有1685人学习下载,适合正在使用MFC构建传统工具栏界面、希望快速掌握CToolBar自定义技巧的初中级Windows开发者。
1. CToolBar工具栏不只放图标:一个能直接跑的图片加文字Demo
前几天给某设备工具软件改界面,客户一句话把我问住了:“这个‘保存’按钮到底是存项目还是存图片?工具提示要悬停一秒才出来,不如直接把文字写在按钮上。”默认的MFC CToolBar只会显示一行小图标,想同时放图片和文字得自己折腾按钮样式。我后来整理了一个极简的MFC CToolBar自定义图片及文字Demo:主窗口上方一条工具栏,四个按钮,左边是16x16的位图,右边紧跟着中文文字,点击后还能弹出响应。适合正在做MFC界面定制或者单纯想把工具栏按钮做得更直白的开发者。这个Demo不依赖第三方库,VS打开就能编译运行。
2. 项目概览与技术选型:一个“图文按钮工具栏”该有的样子
2.1 这个Demo解决什么:从默认Icon Only到Icon + Text
标题里的BUTTON PICTURE DEMO,说白了就是“按钮位图演示”。默认用向导生成一个MFC框架,工具栏上无非是一排灰色的小图标,鼠标悬停后才有Tooltip提示。这种交互在内部工具软件里很受诟病:使用者根本分不清图标含义。这个Demo的目标很简单——让工具栏按钮同时拥有“图片”和“文字”两个信息载体。图片负责快扫,文字负责精读。
具体到运行效果,程序启动后主框架窗口的客户区上部有一条可停靠工具栏,包含四个按钮:新建、打开、保存、参数设置。每个按钮左侧是一枚16x16的位图图标,图标右侧直接显示中文文字,不需要悬停等待。点击任意按钮会弹出消息框,确认命令已经路由到框架层的处理函数;另外,“保存”按钮还会根据当前文档的脏状态动态置灰,这是一个很典型的UpdateCommandUI应用。
这四个按钮覆盖了日常开发中常用工具栏能达到的交互:静态创建、图文混排、消息响应、状态刷新。对照标题中的MFC CToolBar,你会发现核心对象其实只有一个:CToolBar。但要用好它,得先把按钮样式、位图资源和通用控件API理清楚。
2.2 技术选型:为什么还是用传统CToolBar而不是CMFCToolBar
很多从Visual Studio 2008之后接触MFC的开发者,一提到工具栏就想到CMFCToolBar,甚至直接用Ribbon界面。这个Demo特意没用CMFCToolBar,原因是真实工作里大量维护中的程序还是延续老的CToolBar代码,而且CToolBar足够解决80%的“图标+文字”需求。
CToolBar是MFC对Windows通用控件“工具栏”的传统封装。它通过资源编辑器直接加载TOOLBAR资源和位图资源,代码一旦跑起来,每一步可以断点调试:创建、加载位图、切分图标、绑定命令、刷新状态,链路非常清晰。而CMFCToolBar虽然支持复杂的停靠与自定义,底层会引入很多重绘和自定义管理器机制,对一个小功能来说过于笨重,出了问题反而更难定位。
字符集方面,Demo全程使用Unicode,这样TCHAR和_T宏都能顺利编译。项目工程没有引用第三方库,编译版本支持x86和x64两种平台。如果你拿着老的多字节工程,建议先在项目属性里把所有字符集切换成Unicode,否则字符串字面量会与_T拼接时出现类型不匹配。
2.3 解包后的目录结构:每个文件干什么
拿到源码包,先别急着双击sln,大概扫一眼目录能知道东西都在哪。假设项目名是ImageTextToolBarDemo,解压后应该看到以下文件:
| 路径 | 作用 |
|---|---|
| ImageTextToolBarDemo.sln | 解决方案文件,Visual Studio直接打开 |
| ImageTextToolBarDemo.vcxproj | 项目工程文件,包含编译配置 |
| MainFrm.h / MainFrm.cpp | 主框架窗口,工具栏的创建、样式调整、消息映射都在这里 |
| ImageTextToolBarDemo.h / .cpp | CWinApp派生应用类,维护程序入口 |
| Resource.h | 资源ID常量定义,比如IDR_MAINFRAME、ID_FILE_NEW |
| res\Toolbar.bmp | 工具栏位图,水平排列四张16x16小图 |
| res\ImageTextToolBarDemo.rc | 资源脚本,声明工具栏按钮、菜单、位图等 |
这是一个很典型的MFC单文档工程结构。如果源码包是精简过的,可能少了res文件夹下的位图,这时你需要自己用画图程序做一张64x16的位图:从左到右依次画四个16x16小图,分别表示文档、文件夹、磁盘、齿轮。
2.4 位图资源的潜规则:一张bmp按水平方向切成多个按钮图标
你可能注意到工具栏按钮的图片不是四个独立文件,而是全部打包在Toolbar.bmp这张位图里。这是Toolbar控件沿用多年的“图集”机制:按钮的iBitmap字段用来指定使用图集中的第几个图像,而图集就是一张水平拼接的位图。
资源编辑器自动生成的位图宽度必须是16的倍数,高度通常是16像素。比如四个按钮,文件宽度64像素,高度16像素。如果某个小图画得超出了16x16的边界,要么图片被裁剪,要么会串到下一个按钮,这是后期排查图片错位的一个原因。
在资源脚本中,TOOLBAR段会列出每个按钮的顺序和ID:
IDR_MAINFRAME TOOLBAR 16, 15 BEGIN BUTTON ID_FILE_NEW BUTTON ID_FILE_OPEN BUTTON ID_FILE_SAVE BUTTON ID_FILE_SAVE_AS // 这里按钮顺序对应位图从左到右的顺序 END虽然这个Demo里只是一个简单的四个按钮,但明白这条规则之后,你就能理解为什么有时候多加了按钮,图片顺序会乱。后面避坑章节会展开。
3. 核心代码拆解:从主框架到按钮点击的完整链路
3.1 创建工具栏:OnCreate里最关键的两步
所有工具栏代码都集中在CMainFrame::OnCreate中。这里有两个动作必须按顺序做:先创建CToolBar窗口,再调用LoadToolBar加载工具栏资源。我把代码写进MainFrm.cpp:
int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct) { if (CFrameWnd::OnCreate(lpCreateStruct) == -1) return -1; // 创建工具栏:挂到主框架窗口上,停靠在顶部,支持Tooltip if (!m_wndToolBar.Create(this, WS_CHILD | WS_VISIBLE | CBRS_TOP | CBRS_TOOLTIPS | CBRS_FLYBY, IDR_MAINFRAME)) { TRACE0("Failed to create toolbar\n"); return -1; } // 真正加载TOOLBAR资源和位图 if (!m_wndToolBar.LoadToolBar(IDR_MAINFRAME)) { TRACE0("Failed to load toolbar\n"); return -1; } return 0; }Create函数的第一个参数是父窗口指针,这里传递this指向主框架;第二个参数是控制条样式,WS_CHILD和WS_VISIBLE是控件基本属性,CBRS_TOP表示停靠在客户区顶部,CBRS_TOOLTIPS允许按钮浮动提示,CBRS_FLYBY让菜单栏提示跟随鼠标。第三个参数IDR_MAINFRAME既是工具栏资源的ID,也是随后LoadToolBar要使用的ID。如果你在资源里改过工具栏的ID,记得两个调用要统一。
LoadToolBar的作用是从资源加载TOOLBAR段和对应的位图。这一步完成后,工具栏控件已经知道有几个按钮,也知道每个按钮的图像索引。但此时按钮是“没写文字”的状态,因为工具栏资源本身没有存文字信息。
3.2 让文字显示在图标右侧:修改按钮尺寸和工具栏样式
仅加载工具栏,按钮上只有图标。要让文字出现,需要两件事:把按钮占位区域加宽,再告诉工具栏“图标旁边可以放文字”。我采用GetToolBarCtrl()返回通用工具栏控件的引用来做:
// 获取工具栏窗口的扩展样式并设置TBSTYLE_LIST DWORD dwStyle = m_wndToolBar.GetToolBarCtrl().GetStyle(); m_wndToolBar.GetToolBarCtrl().SetStyle((dwStyle | TBSTYLE_LIST) & ~TBSTYLE_WRAPABLE); // 按钮整体尺寸,单位是像素 SIZE szBtn = { 72, 30 }; m_wndToolBar.GetToolBarCtrl().SetButtonSize(szBtn); // 位图图标尺寸 SIZE szBitmap = { 16, 16 }; m_wndToolBar.GetToolBarCtrl().SetBitmapSize(szBitmap);GetStyle和SetStyle是工具栏控件的原生API,CToolBar类本身没有直接暴露SetStyle,所以要先GetToolBarCtrl()拿到内部的CToolBarCtrl对象。TBSTYLE_LIST样式告诉控件:按钮的文本画在图标右侧而不是图标下方。去掉TBSTYLE_WRAPABLE是为了防止工具栏在窄窗口里自动换行。SetButtonSize接收的是一个SIZE结构,第一个值是按钮总宽,第二个值是总高。宽72像素可以放进16像素图标加上好几个汉字的宽度,30像素高度足够容下带边框的按钮。
还有一步经常被忽略:位图大小要和实际资源匹配。如果资源里每个小图是16x16,SetBitmapSize就传16x16,两者不一致时按钮取图会错位。
3.3 绑定文字:SetButtonText与按钮索引的对应关系
样式准备好后,可以逐个按钮绑定文字。CToolBar提供了SetButtonText方法:
// 注意参数是按钮索引,从0开始 m_wndToolBar.SetButtonText(0, _T("新建")); m_wndToolBar.SetButtonText(1, _T("打开")); m_wndToolBar.SetButtonText(2, _T("保存")); m_wndToolBar.SetButtonText(3, _T("参数设置"));SetButtonText的第一个参数是按钮在工具栏上的索引,不是命令ID。通常工具栏资源里按钮顺序是固定的,索引也就固定。如果你不希望硬编码索引,可以用ID查找索引:
int nIndex = m_wndToolBar.CommandToIndex(ID_FILE_SAVE); if (nIndex >= 0) m_wndToolBar.SetButtonText(nIndex, _T("保存"));CommandToIndex通过命令ID反查索引,这让代码更健壮,即使调整了按钮顺序也不用改文字绑定。注意SetButtonText只改变了控件的文字缓冲区,真正显示文字需要按钮样式支持。因此3.2节的TBSTYLE_LIST不要漏掉,否则这段代码会“默默失效”。
3.4 消息映射:ON_COMMAND把点击送到处理函数
文字显示出来之后,要让按钮干活。传统MFC命令路由并不关心按钮长什么样,只要按钮有命令ID,就能通过消息映射进入主框架的处理函数。在MainFrm.cpp的消息映射里添加:
BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd) ON_WM_CREATE() ON_COMMAND(ID_FILE_NEW, &CMainFrame::OnFileNew) ON_COMMAND(ID_FILE_OPEN, &CMainFrame::OnFileOpen) ON_UPDATE_COMMAND_UI(ID_FILE_SAVE, &CMainFrame::OnUpdateFileSave) END_MESSAGE_MAP()三个处理函数的示例实现:
void CMainFrame::OnFileNew() { AfxMessageBox(_T("你点击了[新建]按钮")); } void CMainFrame::OnFileOpen() { AfxMessageBox(_T("你点击了[打开]按钮")); } void CMainFrame::OnUpdateFileSave(CCmdUI* pCmdUI) { // 用成员变量控制保存按钮是否可点击 pCmdUI->Enable(m_bDocumentDirty); }ON_COMMAND是“命令发生后就调用”,它对应按钮被按下这一瞬间的动作。ON_UPDATE_COMMAND_UI则是在工具栏空闲刷新的间隙被调用,用来动态更新按钮的可见、置灰、加粗等状态。这里m_bDocumentDirty是一个BOOL成员,模拟文档是否存在未保存修改。当它为FALSE时,“保存”按钮自动变灰,这正是MFC命令UI机制的价值:界面状态和业务状态一致。
3.5 数据结构的幕后:TBBUTTON与位图索引
如果你对工具栏内部如何把图片和按钮对应起来感兴趣,可以看CToolBarCtrl::AddButtons使用的TBBUTTON结构:
TBBUTTON btn = {0}; btn.idCommand = ID_FILE_NEW; // 命令ID btn.iBitmap = 0; // 位图索引,0对应资源里第一张小图 btn.fsState = TBSTATE_ENABLED; // 初始状态 btn.fsStyle = TBSTYLE_BUTTON; // 普通按钮样式 btn.iString = (INT_PTR)_T("新建"); // 按钮文字LoadToolBar内部会解析TOOLBAR资源并自动填充这个结构,所以平时我们接触不到。但当需要动态添加按钮时,就得手动构造TBBUTTON数组并调用AddButtons。iBitmap字段是图片在工具栏图集中的索引,0就是第一张,1就是第二张,顺序和位图文件里的从左到右排序一致。理解了这一点,后面想自己扩展按钮时就能有的放矢。
4. 编译与运行:把这个Demo拉起来跑一遍
4.1 环境准备:MFC组件不要漏装
用Visual Studio打开这个Demo之前,先确认安装器里有没有“使用C++的桌面开发”工作负载。MFC并不是默认安装项,需要在该工作负载的可选组件中找到“适用于最新生成工具的C++ MFC(x86和x64)”。我用的是VS2022社区版,装好组件以后,双击.sln文件,Visual Studio会自动加载工程。
如果你的机器上只有VS2019,工程文件一般也能打开,因为绝大多数代码用的都是静态MFC API。遇到平台工具集版本提示,选择本机已安装的工具集重新编译即可。
4.2 打开工程后要检查的三个配置
打开工程属性页,依次检查:
- 常规 → 字符集,必须为“使用Unicode字符集”。如果打开的是老工程,可能是多字节,你需要手动切换。
- 常规 → 配置类型,应为“应用程序(.exe)”。
- C/C++ → 代码生成 → 运行库,如果是Debug静态库,使用/MTd,Release使用/MT;如果用动态MFC,则对应使用/MDd或/MD。这个Demo默认用动态MFC也没问题,只要MFC组件装了就正常。
如果字符集设置错了,典型现象是编译报错C2664,说无法将const wchar_t[]转换为LPSTR。那是因为Unicode字符集下字符串字面量默认宽字符,而你的函数入口仍是ANSI。把项目切到“使用Unicode字符集”即可恢复,因为代码里全是_T宏。
检查完毕后直接Ctrl+Shift+B编译。如果一切顺利,输出窗口会提示生成成功。
4.3 运行效果验证清单
按F5运行,程序启动后你会看到一个单文档窗口。要验证的关键点罗列如下:
- 主窗口顶部出现工具栏,四个按钮一字排开。
- 每个按钮左侧有清晰的小图标,右侧有中文文字。
- 鼠标悬停时出现Tooltip,内容就是按钮文字。
- 点击“新建”按钮,弹出一个消息框,说明命令路由到了OnFileNew。
- 把m_bDocumentDirty设为TRUE,“保存”按钮从灰色变为可点击。
如果这些都符合,说明Demo的核心链路已经跑通。如果再出现图片错位或者文字不显示的问题,直接跳到第5章对照排查。
4.4 工具栏停靠控制:DockControlBar的细节
在框架类OnCreate结束时,通常还要把工具栏“钉住”,否则它只在顶部悬浮,拖动后可能乱跑。MFC的CFrameWnd提供了DockControlBar方法:
// 让工具栏支持停靠,并停靠在框架顶部 EnableDocking(CBRS_ALIGN_ANY); m_wndToolBar.EnableDocking(CBRS_ALIGN_ANY); DockControlBar(&m_wndToolBar);EnableDocking通知主框架“可以接受控制条停靠”,m_wndToolBar.EnableDocking是这个工具栏允许被拖去停靠,最后一行DockControlBar把它吸附到顶边。如果不加这组代码,工具栏只是以子窗口形式显示,不能拖动调整。很多新手在这里翻车,以为工具栏创建失败,其实是没有调DockControlBar。
4.5 调试技巧:断点看按钮文字什么时候设置
如果运行时工具栏文字不出现,可以在MainFrm.cpp里SetButtonText处下断点,确认这行代码有没有执行。有时候由于资源ID冲突,LoadToolBar内部会返回失败,直接把OnCreate后面的流程跳过。这时在LoadToolBar调用处按下F11,观察是否进入失败分支。TRACE0输出的调试信息也会出现在“输出”窗口,这比反复编译瞎猜要快得多。
还可以使用Spy++查看工具栏控件窗口的类名,确认创建的确实是ToolbarWindow32,而不是其他窗口。这样能快速区分是CToolBar封装失败,还是普通窗口覆盖问题。
如果链接器报LNK2019:无法解析的外部符号,大概率是MFC没有链接进去。检查代码生成→运行库是否使用了/MDd或/MTd,以及是否勾选了“使用MFC的共享库”或“使用MFC的静态库”。有人习惯在stdafx.h里加#include <afxwin.h>,但忘记在链接器输入里添加mfc*.lib,老版VS容易坑。
4.6 不想用现成sln?向导重建后拷贝代码
如果碰到诡异的环境问题,最快的方式是自己在VS里新建MFC工程。选择“MFC应用”,应用程序类型选“单个文档”,项目样式选“Visual Studio”,然后在生成的MainFrm.cpp里覆盖OnCreate函数,把上述代码复制进去。注意新建工程的资源ID可能不同,你需要把LoadToolBar里的IDR_MAINFRAME改成自己的资源ID。
这种重建方式的好处是资源脚本干净,不会出现旧工程里ID冲突。缺点是需要重新构建四个按钮的位图资源。如果你只是验证功能,可以先不加载自己位图,用向导默认的工具栏位图跑通文字逻辑,再慢慢替换。
5. 避坑指南:工具栏图片和文字常见的五处翻车现场
5.1 文字怎么调都不显示
现象:SetButtonText写好了,编译运行后按钮上依然只有图,没有文字。
原因:CToolBar在创建时,按钮默认样式是TBSTYLE_BUTTON,并没有开启TBSTYLE_LIST或TBSTYLE_AUTOSIZE。SetButtonText只是把文字存进一块内存,控件重绘时不知道要不要画。另一个常见原因是工具栏控件尺寸太小,文字被挤出可视区域。
解决:在Create后先调用GetToolBarCtrl().SetStyle(dwStyle | TBSTYLE_LIST),同时把按钮尺寸设成至少能容纳“图标+文字+边际”的数值,比如72x30。SetButtonText之后调用m_wndToolBar.Invalidate(TRUE)强制重绘。我遇到过SetButtonText执行了但没重绘的情况,加上Invalidate立刻就好。
5.2 文字在图标下面而不是右边
现象:按钮上出现了“两行”,上面图标,下面文字,整个工具栏又高又难看。
原因:TBSTYLE_LIST没有设置,默认的工具栏按钮文字渲染位置是图标下方。MFC的CToolBar::SetButtonSize调整的是整个按钮区域,但它并不会自动给你把文字移到右侧。
解决:用GetToolBarCtrl().SetStyle给工具栏加上TBSTYLE_LIST,然后调用SetButtonSize把按钮宽度从默认的23左右提高到70以上。如果你用的是资源编辑器里的工具栏设计器,也可以在资源脚本的TOOLBAR段后手动设置样式,但代码里改样式最直接。
5.3 图片错位:按钮显示成了相邻的图
现象:四个按钮的图标乱序:点“保存”却显示“打开”的图标;或者某个按钮图片被截断了一半。
原因:位图资源里的小图顺序和资源脚本TOOLBAR段里的BUTTON顺序不一致。MFC的LoadToolBar按BUTTON声明顺序从左到右切分位图,如果位图里第一张是“新建”,第二张是“打开”,BUTTON段也应按此顺序声明。另外,还要确保bmp宽度是16的整数倍,否则切图会错位。
解决:用画图软件打开Toolbar.bmp,确认宽度=按钮数量*16。例如四个按钮,宽度=64。然后检查.rc文件TOOLBAR段的BUTTON顺序和bmp中的小图是否一致。如果不想改位图顺序,也可以用CToolBar::LoadBitmap配合指定起始索引,但建议优先修bmp顺序,毕竟这本来就是资源编辑器的默认行为。
5.4 高DPI下图标发虚
现象:显示缩放125%或150%时,工具栏图标边缘模糊,文字却是清晰的。
原因:传统位图是固定像素图像,系统只做拉伸缩放,自然变虚。文字是GDI绘制的矢量字,缩放后依然锐利。
解决:没有一劳永逸的办法,能在小改动范围内改善的是改用CImageList动态加载图标,并配合SetImageList让工具栏使用图标列表而不是位图。同时把图标资源准备成多尺寸,比如16x16和20x20,在OnCreate里根据DPI选择加载。对于这个Demo而言,图标不是主角,可以接受16x16略虚;如果你很在意,可以把文字放大,让用户更多依赖文字而不是小图。
5.5 鼠标悬停时工具栏闪白
现象:鼠标在按钮间来回滑动时,按钮背景闪成白色,重绘明显。
原因:旧式工具栏默认没有开启扁平(flat)样式,也未使用双缓冲;系统每次做状态变化时都会用控件背景色刷一次。
解决:在SetStyle时加上TBSTYLE_FLAT:
DWORD dwStyle = m_wndToolBar.GetToolBarCtrl().GetStyle(); m_wndToolBar.GetToolBarCtrl().SetStyle(dwStyle | TBSTYLE_FLAT);扁平样式让按钮只在鼠标移入时凸起,背景不再大块闪白。如果仍然闪,可以在WM_PAINT里做双缓冲,但对简单工具栏来说TBSTYLE_FLAT够用了。
6. 二次开发:把Demo提炼成一个CImageTextToolBar可复用类
6.1 为什么值得封装
Demo里的四个按钮写死在MainFrm.cpp,换个窗口或者增加按钮,就要复制一大段SetStyle和SetButtonText。我习惯把它收拢成一个专用类,以后任何MFC工程都可以直接引用。
6.2 封装后的类定义
class CImageTextToolBar : public CToolBar { public: BOOL CreateImageText(CWnd* pParent, UINT nToolbarId); BOOL AddTextButton(UINT nCmdID, int nImageIndex, LPCTSTR lpszText); };CreateImageText完成创建、加载资源和统一样式;AddTextButton负责把按钮和文字绑定。
6.3 核心实现
BOOL CImageTextToolBar::CreateImageText(CWnd* pParent, UINT nToolbarId) { if (!Create(pParent, WS_CHILD | WS_VISIBLE | CBRS_TOP | CBRS_TOOLTIPS, nToolbarId)) return FALSE; if (!LoadToolBar(nToolbarId)) return FALSE; DWORD dwStyle = GetToolBarCtrl().GetStyle(); GetToolBarCtrl().SetStyle((dwStyle | TBSTYLE_LIST | TBSTYLE_FLAT) & ~TBSTYLE_WRAPABLE); GetToolBarCtrl().SetButtonSize(CSize(72, 30)); GetToolBarCtrl().SetBitmapSize(CSize(16, 16)); return TRUE; } BOOL CImageTextToolBar::AddTextButton(UINT nCmdID, int nImageIndex, LPCTSTR lpszText) { TBBUTTON btn = {0}; btn.idCommand = nCmdID; btn.iBitmap = nImageIndex; btn.fsState = TBSTATE_ENABLED; btn.fsStyle = TBSTYLE_BUTTON; btn.iString = (INT_PTR)lpszText; return GetToolBarCtrl().AddButtons(1, &btn); }AddButtons一次性把按钮加进工具栏控制区。nCmdID是命令ID,nImageIndex是位图索引,lpszText是按钮文字。别小看btn.iString,它存的是指针,如果传临时字符串,按钮一刷新文字就变成乱码,所以文字推荐用字符串资源或静态存储。
6.4 在框架中使用
修改MainFrm成员类型,把原来的CToolBar换成CImageTextToolBar:
CImageTextToolBar m_wndToolBar; // OnCreate里 m_wndToolBar.CreateImageText(this, IDR_MAINFRAME); m_wndToolBar.AddTextButton(ID_FILE_NEW, 0, _T("新建"));这样如果以后要加一个“打印”按钮,只需在bmp图集里追加一张小图,再调用一行AddTextButton,五分钟后就能看到完整的图文按钮。
6.5 验证与心得
每次改完工具栏,我都强制跑一遍“点击、置灰、拖动停靠、DPI缩放”四件事。特别是DPI缩放,最容易发现图片模糊和按钮错位。后来项目里凡是需要“图标+文字”按钮的窗口,我都直接拖这个类过去用。希望帮到你。
本文还有配套的精品资源,点击获取