Make a Customized ToolBar Dialog Work in MFC (VC6)

Make a Customized ToolBar Dialog Work in MFC (VC6)

Am I the only fool in this world? I searched the Web, posted questions on newsgroups, but found nothing and nobody answered me. It seems everybody knows how to do this except me. The customize toolbar dialogue either goes by with a flash or I could not add buttons back after they were deleted.

MSDN said you must answer several Notify messages. They are TBN_QUERYINSERT, TBN_QUERYDELETE, and TBN_GETBUTTONINFO, but there is no code showing how to do it.

I finally found the way after having a detailed look at the structure of CToolBarEx. It's simple but I don't know why nobody posts it.

Here is the way to accomplish the task:

  1. Let the wizard make a standard MFC .exe, create a menu—let's call it "Customize ToolBar"—and use the wizard to create a hndle in CFrameWindow. In the handle, write:
  2. CToolBarCtrl& myTBCtrl = m_wndToolBar.GetToolBarCtrl();
    myTBCtrl.Customize();
    

    Compile and link, run the .exe, and then click the "Customize ToolBar" menu option; this makes the Customize dialog flash when you click the "Customize ToolBar" menu option. That's because your frame window has no answering handler to the message of TBN_QUERYINSERT.

  3. Add a handler to TBN_QUERYINSERT in the frame windows. In the Mainfrm.h file, add

    afx_msg void QueryInsert(NMHDR * pNotifyStruct,
                             LRESULT * result);
    

    In the Mainfrm.cpp file's message map, add

    ON_NOTIFY(TBN_QUERYINSERT,AFX_IDW_TOOLBAR,QueryInsert)

    Also, in the Mainfrm.cpp file, add:

    void CMainFrame::QueryInsert(NMHDR * pNotifyStruct,
                                 LRESULT * result )
    {
       *result = TRUE;
    }
    

    Now, because the frame windows will answer the TBN_QUERYINSERT message, the Customize ToolBar dialog knows you are allowing the ToolBar change. You see that, when you click the "Customize ToolBar" menu, the dialog appears, but you cannot delete any button. Neither can you add any button except the separator. Why? Because there is no handler for TBN_QUERYDELETE in the frame window.

  4. Add a handler to TBN_QUERYDELETE in the frame window; that's the same code as adding TBN_QUERYINSERT. That's shown here:
  5. afx_msg void QueryDelete(NMHDR * pNotifyStruct,
                             LRESULT * result);
    ON_NOTIFY(TBN_QUERYDELETE,AFX_IDW_TOOLBAR,QueryDelete)
    void CMainFrame::QueryDelete(NMHDR * pNotifyStruct,
                                 LRESULT * result )
    {
       *result = TRUE;
    }
    

    Now, you see you could 'Remove' (delete) the button, but after that you could never get it back, even when you click 'Add'. Why? Actually, before the button adds back, the dialog sends another message—TBN_GETBUTTONINFO—to the frame window to search the button info you choose. If there is no info reply, there is no way to add back the button. So, we need the third handler for TBN_GETBUTTONINFO.

  6. How we get it to answer TBN_GETBUTTONINFO? We need collect all the toolbar button info before the dialog appears. Okay, let's do it after the frame windows create the toolbar.

    We add a private function in the frame window, and add two members into frame windows to save the info and the count of how many buttons you have in your CToolbar.

    In the frame windows Mainfrm.h file, add the function:

    private:
    void GetToolbarButtonInfo();
    

    Also, add the members:

    TBBUTTON m_TBinfo[256];    // suppose you have a toolbar
                               // button maxed to 256
    int      m_iTBCount;
    

    In the Mainfrm.cpp file, add:

    void CMainFrame::GetToolbarButtonInfo()
    {
       CToolBarCtrl& myTBCtrl = m_wndToolBar.GetToolBarCtrl();
    
       //get how many toolbar buttons you have when the frame loads
       m_iTBCount = myTBCtrl.GetButtonCount();
    
       //save loaded toolbar button info into a m_TBinfo array
       for (int i=0; i<=m_iTBCount; i++)
       {
          myTBCtrl.GetButton(i,&m_TBinfo[i]);
       }
    
    }
    

    We call GetToolbarButtonInfo() right after the toolbar was created in the CFrameWnd:: OnCreate(...).

    if (!m_wndToolBar.CreateEx(this, TBSTYLE_FLAT, WS_CHILD |
                               WS_VISIBLE | CBRS_TOP |
                               CBRS_GRIPPER | CBRS_TOOLTIPS |
                               CBRS_FLYBY |
                               CBRS_SIZE_DYNAMIC) ||
       !m_wndToolBar.LoadToolBar(IDR_MAINFRAME))
    {
       TRACE0("Failed to create toolbar\n");
       return -1;    // fail to create
    }
    
    GetToolbarButtonInfo();
    

    (You see, we did not add a parameter of CCS_ADJUSTABLE, but the toolbar is still adjustable.)

  7. Now, we have the Toolbar information to answer the TBN_GETBUTTONINFO. Let's add the handler into the frame window:
  8. In Mainfrm.h, add:

    afx_msg void QueryInfo(NMHDR * pNotifyStruct,
                           LRESULT * result);
    

    In Mainfrm.cpp, add:

    ON_NOTIFY(TBN_GETBUTTONINFO,AFX_IDW_TOOLBAR,QueryInfo)
    void CMainFrame::QueryInfo(NMHDR * pNotifyStruct,
                               LRESULT * result )
    {
       TBNOTIFY* pTBntf = (TBNOTIFY *)pNotifyStruct;
    
       if((pTBntf->iItem>=0) && (pTBntf->iItem <= m_iTBCount))
       {
          pTBntf->tbButton = m_TBinfo[pTBntf->iItem];
          *result = TRUE;
       }
    
       else
       {
          *result = FALSE;
       }
    }
    

