news 2026/10/20 5:30:35

MFC CToolBar图文按钮工具栏Demo:图片+文字共存的实现方法

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
MFC CToolBar图文按钮工具栏Demo:图片+文字共存的实现方法

简介: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 / .cppCWinApp派生应用类,维护程序入口
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 打开工程后要检查的三个配置

打开工程属性页,依次检查:

  1. 常规 → 字符集,必须为“使用Unicode字符集”。如果打开的是老工程,可能是多字节,你需要手动切换。
  2. 常规 → 配置类型,应为“应用程序(.exe)”。
  3. 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缩放,最容易发现图片模糊和按钮错位。后来项目里凡是需要“图标+文字”按钮的窗口,我都直接拖这个类过去用。希望帮到你。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/20 5:30:10

用Codex一键生成高效脚本:从需求拆解到工程化落地的完整实践

最近手头只要堆了临时脚本的活&#xff0c;我基本都会先把Codex叫出来聊一轮。以前写个日志分析脚本&#xff0c;少说半小时起步&#xff0c;还得考虑参数、异常、输出格式&#xff0c;写完自己都不想看第二遍。现在用自然语言把需求说清楚&#xff0c;Codex能直接给你生成一版…

作者头像 李华
网站建设 2026/10/20 5:30:07

Webshell流量特征分析:菜刀、蚁剑、冰蝎、哥斯拉识别与检测

在安全运营这个行当待久了&#xff0c;Webshell排查基本是每个蓝队人都躲不过去的活。不管护网还是日常应急&#xff0c;流量侧分析永远是第一时间发现问题的关键手段。菜刀、蚁剑、冰蝎、哥斯拉&#xff0c;这四个名字可以说是Webshell管理工具里的“四大天王”&#xff0c;贯…

作者头像 李华
网站建设 2026/10/20 5:29:29

哥斯拉与冰蝎Webshell加密流量分析:特征提取与检测规则实战

最近在参与某次内部的攻防演练复盘时&#xff0c;好几个负责流量侧的同事都在抱怨同一个问题&#xff1a;明明告警平台已经标红了&#xff0c;但是看了一眼原始流量包&#xff0c;根本不知道该怎么研判。原因倒也不复杂——现在的Webshell管理工具早就过了"一句话菜刀&quo…

作者头像 李华
网站建设 2026/10/20 5:29:28

AI大模型应用场景落地策略:从Demo到生产环境的实战指南

你的团队买好了显卡&#xff0c;代码也调通了&#xff0c;核心大模型API也接了&#xff0c;但真正把AI大模型应用场景落地到业务里&#xff0c;大概率还是会卡壳。我这些年带过不少项目&#xff0c;见过很多团队一脸兴奋地拿着Demo去找业务方&#xff0c;结果一上生产环境&…

作者头像 李华
网站建设 2026/10/20 5:29:22

工业设计软件实战指南:从选型到协同,打造产品创新数字引擎

上个月一位做智能硬件的朋友来问我&#xff0c;说团队从五个人加到二十五个人&#xff0c;软件却还在“一人一个版本”的阶段&#xff1a;有人用开源格式建模、有人用老版本存档&#xff0c;外壳改一版&#xff0c;结构那边等了三天打不开文件。他说&#xff0c;感觉软件买了一…

作者头像 李华
网站建设 2026/10/20 5:29:01

歌曲标签与用户画像双通道音乐推荐系统:从Spark计算到虚拟化部署实践

不知道你有没有发现&#xff0c;市面上讲音乐推荐的文章不少&#xff0c;但大多数停留在“ALS协同过滤”或者“TF-IDF算相似度”这种单点技术上。真正把一个推荐系统从数据清洗、歌曲标签建模、用户画像构建&#xff0c;到Spark分布式计算、虚拟化环境部署&#xff0c;最后再拿…

作者头像 李华