销毁 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 并重新创建它们的正确方法是啥?的主要内容,如果未能解决你的问题,请参考以下文章

CMFCMenuBar 显示错误的工具提示

如何右对齐 CMFCMenuBar 中的“帮助”菜单项

帮助查找 CMFCMenuBar 上的工具提示问题

当组件被销毁时,Angular 如何销毁事件处理程序和属性绑定

Srping之Bean的初始化和销毁

zz Android -- Activity的销毁和重建