如何实现工具栏的下拉箭头按钮

2016-06-06

论坛中有人曾经问过这样的问题:如何在C++/MFC程序中实现类似IE工具栏中带下拉箭头的按钮,如图一所示:

图一 IE工具栏中带下拉箭头的按钮

本文将告诉你如何实现,方法其实很简单:

1、新建一个MFC/SDI工程,一路都取默认的设置。

2、在mainframe.cpp文件中找到 CMainFrame::OnCreate()框架窗口创建函数。在末尾添加如下代码行:

      DWORD dwExStyle = TBSTYLE_EX_DRAWDDARROWS;
      m_wndToolBar.GetToolBarCtrl().SendMessage(TB_SETEXTENDEDSTYLE, 0, (LPARAM)dwExStyle);      

这两行代码的作用是让工具栏具有处理下拉箭头的能力,接着要用SetButtonStyle()方法在选定的地方添加下拉箭头按钮,在例子程序里,下拉箭头按钮是加在了文件打开菜单上:

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

加了上述代码行之后,编译一次程序,然后运行程序,你就可以看到下拉箭头了。但此时还不能操作。

3、接下来要做的事情是添加下拉箭头的消息处理代码以及程序要用的菜单资源。现在假设你已经创建了菜单资源,且假设这个菜单的资源ID是IDR_MENU1。如图二所示。

 

图二 新创建的菜单资源IDR_MENU1

在CMainFrame类的消息映射中加入下拉箭头的TBN_DROPDOWN消息映射:

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

4、在MainFrame.h头文件中加入消息处理函数的声明:

      //{{AFX_MSG(CMainFrame)
      ...
      afx_msg void OnToolbarDropDown(NMTOOLBAR* pnmh, LRESULT* plRes);
      //}}AFX_MSG     

5、在MainFrame.cpp文件中加入TBN_DROPDOWN消息处理的实现代码:

      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);
      }      

6、前面第三步创建的菜单资源里有三个菜单命令,“最近打开的文件”、“打开关于对话框”、“VC知识库主页”。它们对应的菜单ID为:ID_FILE_MRU_FILE1、ID_APP_ABOUT、ID_VCKBASE_HOME,前两个ID都是MFC默认的菜单,它们调用的命令函数在例子程序中也是MFC自动创建和调用的。ID_VCKBASE_HOME菜单项在例子程序中是通过ClassView关联到视图类进行处理的,用户选择这个菜单项时调用OnGotoVckbaseHomePage():

      void CMyView::OnGotoVckbaseHomePage() 
      {
        	// TODO: Add your command handler code here
        	ShellExecute(NULL,"open","http://www.vckbase.com",
			NULL,NULL,SW_SHOWNORMAL);
      }     

它打开默认的浏览器并访问VC知识库主页。

好了,现在编译并运行程序吧。下面是例子程序运行画面(图三):

图三 例子程序运行画面

注意本文提供源代码仅仅只是个例子程序,在现实的软件设计和编程行为中,没有谁会在“打开文件”功能菜单里加入什么“打开关于对话框”和“VC知识库主页”之类的菜单命令。这有悖于Windows程序的UI规则。