运行时调整CToolbar高度后,如何让按钮图片垂直居中?
Visual C++ MDI工具栏运行时DPI调整后按钮图片无法垂直居中问题
问题描述
开发Visual C++ MDI应用时,包含3个CToolBar工具栏。为响应运行时DPI变化(如修改屏幕显示设置),会动态调整工具栏高度(有时含宽度),尺寸调整正常,但修改高度后按钮图片无法垂直居中,始终紧贴工具栏顶部。仅在关闭重启应用后,图片才能正确居中显示。
调整流程:
- 用户运行时修改DPI
- 基于新DPI计算工具栏所需高度
- 分离工具栏现有按钮位图
- 删除所有工具栏按钮
- 根据新DPI重置工具栏尺寸
- 加载新图片并附加到工具栏
即使加载与修改前尺寸相同的图片,问题依然存在,图片左上角位置未随工具栏高度增加而调整。
问题代码
//在我的CMainFrame.h文件中(CMainFrame是CMDIFrameWnd的子类) CToolBar my_toolbar; CBitmap m_bitmap_for_toolbar; //在我的CMainFrame.cpp文件中(CMainFrame是CMDIFrameWnd的子类): //分离工具栏图片使用的现有位图 (HBITMAP)m_bitmap_for_toolbar.Detach(); //调整工具栏尺寸前先移除现有按钮 int iButtonCount = my_toolbar.SendMessage(TB_BUTTONCOUNT, (WPARAM)0, (LPARAM)0); for (int i = iButtonCount; i > 0; i--) { nButtonIndex = i - 1; my_toolbar.SendMessage(TB_DELETEBUTTON, (WPARAM)nButtonIndex, (LPARAM)0); } //为工具栏分配新尺寸 my_toolbar.SetSizes(CSize(new_button_width, new_button_height), CSize(button_image_width, button_image_height)); //为工具栏创建新按钮 TBBUTTON tbButtonsFile[3] = { { 0, ID_FILE_NEW, TBSTATE_ENABLED, BTNS_AUTOSIZE, {0}, 0, (INT_PTR)NULL}, { 1, ID_FILE_OPEN, TBSTATE_ENABLED, BTNS_AUTOSIZE, {0}, 0, (INT_PTR)NULL}, { 2, ID_FILE_SAVE, TBSTATE_ENABLED, BTNS_AUTOSIZE, {0}, 0, (INT_PTR)NULL} }; my_toolbar.SendMessage(TB_BUTTONSTRUCTSIZE, (WPARAM)sizeof(TBBUTTON), 0); my_toolbar.SendMessage(TB_ADDBUTTONS, (WPARAM)3, (LPARAM)&tbButtonsFile); my_toolbar.SendMessage(TB_AUTOSIZE, 0, 0); my_toolbar.Invalidate(); //从文件加载工具栏按钮图片 HBITMAP hBitmap = (HBITMAP)LoadImage(NULL, str_bitmap_file_path, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE); m_bitmap_for_toolbar.Attach(hBitmap); //附加将用于工具栏图片的位图 my_toolbar.SetBitmap(m_bitmap_for_toolbar); my_toolbar.Invalidate(); //设置工具栏样式 my_toolbar.SetBarStyle(my_toolbar.GetBarStyle() | CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC);
原因分析
问题源于CToolBar内部布局逻辑:运行时动态调整尺寸后,仅调用SetSizes和Invalidate无法让工具栏重新计算按钮图片的垂直偏移量。重启应用时工具栏会完成完整初始化流程,能正确计算居中偏移,但运行时动态调整时,需手动触发关键布局参数的更新。
解决方案
通过以下修改强制工具栏重新计算图片布局:
- 使用
TB_DELETEALLBUTTONS高效删除所有按钮,避免循环索引错误 - 在
SetSizes后发送TB_SETBITMAPSIZE消息,明确告知工具栏位图尺寸(这是计算居中偏移的核心) - 调整位图加载顺序:先加载并设置位图,再添加按钮,让工具栏创建按钮时就使用正确的尺寸计算布局
- 添加
TB_REFRESH消息和UpdateWindow,强制工具栏立即刷新布局
修改后的代码
//在我的CMainFrame.cpp文件中(CMainFrame是CMDIFrameWnd的子类): //分离工具栏图片使用的现有位图 (HBITMAP)m_bitmap_for_toolbar.Detach(); // 高效删除所有按钮,替代循环删除 my_toolbar.SendMessage(TB_DELETEALLBUTTONS, 0, 0); //为工具栏分配新尺寸 my_toolbar.SetSizes(CSize(new_button_width, new_button_height), CSize(button_image_width, button_image_height)); // 明确设置位图尺寸,让工具栏知晓图片大小,用于计算居中偏移 my_toolbar.SendMessage(TB_SETBITMAPSIZE, 0, (LPARAM)MAKELONG(button_image_width, button_image_height)); // 提前加载位图:先加载并设置到位工具栏,再添加按钮 HBITMAP hBitmap = (HBITMAP)LoadImage(NULL, str_bitmap_file_path, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE); m_bitmap_for_toolbar.Attach(hBitmap); my_toolbar.SetBitmap(m_bitmap_for_toolbar); //为工具栏创建新按钮 TBBUTTON tbButtonsFile[3] = { { 0, ID_FILE_NEW, TBSTATE_ENABLED, BTNS_AUTOSIZE, {0}, 0, (INT_PTR)NULL}, { 1, ID_FILE_OPEN, TBSTATE_ENABLED, BTNS_AUTOSIZE, {0}, 0, (INT_PTR)NULL}, { 2, ID_FILE_SAVE, TBSTATE_ENABLED, BTNS_AUTOSIZE, {0}, 0, (INT_PTR)NULL} }; my_toolbar.SendMessage(TB_BUTTONSTRUCTSIZE, (WPARAM)sizeof(TBBUTTON), 0); my_toolbar.SendMessage(TB_ADDBUTTONS, (WPARAM)3, (LPARAM)&tbButtonsFile); my_toolbar.SendMessage(TB_AUTOSIZE, 0, 0); // 强制工具栏刷新布局并立即更新显示 my_toolbar.SendMessage(TB_REFRESH, 0, 0); my_toolbar.UpdateWindow(); // 工具栏样式建议在初始化时设置,避免重复操作 my_toolbar.SetBarStyle(my_toolbar.GetBarStyle() | CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC);
内容的提问来源于stack exchange,提问作者cjm20
相关产品推荐
相关产品推荐

