MFC: CMFCDropDownToolBar is blank for all but the first one button

174 views Asked by At

Created a CMFCDropDownToolBarin CMainFrame::OnCreate:

// Loading toolbar with a single icon/entry.
if (!m_wndMyDropdownToolBar.Create(this,   
    WS_CHILD|CBRS_TOP|CBRS_TOOLTIPS|CBRS_FLYBY|CBRS_HIDE_INPLACE|CBRS_SIZE_DYNAMIC|   
    CBRS_GRIPPER | CBRS_BORDER_3D,   
    ID_ACTION_BUTTON) ||    
    !m_wndMyDropdownToolBar.LoadToolBar (IDR_TOOLBAR_DROPDOWNSELECT))   
{   
    TRACE0("Failed to create build toolbar\n");   
    return FALSE;      // fail to create   
}   

// build list - use existing icon
for (UINT i=0; i<2; i++) {
  CString s;
  s.Format(_T("%i: Whatever %i\n"), i, i);
  m_wndMyDropdownToolBar.InsertButton(CMFCToolBarButton(ID_COMMAND_START+i, 0, s));
};

Then the rest:

afx_msg LRESULT CMainFrame::OnToolbarReset(WPARAM idtoolbar, LPARAM)
{
  if (idtoolbar==IDR_MAINFRAME) {
    ASSERT(m_wndMyDropdownToolBar.GetSafeHwnd() != NULL);

     //-----------------------------------   
     // Replace dropdown button:   
     //-----------------------------------   
    m_wndToolBar.ReplaceButton(ID_ACTION_BUTTON_DUMMY,
                               CMFCDropDownToolbarButton(_T("Text that doesn't show anywhere"), &m_wndMyDropdownToolBar));
  }

  return 0;
}


BOOL CMainFrame::GetToolbarButtonToolTipText(CMFCToolBarButton* pButton, CString& strTTText)
{
  if (pButton->m_nID>=ID_COMMAND_START && pButton->m_nID<=ID_COMMAND_END) {
    // use text
    strTTText=pButton->m_strText;
    return TRUE;
  }
  return FALSE;
}   

It all works fine as far as the drop down shows the buttons, hovering shows the tooltip, but when you select one of the buttons added manually via the InsertButton() call, the button on the main toolbar is blank and no tooltips. If I then go back and select first button (part of resource loaded), it shows the correct icon and tool tip.

What am I not doing correct?

Thanks!!

1

There are 1 answers

1
user3161924 On

