尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

MFC工具栏图片文字同显:CToolBar自定义实战指南

MFC工具栏图片文字同显:CToolBar自定义实战指南 简介面向MFC开发者的工具栏自定义实例资源以CToolBar类为主线系统讲解按钮图片与文字的自定义方法以及工具栏的停靠与浮动实现适合界面开发经验不足、想快速美化工具栏的Windows应用开发者参考。压缩包共24个文件核心代码以7个header头文件和5个cpp源文件为主配合rc资源脚本、ico图标、解决方案文件、可执行Demo及使用文档资源体积仅324KB轻量且完整便于下载后直接打开调试。当前已有1682人下载学习。资源以TimeClient示例项目为载体覆盖了Create创建工具栏、LoadBitmap加载位图、SetButtonInfo设置按钮ID与图像索引、DockControlBar/FloatToolBar控制停靠等关键环节同时附带“工具栏图片素材及代码.rar”内置可直接替换的按钮位图和现成代码片段ReadMe文档也会逐步说明如何导入图片资源并响应按钮点击既能满足初学者从零搭建自定义工具栏的需求也可供进阶开发者在现有MFC工程中快速复用界面方案。 做MFC的兄弟应该都有过这种体验向导生成的CToolBar工具栏默认只有一排小图标功能全靠猜。用户问“这按钮干嘛的”、PM要求“按钮上既要有图又要写字”你就得折腾工具栏的自定义图片和文字显示。这篇文章把我在实际开发里验证过的一套做法完整记录下来从CToolBar的创建机制讲起到自定义图片、按钮文字、布局调整、坑位排查一条龙说清楚。正被工具栏定制卡住的朋友可以直接照抄。1. 先理清楚CToolBar工具栏的常规创建流程1.1 默认方案为什么不够用MFC工具栏最经典的创建套路是在CMainFrame::OnCreate里调用CreateEx创建工具条接着LoadToolBar(IDR_MAINFRAME)加载资源。IDR_MAINFRAME在.rc文件里定义了一个TOOLBAR资源里面放着一串按钮的命令ID和一张拼好多个等宽图标的位图。程序运行时MFC把这张位图按按钮数量均分每个按钮取其中一小块显示。这个机制的局限非常明显图标必须提前拼在一张位图里改其中一个图标就得重画整张图按钮上只能显示图片没有原生的文字位想支持高分屏、扁平化图标维护成本也高。很多老项目改不动资源结构只能在现有基础上追加文字这时候就需要换一种思路。1.2 搞清楚CToolBar和CToolBarCtrl的关系CToolBar只是MFC包装出来的外壳真正干活的底层控件是CToolBarCtrl它封装了Windows通用控件里的Toolbar。很多自定义样式实际上是发给CToolBarCtrl的所以操作必须走GetToolBarCtrl()这个入口。比如设置TBSTYLE_LIST样式、调用SetButtonInfo设置图标索引、通过SetImageList挂图片列表都是这个内嵌控件在接收命令。理解这一层关系之后工具栏上很多看似“莫名其妙”的样式问题就能想通了。比如你直接给CToolBar设置某个风格却没有任何效果多半是因为那个风格必须经由GetToolBarCtrl().SetStyle()下发或者要在CreateEx的第一个参数dwCtrlStyle里传入。CToolBar就像4S店前台真正递扳手修车的是后面的Toolbar控件。2. 让图片和文字同时出现核心实现步骤2.1 关键开关TBSTYLE_LIST样式想要按钮上同时显示图标和文字最核心的一步是给工具栏控件设置TBSTYLE_LIST样式。这个样式的作用是让按钮的文字标签显示在图标右侧整体呈横向列表式布局。不设置这个样式就算后面调用了SetButtonText文字也可能不显示或被图标覆盖。设置位置有两个一是在CreateEx创建时直接传进去二是在创建之后用GetToolBarCtrl().SetStyle()补上。我个人推荐第一种一步到位避免创建后再刷样式导致窗口闪烁。// CMainFrame.h protected: CToolBar m_wndToolBar; CImageList m_imgList; // 图片列表必须长期有效 // CMainFrame.cpp :: OnCreate() if (!m_wndToolBar.CreateEx(this, TBSTYLE_FLAT | TBSTYLE_LIST, // 关键允许文字显示在图标右侧 WS_CHILD | WS_VISIBLE | CBRS_TOP | CBRS_GRIPPER | CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC) || !m_wndToolBar.LoadToolBar(IDR_MAINFRAME)) { TRACE0(Failed to create toolbar\n); return -1; }CreateEx的第一个参数dwCtrlStyle就是发给工具栏控件的样式这里把TBSTYLE_FLAT扁平风格和TBSTYLE_LIST列表布局一起传入。第二个dwStyle是窗口和MFC栏的样式CBRS_TOP决定工具栏停靠在顶部CBRS_GRIPPER显示拖动把手CBRS_TOOLTIPS启用悬浮提示。资源加载沿用LoadToolBar按钮命令ID还是从IDR_MAINFRAME资源里取。2.2 图标加载推荐CImageList替代资源位图传统方式用一张拼接位图维护起来非常痛苦。现在更推荐用CImageList加载独立图标再通过SetImageList挂给工具栏。这样每个图标是一个独立资源可以随时替换还支持带Alpha通道的半透明图标。// 创建图片列表尺寸要和图标资源一致 m_imgList.Create(24, 24, ILC_COLOR32 | ILC_MASK, 0, 4); m_imgList.Add(AfxGetApp()-LoadIcon(IDI_ICON_NEW)); m_imgList.Add(AfxGetApp()-LoadIcon(IDI_ICON_OPEN)); m_imgList.Add(AfxGetApp()-LoadIcon(IDI_ICON_SAVE)); // 把图片列表设置给工具栏控件 m_wndToolBar.GetToolBarCtrl().SetImageList(m_imgList); // 将每个按钮和图标索引绑定 m_wndToolBar.GetToolBarCtrl().SetButtonInfo(ID_FILE_NEW, TBBS_BUTTON, 0); m_wndToolBar.GetToolBarCtrl().SetButtonInfo(ID_FILE_OPEN, TBBS_BUTTON, 1); m_wndToolBar.GetToolBarCtrl().SetButtonInfo(ID_FILE_SAVE, TBBS_BUTTON, 2);这里有个容易踩的坑m_imgList必须是类成员变量不能是局部变量。工具栏控件在内部保存的是图片列表的指针一旦局部变量析构指针就悬空了轻则图标消失重则直接崩溃。另一个容易忽略的是如果用ILC_COLOR32加载真彩色图标图标本身需要支持Alpha通道否则背景会发黑。2.3 给按钮添加文字SetButtonText的正确用法图片挂好了接下来是文字。CToolBar::SetButtonText接收两个参数按钮索引和文字内容。如果你在资源里排的按钮顺序是“新建、打开、分隔符、保存”那按钮索引分别是0、1、2注意分隔符不算按钮索引要跳过。// 按钮索引按实际按钮个数排分隔符不计入 m_wndToolBar.SetButtonText(0, _T(新建)); m_wndToolBar.SetButtonText(1, _T(打开)); m_wndToolBar.SetButtonText(2, _T(保存)); // 刷新布局让工具栏自动撑开 m_wndToolBar.GetToolBarCtrl().AutoSize();很多新手第一次写会把命令ID当作索引传进去比如SetButtonText(ID_FILE_NEW, _T(新建))结果发现文字位置全乱。这是索引和ID混淆导致的。另外如果按钮文字来自数据库或者外部配置而数据本身是CString直接传进去即可MFC内部会完成字符集转换但要注意工程字符集设置Unicode还是多字节否则容易出现乱码。3. 自定义细节从图标尺寸到按钮布局3.1 调整按钮与图标尺寸工具栏刚创建出来按钮的尺寸可能和你的图标不匹配。比如图标是32x32默认按钮高度却是24就会出现裁切。这时需要用SetButtonSize和SetImageSize统一设置。m_wndToolBar.GetToolBarCtrl().SetImageSize(CSize(32, 32)); m_wndToolBar.GetToolBarCtrl().SetButtonSize(CSize(80, 40));SetImageSize设置图标显示区域SetButtonSize设置整个按钮的区域。按钮宽度的经验值大约是“图标宽度 文字宽度 左右边距”在TBSTYLE_LIST模式下文字越长按钮越宽。如果不想手算可以设置完文字后调用AutoSize让控件自己去量。3.2 分隔符和按钮分组工具栏上一组一组的功能区域比一条长龙友好得多。CToolBar支持通过ID_SEPARATOR插入分隔符也可以直接修改按钮样式实现分组。// 给第5个按钮前面加一个分隔符 m_wndToolBar.GetToolBarCtrl().SetButtonInfo(4, TBBS_SEPARATOR, 0);如果分隔符是静态写死在资源里的那改起来更简单。但要注意分隔符本身也占一个索引位后续SetButtonText的索引必须跳过它。还有一种更优雅的分组方式是给按钮追加TBBS_GROUP样式让一组按钮在视觉上靠得更近表示它们属于同一类操作。这在做“文件操作”“编辑操作”这类分组时很实用。3.3 图片和文字的间距控制文字和图片之间的空隙TBSTYLE_LIST模式下默认会给一点点间距但有时候不够用。最常见的做法是给图标外侧留透明边距——在制作图标资源时四周多留几个像素的透明区域视觉上就有间距了。也可以用改变SetImageSize和SetButtonSize的差值来制造间距比如图标区设24x24按钮区设80x36多余的56像素宽度就成了文字和图标之间的间隔。注意如果图标资源本身没有透明边距又想快速加间距可以在SetImageSize时把尺寸设得比实际图标大一些并设置ILC_MASK让多余部分作为透明区域处理。4. 实操复盘常见问题与排查技巧4.1 工具栏或按钮消失不见了遇到工具栏空白时先按下面这个表格排查现象可能原因排查步骤整个Toolbar不显示没有调用DockControlBar/EnableDocking在OnCreate里检查停靠代码有栏位但按钮空白图标资源加载失败确认IDI_ICON_XXX资源存在窗口有按钮位置但完全不可见图片列表生命周期结束检查m_imgList是否为成员变量编译报错ID不识别命令ID没有声明在Resource.h在资源编辑器里补上ID定义我自己遇到过最隐蔽的一种情况LoadToolBar(IDR_MAINFRAME)里按钮命令ID和工具栏控件内实际按钮数量不一致导致后面按钮索引和图标索引全部错位。处理办法是做一个ASSERT(m_wndToolBar.GetToolBarCtrl().GetButtonCount() nExpectedCount)在调试期就暴露出数量不匹配的问题。4.2 图片出来了文字却始终不显示这是整个定制流程里被问得最多的一个。图片正常显示SetButtonText也调了但按钮上就是没有字。绝大多数原因是漏了TBSTYLE_LIST样式文字没有对应的布局模式自然不展示。还有一部分情况是SetButtonText调用时机太早赶在LoadToolBar之前执行后者的资源加载把文字覆盖掉了。建议顺序固定为创建工具栏、加载资源、设置图片列表、绑定图标索引、设置文字、AutoSize。这个顺序不要乱。另外检查一下是否漏调用SetButtonSize如果按钮宽度不够文字会被压缩成“…”甚至完全看不见。4.3 文字和图片挤在一起排版没法看常见原因是图标资源本身没有预留空白加上按钮宽度设置过小。处理方式有两种一是重新调整SetButtonSize给文字留足空间二是给图标文件本身预留透明边距。我一般更倾向第二种因为图标透明区域在菜单、对话框里也能统一视觉效果。如果是在TBSTYLE_LIST下文字偏左、离图标太近可以给图片列表加一个空白图标占位或者在文字字符串前面加一个空格。空格字符虽然土但在紧急上线时非常管用调整成本几乎为零。4.4 高DPI环境下图标模糊这个问题在Windows 10/11触屏本上特别明显。默认没做DPI感知声明的MFC程序系统会对工具栏位图做拉伸缩放图标直接糊成一团。解决思路是在程序清单里声明PerMonitorV2的DPI感知并在收到WM_DPICHANGED消息时重新加载对应尺寸的图标然后重新SetImageList。case WM_DPICHANGED: RecreateToolbarImages(); // 按新DPI加载对应尺寸图标 break;实测下来用LoadImage指定LR_DEFAULTSIZE在保护性缩放下效果一般真正干净的做法还是为不同DPI提供不同分辨率的图标资源或者在运行时用SHCreateImageList生成多分辨率列表。如果项目实在来不及改可以先在清单里声明PerMonitorV2至少保证系统不再强行拉抻位图。5. 最后聊两句我在项目里的体会工具栏这种看似不起眼的控件做起来其实全是细节。CToolBarCImageListSetButtonText这套组合是我在好几轮项目迭代里验证过最稳的做法改图标不用动资源位图加按钮不用碰RC文件后期接手的人也不用在一张拼接图上猜哪块是哪块。尤其是老项目资源结构已经盘根错节用代码动态指定图标和文字维护成本比想象中低很多。还有一个我自己的小习惯凡是工具栏调用过AutoSize我一般会再往右多留出按钮宽度因为中文系统下按钮文字变宽之后默认宽度往往不够。如果按钮刚好卡在工具栏右边缘设置CBRS_SIZE_DYNAMIC让用户可以拖宽工具栏也是缓解文字截断的好办法。希望这篇实战记录能帮大家少踩几个坑早点下班。本文还有配套的精品资源点击获取
返回列表