Compile, link, and run it. Does it work now?



Downloads

Comments

  • Access violation - FIXED

    Posted by Harvey Block on 02/06/2007 01:41pm

    The project as is causes an access violation.
    Otherwise, nice info. Thanks!
    Here is a correction. Original line is commented out. Corrected line is noted. The index was going one too far. How did/does this work on anyones computer as it was?
    Harvey (harveyab AT juno.com)
    
    void CMainFrame::QueryInfo(NMHDR * pNotifyStruct, LRESULT * result )
    {
        TBNOTIFY* pTBntf = (TBNOTIFY *)pNotifyStruct;
    	
    //  if((pTBntf->iItem>=0) && (pTBntf->iItem <= m_iTBCount)) // Caused Access violation (after return)
        if((pTBntf->iItem>=0) && (pTBntf->iItem < m_iTBCount)) // Corrected
        {
            pTBntf->tbButton = m_TBinfo[pTBntf->iItem];
            *result = TRUE;
        }
        else
        {
            *result = FALSE;
        }
    }

    • Correct !

      Posted by Johnners on 01/09/2008 12:23pm

      Hello Harvey, If you look through "All comments" on this topic, you'll notice that this was pointed out a couple of years ago. The project gave unpredictable results before that. I guess the author just didn't have time to update the project with this comment. But I'm not going to criticise the author for that, because he (and this thread)saved me from tearing out what little hair I had left! Regards, John

      Reply
    Reply
  • sorry

    Posted by hailang8168 on 09/06/2005 02:20am

    I download your project,but it doesn't work !

    Reply
  • Export / import toolbar to / from file

    Posted by Laurs on 05/03/2005 07:08am

    Hi. Is there anyone which can help me with export and import of the toolbar. Lets assume that a user has customized the toolbar. The program responsible then wants to export the toolbar and import it to another PC's registry. I followed the code for saving / loading from registry but has no idea how to export thise values to a file, and them import them on another pc. Laurs

    Reply
  • Customize all toolbars dialog ...

    Posted by JJO on 08/24/2004 02:14am

    Current Microsoft products like Word, Excel, Outliik etc. have all the same tool to manipulate toolbar costomization ( through Tools/Customize... ). This enables the user to manipulate all toolbars, rearrange menu items etc. The fact that this facility is global to the Microsoft applications, I wondered if this facility could be acticated from a MFC (VC++) application so that I do not need to code it myself ... Kind regards, Jan Jochimsen

    Reply
  • Thanks for the tips

    Posted by Johnners on 07/20/2004 07:03am

    Hi, like a lot of people, I was struggling with customising my toolbars, but thanks to this thread, I've done it. One minor comment is that the l;ine in CMainFrame::QueryInfo if((pTBntf->iItem>=0) && (pTBntf->iItem <= m_iTBCount)) should probably read if((pTBntf->iItem>=0) && (pTBntf->iItem < m_iTBCount)) because m_TBinfo[] is zero based and m_iTBCount is not. I was getting unpredictable results when using '<=' but changed it to '<' and everything worked out fine. Thanks again

    • You are correct!Thanks!

      Posted by syhsyh on 07/24/2004 04:19am

      Hi johnners, Yes, your comments are absolutely correct, it solved my problem. And I also found if use 'pTBntf->iItem <= m_iTBCount;' will make application not work in Windows98. Thanks a lot.

      Reply
    Reply
  • Here comes the code for save/load Toolbar button settings

    Posted by syhsyh on 05/11/2004 02:26am

    Have just notice that, a built-in function have already in CToolBarCtrl, that's 
    CToolBarCtrl::SaveState and CToolBarCtrl::RestoreState.
    
    So I add the 'save setting' codes when FrameWnd close, and 'restore setting' codes after
    Create toolbar and GetToolbarButtonInfo().
    
    Here is the steps:
    
    1.Add a private function of Save/load toolbar settings in the FrameWnd:
    
    void CMainFrame::SaveLoadToolBar(BOOL bSave)
    {
    	//when bSave = TRUE save setting , FALSE for load settings
    	CString myToolBarRegStr = "ToolBar";//better define your string at .h
    	CString mysubkey = "SOFTWARE\\";
    	CWinApp* myapp = AfxGetApp();
    	CToolBarCtrl & myToolBarCtrl = m_wndToolBar.GetToolBarCtrl();
    
    	mysubkey = mysubkey + myapp->m_pszRegistryKey+"\\"+myapp->m_pszAppName+"\\"+"Settings";
    	
    	if(bSave)
    	{
    		myToolBarCtrl.SaveState(HKEY_CURRENT_USER,(LPCTSTR)mysubkey,(LPCTSTR)myToolBarRegStr);
    	}
    	else	
    	{
    		myToolBarCtrl.RestoreState(HKEY_CURRENT_USER,(LPCTSTR)mysubkey,(LPCTSTR)myToolBarRegStr);
    	}
    
    }
    
    2. Add load toolbar setting after FrameWnd create toolbar in Framewnd
    
    int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
    {
    ...
    	if (!m_wndToolBar.CreateEx(this, TBSTYLE_FLAT, WS_CHILD | WS_VISIBLE | CBRS_TOP
    		| CBRS_GRIPPER | CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC) ||
    		!m_wndToolBar.LoadToolBar(IDR_MAINFRAME))
    	{
    		TRACE0("Failed to create toolbar\n");
    		return -1;      // fail to create
    	}
    
    	GetToolbarButtonInfo();
    	SaveLoadToolBar(FALSE); //code for load setting of toolbar
    ...
    }
    
    3. Add save toolbar setting before FrameWnd destroyed. Add save toolbar setting func
    into a new created virtue function of FrameWnd::DestroyWindow()
    
    BOOL CMainFrame::DestroyWindow() 
    {
    	// TODO: Add your specialized code here and/or call the base class
    	SaveLoadToolBar(TRUE);
    	
    	return CFrameWnd::DestroyWindow();
    }
    
    Finally, I could see sometimes when insert the toolbar, the toolbar is not redraw/refresh,
    need add some code to trigger it, but where to add?

    • Thanks! - Load / save toolbar

      Posted by Laurs on 05/11/2004 03:44am

      Hi. This was great. Laurs

      Reply
    Reply
  • How to save the edited toolbar,

    Posted by Laurs on 05/06/2004 08:01am

    I am really happy that I found your article. I have used it with Visul C++.NET 2003 and it runs. I have 2 questions as I use this on my own user defined toolbars, not on the windows main toolbar (m_wndToolBar): 1) Do you have a tip to save the toolbar in its new form to registry. 2) Do you have a tip of how to use the tooltip text for the toolbar in the Customize dialog? wit best Regards Laurs Laursen

    • some ideas

      Posted by syhsyh on 05/10/2004 04:54am

      Hi, Laurs
      
      1. To save the edited toolbar, I could find some tips in MSDN at http://msdn.microsoft.com/library/default.asp?url=/library/en-us/shellcc/platform/commctls/toolbar/messages/tb_saverestore.asp, I hope I could work out some codes to demo it.
      2. Your question about save your own user defined toolbar. I think once you have the handle of your own toolbarctrl, the same code should work. because CToolBarCtrl::Customize() is attached to toolbarctrl and not related to framewindow.
      3. For your question of usage of tooltip text in the dialog, I never try and now I will begin.
      
      Thanks!

      Reply
    Reply
  • YES!! Finally!

    Posted by kevinp on 05/04/2004 02:47pm

    Thank you man!! You have solved my question.

    Reply
Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Live Event Date: December 11, 2014 @ 1:00 p.m. ET / 10:00 a.m. PT Market pressures to move more quickly and develop innovative applications are forcing organizations to rethink how they develop and release applications. The combination of public clouds and physical back-end infrastructures are a means to get applications out faster. However, these hybrid solutions complicate DevOps adoption, with application delivery pipelines that span across complex hybrid cloud and non-cloud environments. Check out this …

  • With the average hard drive now averaging one terabyte in size, the fallout from the explosion of user-created data has become an overwhelming volume of potential evidence that law-enforcement and corporate investigators spend countless hours examining. Join Us and SANS' Rob Lee for our 45-minute webinar, A Triage and Collection Strategy for Time-Sensitive Investigations, will demonstrate how to: Identify the folders and files that often contain key insights Reduce the time spent sifting through content by …

Most Popular Programming Stories

More for Developers

RSS Feeds