销毁 CMFCMenuBar 和 CMFCToolBar 并重新创建它们的正确方法是啥?
Posted
技术标签:
【中文标题】销毁 CMFCMenuBar 和 CMFCToolBar 并重新创建它们的正确方法是啥?【英文标题】:What is the correct way to destroy the CMFCMenuBar and CMFCToolBar and recreate them?销毁 CMFCMenuBar 和 CMFCToolBar 并重新创建它们的正确方法是什么? 【发布时间】:2020-06-23 06:35:09 【问题描述】:我想将 MFC 应用程序更改为可感知每个监视器。大多数事情都可以正常工作,但菜单和工具栏却不行。我想简单地重新创建它们,大概是拾取当前的 DPI。我基本上试过了:
if (m_wndToolBar.GetSafeHwnd())
m_wndToolBar.DestroyWindow();
if (m_wndMenuBar.GetSafeHwnd())
m_wndMenuBar.DestroyWindow();
随后是正常的创建过程,但在 m_wndMenuBar.Create()
它断言。
这是在 m_wndMenuBar.Create(this)
中失败的地方
if (!CMFCBaseToolBar::Create(
GetGlobalData()->RegisterWindowClass(_T("Afx:ToolBar")), dwStyle, rect, pParentWnd, nID, 0))
return FALSE;
【问题讨论】:
【参考方案1】:假设您的主窗口源自CFrameWnd
,您可以从应用程序的资源重新加载菜单栏,应该重置它(其中resource
是主菜单的 ID,并且加速器表):
CMyFrameWnd *pFrame = dynamic_cast(AfxGetApp()->AfxGetMainWnd()); if (pFrame && pFrame->IsWindowEnabled())) HMENU defMenu = LoadMenu(AfxGetInstanceHandle(), MAKEINTRESOURCE(resource)); pFrame->m_wndMenuBar.CreateFromMenu(defMenu, TRUE, TRUE); pFrame->LoadAccelTable(MAKEINTRESOURCE(resource));
您可以可能使用CMFCToolBar::LoadToolBar()
成员在工具栏上执行类似的操作,但我没有使用或测试过该功能。
【讨论】:
它没有改变字体大小。我希望它能像启动时那样扩展。所以我们的想法是销毁现有项目,让它再次创建它们,MFC 会自动设置大小(在每个监视器 dpi 模式下)。 添加了失败位置的详细信息。【参考方案2】:在每个显示器模式下处理 DPI 缩放。这似乎适用于默认菜单栏和工具栏。
class CCustomMFCToolBar : public CMFCToolBar
public:
BOOL FullRestoreOriginalState()
// ensure restore original
if (m_uiOriginalResID == 0)
return FALSE;
// remove existing images
ResetAllImages();
// reset id so bitmaps are loaded again
UINT resid=m_uiOriginalResID;
m_uiOriginalResID=0;
// handle loading toolbar and bitmaps
BOOL bRes = LoadToolBar(resid);
AdjustLayout();
if (IsFloating())
RecalcLayout();
else if (m_pParentDockBar != NULL)
CSize sizeCurr = CalcFixedLayout(FALSE, IsHorizontal());
CRect rect;
GetWindowRect(rect);
if (rect.Size() != sizeCurr)
SetWindowPos(NULL, 0, 0, sizeCurr.cx, sizeCurr.cy, SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOZORDER);
UpdateVirtualRect();
m_pDockBarRow->ArrangePanes(this);
AFXGetParentFrame(this)->RecalcLayout();
RedrawWindow(NULL, NULL, RDW_FRAME | RDW_INVALIDATE | RDW_UPDATENOW | RDW_ERASE);
return bRes;
;
在此处捕获 DPI 更改或重新启动所有项目,因为 WM_SETTINGCHANGED 在 WM_DPICHANGED 之后调用
void CMainFrame::OnSettingChange(UINT uFlags, LPCTSTR lpszSection)
CFrameWndEx::OnSettingChange(uFlags, lpszSection);
g_DPIHelper.SetChangedScaleForWindow(m_hWnd);
struct GlobalDataUnprotect : AFX_GLOBAL_DATA
#define AFX_FONT_NAME_OFFICE _T("Tahoma")
#define AFX_FONT_NAME_OFFICE_2007 _T("Segoe UI")
#define AFX_FONT_NAME_DEFAULT _T("MS Sans Serif")
#define AFX_FONT_NAME_VERT _T("Arial")
#define AFX_FONT_NAME_MARLETT _T("Marlett")
// This is a copy of UpdateFonts() modified a couple places to
// set the correct / changed scaling.
static int CALLBACK FontFamilyProcFonts(const LOGFONT FAR* lplf, const TEXTMETRIC FAR* lptm, ULONG ulFontType, LPARAM lParam)
ENSURE(lplf != NULL);
ENSURE(lParam != NULL);
CString strFont = lplf->lfFaceName;
return strFont.CollateNoCase((LPCTSTR) lParam) == 0 ? 0 : 1;
void UpdateScaledFonts()
CWindowDC dc(NULL);
m_dblRibbonImageScale = static_cast<double>(g_DPIHelper.GetScale())/100.0;
if (m_dblRibbonImageScale > 1. && m_dblRibbonImageScale < 1.1)
m_dblRibbonImageScale = 1.;
if (fontRegular.GetSafeHandle() != NULL)
::DeleteObject(fontRegular.Detach());
if (fontTooltip.GetSafeHandle() != NULL)
::DeleteObject(fontTooltip.Detach());
if (fontBold.GetSafeHandle() != NULL)
::DeleteObject(fontBold.Detach());
if (fontDefaultGUIBold.GetSafeHandle() != NULL)
::DeleteObject(fontDefaultGUIBold.Detach());
if (fontUnderline.GetSafeHandle() != NULL)
::DeleteObject(fontUnderline.Detach());
if (fontDefaultGUIUnderline.GetSafeHandle() != NULL)
::DeleteObject(fontDefaultGUIUnderline.Detach());
if (fontVert.GetSafeHandle() != NULL)
::DeleteObject(fontVert.Detach());
if (fontVertCaption.GetSafeHandle() != NULL)
::DeleteObject(fontVertCaption.Detach());
if (fontMarlett.GetSafeHandle() != NULL)
::DeleteObject(fontMarlett.Detach());
if (fontSmall.GetSafeHandle() != NULL)
::DeleteObject(fontSmall.Detach());
// Initialize fonts:
NONCLIENTMETRICS info;
info.cbSize = sizeof(info);
GetNonClientMetrics (info);
LOGFONT lf;
memset(&lf, 0, sizeof(LOGFONT));
lf.lfCharSet = (BYTE) GetTextCharsetInfo(dc.GetSafeHdc(), NULL, 0);
lf.lfHeight = info.lfMenuFont.lfHeight;
lf.lfWeight = info.lfMenuFont.lfWeight;
lf.lfItalic = info.lfMenuFont.lfItalic;
//------------------
// Adjust font size:
//------------------
int nFontHeight = lf.lfHeight < 0 ? -lf.lfHeight : lf.lfHeight;
if (nFontHeight <= 12)
nFontHeight = 11;
else if (!m_bDontReduceFontHeight)
nFontHeight--;
lf.lfHeight = (lf.lfHeight < 0) ? -nFontHeight : nFontHeight;
lf.lfHeight=g_DPIHelper.ScaleNonClientMetricsFont(lf.lfHeight);
// Check if we should use system font
lstrcpy(lf.lfFaceName, info.lfMenuFont.lfFaceName);
BOOL fUseSystemFont = m_bUseSystemFont || (info.lfMenuFont.lfCharSet > SYMBOL_CHARSET);
if (!fUseSystemFont)
// Check for "Segoe UI" or "Tahoma" font existance:
if (::EnumFontFamilies(dc.GetSafeHdc(), NULL, FontFamilyProcFonts, (LPARAM)(LPCTSTR) AFX_FONT_NAME_OFFICE_2007) == 0)
// Found! Use MS Office 2007 font!
lstrcpy(lf.lfFaceName, AFX_FONT_NAME_OFFICE_2007);
lf.lfQuality=5; // CLEARTYPE_QUALITY;
else if (::EnumFontFamilies(dc.GetSafeHdc(), NULL, FontFamilyProcFonts, (LPARAM)(LPCTSTR) AFX_FONT_NAME_OFFICE) == 0)
// Found! Use MS Office font!
lstrcpy(lf.lfFaceName, AFX_FONT_NAME_OFFICE);
else
// Not found. Use default font:
lstrcpy(lf.lfFaceName, AFX_FONT_NAME_DEFAULT);
fontRegular.CreateFontIndirect(&lf);
// Create small font:
LONG lfHeightSaved = lf.lfHeight;
lf.lfHeight = (long)((1. + abs(lf.lfHeight)) * 2 / 3);
if (lfHeightSaved < 0)
lf.lfHeight = -lf.lfHeight;
fontSmall.CreateFontIndirect(&lf);
lf.lfHeight = lfHeightSaved;
// Create tooltip font:
NONCLIENTMETRICS ncm;
ncm.cbSize = sizeof(ncm);
GetNonClientMetrics (ncm);
lf.lfItalic = ncm.lfStatusFont.lfItalic;
lf.lfWeight = ncm.lfStatusFont.lfWeight;
fontTooltip.CreateFontIndirect(&lf);
lf.lfItalic = info.lfMenuFont.lfItalic;
lf.lfWeight = info.lfMenuFont.lfWeight;
// Create "underline" font:
lf.lfUnderline = TRUE;
fontUnderline.CreateFontIndirect(&lf);
lf.lfUnderline = FALSE;
// Create bold font:
lf.lfWeight = FW_BOLD;
fontBold.CreateFontIndirect(&lf);
// Create Marlett font:
BYTE bCharSet = lf.lfCharSet;
lf.lfWeight = info.lfMenuFont.lfWeight;
lf.lfCharSet = SYMBOL_CHARSET;
lf.lfWeight = 0;
lf.lfHeight=g_DPIHelper.ScaleNonClientMetricsFont(::GetSystemMetrics(SM_CYMENUCHECK) - 1);
lstrcpy(lf.lfFaceName, AFX_FONT_NAME_MARLETT);
fontMarlett.CreateFontIndirect(&lf);
lf.lfCharSet = bCharSet; // Restore charset
// Create vertical font:
CFont font;
if (font.CreateStockObject(DEFAULT_GUI_FONT))
if (font.GetLogFont(&lf) != 0)
lf.lfOrientation = 900;
lf.lfEscapement = 2700;
lf.lfHeight = g_DPIHelper.ScaleNonClientMetricsFont(info.lfMenuFont.lfHeight);
lf.lfWeight = info.lfMenuFont.lfWeight;
lf.lfItalic = info.lfMenuFont.lfItalic;
lstrcpy(lf.lfFaceName, AFX_FONT_NAME_VERT);
fontVert.CreateFontIndirect(&lf);
lf.lfEscapement = 900;
fontVertCaption.CreateFontIndirect(&lf);
// Create dialog underline and bold fonts:
CFont* pDefaultGUIFont = CFont::FromHandle((HFONT) GetStockObject(DEFAULT_GUI_FONT));
ASSERT_VALID(pDefaultGUIFont);
pDefaultGUIFont->GetLogFont(&lf);
lf.lfUnderline = TRUE;
fontDefaultGUIUnderline.CreateFontIndirect(&lf);
lf.lfUnderline = FALSE;
lf.lfWeight = FW_BOLD;
fontDefaultGUIBold.CreateFontIndirect(&lf);
UpdateTextMetrics();
extern CObList afxAllToolBars;
// Notify toolbars about font changing:
for (POSITION posTlb = afxAllToolBars.GetHeadPosition(); posTlb != NULL;)
CMFCToolBar* pToolBar = (CMFCToolBar*) afxAllToolBars.GetNext(posTlb);
ENSURE(pToolBar != NULL);
if (CWnd::FromHandlePermanent(pToolBar->m_hWnd) != NULL)
ASSERT_VALID(pToolBar);
pToolBar->OnGlobalFontsChanged();
;
GlobalDataUnprotect *globaldata=reinterpret_cast<GlobalDataUnprotect*>(GetGlobalData());
globaldata->UpdateScaledFonts();
m_wndToolBar.CleanUpLockedImages();
m_wndToolBar.FullRestoreOriginalState();
【讨论】:
以上是关于销毁 CMFCMenuBar 和 CMFCToolBar 并重新创建它们的正确方法是啥?的主要内容,如果未能解决你的问题,请参考以下文章