The problem is that CMFCDropDownToolbarButton::SetDefaultCommand(UINT uiCmd) is flawed. If not a bUserButton type then it assumes the index to the m_ImagesLocked is by position instead of taking the buttons iIndex. You can't use bUserButton and support bLargeButtons since the user buttons have to be the same size. Also the button that sits on the main toolbar takes on the first items m_strText (normally empty string since loaded with toolbar) so the tooltip updates via CMainFrame::GetToolbarButtonToolTipText() above don't work either (it should pull in the buttons text but doesn't). So probably test best thing to do is take afxdropdowntoolbar.cpp and duplicate it in the project then fix it with a new class name. A little code bloat, if only SetDefaultCommand() was virtual, could fix it easily.

Also note, if using LoadBitmap / LoadBitmapEx, you should set bLocked to TRUE (as well as when using InsertButton())


Have it all working the way it should have to start with, if you need this, here's the core changes:

Ugh, needed to override the protected CMFCToolBarImages::CopyTemp() so instead of having to redo almost all of the MFC toolbar support I added this hack! I doesn't add any data members so should maintain compatiblity with CMFCToolBar.

class CMyToolBarImages : public CMFCToolBarImages
{
  public:

  void MyCopyTemp(CMyToolBarImages *imagesDest) const
  {
      imagesDest->Clear();
      imagesDest->m_bIsTemporary = TRUE;

      imagesDest->m_sizeImage = m_sizeImage;
      imagesDest->m_sizeImageDest = m_sizeImageDest;
      imagesDest->m_hbmImageWell = m_hbmImageWell;
      imagesDest->m_bUserImagesList = m_bUserImagesList;
      imagesDest->m_iCount = m_iCount;
      imagesDest->m_bReadOnly = TRUE;
      imagesDest->m_nBitsPerPixel = m_nBitsPerPixel;
  }
};

class CMyDropDownToolBar : public CMFCToolBar
{
   .
   .
   .
   
  virtual BOOL OnUserToolTip(CMFCToolBarButton* pButton, CString& strTTText) const;

  // HACK protected CMFCToolBarImages!!
  void CopyTempAll(CMyDropDownToolBar *desttoolbar) const
  {
    ((const CMyToolBarImages*) &m_ImagesLocked)->MyCopyTemp((CMyToolBarImages*) &desttoolbar->m_ImagesLocked);
    ((const CMyToolBarImages*) &m_ColdImagesLocked)->MyCopyTemp((CMyToolBarImages*) &desttoolbar->m_ColdImagesLocked);
    ((const CMyToolBarImages*) &m_DisabledImagesLocked)->MyCopyTemp((CMyToolBarImages*) &desttoolbar->m_DisabledImagesLocked);
    ((const CMyToolBarImages*) &m_LargeImagesLocked)->MyCopyTemp((CMyToolBarImages*) &desttoolbar->m_LargeImagesLocked);
    ((const CMyToolBarImages*) &m_LargeColdImagesLocked)->MyCopyTemp((CMyToolBarImages*) &desttoolbar->m_LargeColdImagesLocked);
    ((const CMyToolBarImages*) &m_LargeDisabledImagesLocked)->MyCopyTemp((CMyToolBarImages*) &desttoolbar->m_LargeDisabledImagesLocked);
  }
};

Have it call CMainFrame for tooltip:

BOOL CMyDropDownToolBar::OnUserToolTip(CMFCToolBarButton* pButton, CString& strTTText) const
{
    ASSERT_VALID(pButton);

    CFrameWnd* pTopFrame = AFXGetParentFrame(this);
    if (pTopFrame == NULL)
    {
        return FALSE;
    }

    CMyDropDownFrame* pDropFrame = DYNAMIC_DOWNCAST(CMyDropDownFrame, pTopFrame);
    if (pDropFrame != NULL)
    {
        pTopFrame = AFXGetParentFrame(pDropFrame);
        if (pTopFrame == NULL)
        {
            return FALSE;
        }
    }

    CMDIFrameWndEx* pMainFrame = DYNAMIC_DOWNCAST(CMDIFrameWndEx, pTopFrame);
    if (pMainFrame != NULL)
    {
        return pMainFrame->GetToolbarButtonToolTipText(pButton, strTTText);
    }
    else // Maybe, SDI frame...
    {
        CFrameWndEx* pFrame = DYNAMIC_DOWNCAST(CFrameWndEx, pTopFrame);
        if (pFrame != NULL)
        {
            return pFrame->GetToolbarButtonToolTipText(pButton, strTTText);
        }
        else // Maybe, MDIChild frame
        {
            CMDIChildWndEx* pMDIChild = DYNAMIC_DOWNCAST(CMDIChildWndEx, pTopFrame);

            if (pMDIChild != NULL)
            {
                return pMDIChild->GetToolbarButtonToolTipText(pButton, strTTText);
            }
            else // Maybe, OLE frame...
            {
                COleIPFrameWndEx* pOleFrame = DYNAMIC_DOWNCAST(COleIPFrameWndEx, pFrame);
                if (pOleFrame != NULL)
                {
                    return pOleFrame->GetToolbarButtonToolTipText(pButton, strTTText);
                }
            }
        }
    }

    return FALSE;

}

For the actual first fix needed that started this whole thing:

void CMyDropDownToolbarButton::SetDefaultCommand(UINT uiCmd)
{
  .
  .
  .

        if (pButton->m_nID == uiCmd)
        {
            m_bLocalUserButton = pButton->m_bUserButton;

            if (m_bLocalUserButton)
            {
                m_iSelectedImage = pButton->GetImage();
            }
            else
            {
                // FIXED HERE:
                m_iSelectedImage=pButton->GetImage();
                if (m_iSelectedImage==-1) {
                    m_iSelectedImage=iImage;
                }
            }
            // FIXED HERE:
            m_strText=pButton->m_strText;
            break;
        }

  .
  .
  .
}

To get the tool tips to look consistent with bold and normal, had it use CMainFrame::GetMessageString:

class CMyDropDownFrame : public CMiniFrameWnd
{
   .
   .
   .
   
   // For customizing the default messages on the status bar and second line of tooltip
   virtual void GetMessageString(UINT nID, CString& rMessage) const;
};


// For customizing the default messages on the status bar and second line of tooltip
void CMyDropDownFrame::GetMessageString(UINT nID, CString& rMessage) const
{
    CFrameWnd* pTopFrame = AFXGetParentFrame(this);
    if (pTopFrame == NULL)
    {
        return;
    }

    CMFCDropDownFrame* pDropFrame = DYNAMIC_DOWNCAST(CMFCDropDownFrame, pTopFrame);
    if (pDropFrame != NULL)
    {
        pTopFrame = AFXGetParentFrame(pDropFrame);
        if (pTopFrame == NULL)
        {
            return;
        }
    }

    CMDIFrameWndEx* pMainFrame = DYNAMIC_DOWNCAST(CMDIFrameWndEx, pTopFrame);
    if (pMainFrame != NULL)
    {
        pMainFrame->GetMessageString(nID, rMessage);
    return;
    }
    else // Maybe, SDI frame...
    {
        CFrameWndEx* pFrame = DYNAMIC_DOWNCAST(CFrameWndEx, pTopFrame);
        if (pFrame != NULL)
        {
          pFrame->GetMessageString(nID, rMessage);
      return;
        }
        else // Maybe, MDIChild frame
        {
            CMDIChildWndEx* pMDIChild = DYNAMIC_DOWNCAST(CMDIChildWndEx, pTopFrame);

            if (pMDIChild != NULL)
            {
            pMDIChild->GetMessageString(nID, rMessage);
        return;
            }
            else // Maybe, OLE frame...
            {
                COleIPFrameWndEx* pOleFrame = DYNAMIC_DOWNCAST(COleIPFrameWndEx, pFrame);
                if (pOleFrame != NULL)
                {
              pOleFrame->GetMessageString(nID, rMessage);
          return;
                }
            }
        }
    }
}

Also needed to change CMyDropDownFrame::OnCreate() to deal with protect member:

int CMyDropDownFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
  .
  .
  .
  
    // "Clone" the original toolbar:
  m_pWndOriginToolbar->CopyTempAll(&m_wndToolBar);
  /*
    m_pWndOriginToolbar->m_ImagesLocked.CopyTemp(m_wndToolBar.m_ImagesLocked);
    m_pWndOriginToolbar->m_ColdImagesLocked.CopyTemp(m_wndToolBar.m_ColdImagesLocked);
    m_pWndOriginToolbar->m_DisabledImagesLocked.CopyTemp(m_wndToolBar.m_DisabledImagesLocked);
    m_pWndOriginToolbar->m_LargeImagesLocked.CopyTemp(m_wndToolBar.m_LargeImagesLocked);
    m_pWndOriginToolbar->m_LargeColdImagesLocked.CopyTemp(m_wndToolBar.m_LargeColdImagesLocked);
    m_pWndOriginToolbar->m_LargeDisabledImagesLocked.CopyTemp(m_wndToolBar.m_LargeDisabledImagesLocked);
  */

  .
  .
  .
 };