General Articles and Tutorials

Adding A Drop Arrow To A Toolbar Button

Author: Kirk Stowell
Platform: Visual C++ MFC
Downloads:
g5_prj.zip - Source Files with Demo Project [ 27.98 Kb ]

If you want to add a drop menu like the ones seen in Internet Explorer, it is pretty straight forward. This approach will work for both Visual C++ 5 and 6, however you may want to read up on the enhancements to the toolbar class for VC 6.0. Also, make sure you read the article Q190501 "BUG: Resizing CToolbar with Dropdown Arrow Buttons Freezes Apps" in the MS Knowledge Base.

Toolbar Drop Arrow

First off, after your toolbar has been created in CMainFrame::OnCreate(), you will need to make a call to the following

// Set the drop arrow.
DWORD dwExStyle = TBSTYLE_EX_DRAWDDARROWS;
m_wndToolBar.GetToolBarCtrl().SendMessage(
    TB_SETEXTENDEDSTYLE, 0, (LPARAM)dwExStyle);

This will enable your toolbar to handle drop arrows. The next thing you will need to do is to actually add the drop arrow to your desired button. This will be done via the SetButtonStyle() method:

DWORD dwStyle = m_wndToolBar.GetButtonStyle(
   m_wndToolBar.CommandToIndex(ID_FILE_OPEN));

   dwStyle |= TBSTYLE_DROPDOWN;
   m_wndToolBar.SetButtonStyle(
        m_wndToolBar.CommandToIndex(ID_FILE_OPEN), dwStyle);

return 0;
}

Now, you will need to add a message handler for the drop arrow, as well a menu to the application resources. Assuming you already know how to create a menu, ( if not, click on the resource tab, select the resource name IE: MyApp Resources, then right click. Select insert, then select Menu, then press the New button ) and assuming that the resource id for our menu is IDR_MENU1, add the following code to CMainFrame's message map:

BEGIN_MESSAGE_MAP(CMainFrame, CMDIFrameWnd)
   //{{AFX_MSG_MAP(CMainFrame)
   ON_WM_CREATE()
   ON_NOTIFY(TBN_DROPDOWN, AFX_IDW_TOOLBAR, OnToolbarDropDown)
   //}}AFX_MSG_MAP
END_MESSAGE_MAP()

Add the following method to CMainFrame's .cpp file:

void CMainFrame::OnToolbarDropDown(
       NMTOOLBAR* pnmtb, LRESULT *plr)
{
  CWnd *pWnd;
  UINT nID;

  // Switch on button command id's .
  switch (pnmtb->iItem)
  {
   case ID_FILE_OPEN:
      pWnd = &m_wndToolBar;
      nID  = IDR_MENU1;
      break;
   default:
      return;

   }

   // load and display popup menu
   CMenu menu;
   menu.LoadMenu(nID);
   CMenu* pPopup = menu.GetSubMenu(0);
   ASSERT(pPopup);

   CRect rc;
   pWnd->SendMessage(TB_GETRECT,
             pnmtb->iItem, (LPARAM)&rc);
   pWnd->ClientToScreen(&rc);

   pPopup->TrackPopupMenu(
     TPM_LEFTALIGN | TPM_LEFTBUTTON | TPM_VERTICAL,
     rc.left, rc.bottom, this, &rc);
}

Then add the following to CMainFrame's .h file:

// Generated message map functions
protected:
  //{{AFX_MSG(CMainFrame)
  afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
  //}}AFX_MSG
  afx_msg void OnToolbarDropDown(
                 NMTOOLBAR* pnmh, LRESULT* plRes);
 DECLARE_MESSAGE_MAP()
};