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:
- 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:
- 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.
- Add a handler to TBN_QUERYDELETE in the frame window; that's the same code as adding TBN_QUERYINSERT. That's shown here:
- 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.)
- Now, we have the Toolbar information to answer the TBN_GETBUTTONINFO. Let's add the handler into the frame window:
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.
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.
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?

Comments
Access violation - FIXED
Posted by Harvey Block on 02/06/2007 01:41pmThe 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; } }-
ReplyCorrect !
Posted by Johnners on 01/09/2008 12:23pmHello 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
Replysorry
Posted by hailang8168 on 09/06/2005 02:20amExport / import toolbar to / from file
Posted by Laurs on 05/03/2005 07:08amHi. 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
ReplyCustomize all toolbars dialog ...
Posted by JJO on 08/24/2004 02:14amCurrent 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
ReplyThanks for the tips
Posted by Johnners on 07/20/2004 07:03amHi, 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
-
ReplyYou are correct!Thanks!
Posted by syhsyh on 07/24/2004 04:19amHi 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.
ReplyHere comes the code for save/load Toolbar button settings
Posted by syhsyh on 05/11/2004 02:26amHave 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?-
ReplyThanks! - Load / save toolbar
Posted by Laurs on 05/11/2004 03:44amHi. This was great. Laurs
ReplyHow to save the edited toolbar,
Posted by Laurs on 05/06/2004 08:01amI 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
-
Reply
Replysome ideas
Posted by syhsyh on 05/10/2004 04:54amYES!! Finally!
Posted by kevinp on 05/04/2004 02:47pmThank you man!! You have solved my question.
Reply