Switching Toolbars in MDI

The following method displays specific toolbar for each MDI child window CView type, identified by an ID related to that doc/view, while the mainframe toolbar is always displayed.

The idea is to retrieve the toolbar ID from the view class. This is done by identifying the active MDI child, retrieving its document class, and from that document class retrieving the template class which holds the ID. This is done each time a new window is set, by detecting WM_MDISETMENU message in the client window (CMainClient).

The main frame window holds two additional arrays: one of the toolbar pointers and one of the IDs of the already loaded toolbar. When a new menu is set, the main frame searches for the toolbar ID loaded IDs array. If found, the toolbar from the loaded toolbar array, from index if the required ID, is displayed. Otherwise, a new toolbar created, and the toolbar pointer and the ID are stored in the appropriate arrays and that toolbar is displayed.

This code was developed and tested using VC6.0 on NT 4.

The following 3 pictures show the demo application with toolbar 1 (figure 1), toolbar 2 (figure 1) and without toolbar (figure 3). The differences are in the buttons background


Figure 1: Mainframe toolbar and Tolbar 1 are shown.


Figure 2: Mainframe toolbar and Tolbar 2 are shown.


Figure 3: Only mainframe toolbar is shown.

Integration

In order to integrate this method into your code you should do the following:

  • Create a toolbar for each one of your doc/view classes. Give that toolbar the same ID as the menu ID that assigned for that doc/view.
  • Create a new class, say CMainClient, derived from CWnd (generic CWnd), to be used as the MDI client
  • Add the following two handlers to your MDI client class (CMainClient):
  • 
    LRESULT CMainClient::DefWindowProc(UINT message, WPARAM wParam, LPARAM lParam) 
    { 
    /***************************************************************\ 
    | This function calls the main frame window to switch toolbars  | 
    | when a new MDI menu is set.                                   | 
    \***************************************************************/ 
        LRESULT lRet = CWnd::DefWindowProc(message, wParam, lParam); 
        if (message == WM_MDISETMENU) { 
            CMainFrame *pFrame; 
    
            pFrame = (CMainFrame *) AfxGetMainWnd (); 
            if (::IsWindow (pFrame)) 
                pFrame->SwitchToolbar (); 
        } 
        return (lRet); 
    } 
    
    void CMainClient::OnParentNotify(UINT message, LPARAM lParam) 
    { 
    /***************************************************************\ 
    | This function calls the main frame window to switch toolbars  | 
    | when a all the MDI child windows are closed.                  | 
    \***************************************************************/ 
        CWnd::OnParentNotify(message, lParam); 
        CMainFrame *pFrame; 
        CWnd *pWnd; 
    
        pWnd = AfxGetMainWnd (); 
        if (pWnd->IsKindOf (RUNTIME_CLASS (CMainFrame))) 
            pFrame = (CMainFrame *) AfxGetMainWnd (); 
        else 
            return; // don't crash the application, just leave 
        switch (LOWORD (message)) { 
            case WM_CREATE: 
                m_nChilds++; 
                break; 
            case WM_DESTROY: 
                m_nChilds--; 
                if (m_nChilds == 0) 
                    pFrame->SwitchToolbar (); 
                break; 
            default: 
                break; 
        } 
    } 
    
    
  • Create a new class, say CDocTemplateEx, derived from CDocTemplate. Add the following public method to this class:
  • 
    int CDocTemplateEx::GetResourceID () 
    { 
        return (m_nIDResource); 
    } 
    
    
  • Add the following member variables to CMainFrame class:
  • 
    CArray m_aToolbars; 
    CArray m_idrLoaded; 
    CMainClient m_wndClient; 
    
    
  • Add the following line in CMainFrame::OnCreate function, immediately after the call to CMDIFrameWnd::OnCreate:
  • 
    int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct) 
    { 
         if (CMDIFrameWnd::OnCreate(lpCreateStruct) == -1) 
            return -1; 
      
     m_wndClient.SubclassWindow (m_hWndMDIClient); // this is what YOU add 
    ... 
    } 
    
    
  • Add the following method to CMainFrame class:
  • 
    #include "DocTemplateEx.h" 
    
    void CMainFrame::SwitchToolbar () 
    { 
    /***************************************************************\ 
    | This function retrive the resource ID from the current view.  | 
    | Then it calls 'SetToolbar' to activate the specified toolbar  | 
    | and to disactivets all the others.                            | 
    | If no view is open the resource ID, 'idResource', is set to 0 | 
    | and all toolbars are disactivated.                            | 
    | External functions used:                                      | 
    | IsWindow (CWnd *pWnd)                                         | 
    \***************************************************************/ 
    
        CView *pView; 
        CDocTemplateEx *pTmpl; 
        CMDIChildWnd *pKid; 
        int idResource = 0; // initialize incase no resource is found 
    
        pKid = MDIGetActive (); 
        if (::IsWindow (pKid)) { 
            pView = pKid->GetActiveView (); 
            if (::IsWindow (pView)) { 
                pTmpl = (CDocTemplateEx *) pView->GetDocument ()->GetDocTemplate (); 
                idResource = pTmpl->GetResourceID (); 
            } 
        } 
        SetToolbar (idResource); 
    } 
    
    void CMainFrame::SetToolbar (int nIdr) 
    /***************************************************************\ 
    | This function gets the toolbar index to the toolbars array,   | 
    | 'm_vToolbars'. The it loops over all the toolbars. If the     | 
    | toolbar the one to be displayed it is closed.                 | 
    | External functions used:                                      | 
    | ShowControlBar                                                | 
    | RecalcLayout                                                  | 
    \***************************************************************/ 
    { 
        static int nToolbars; 
        int n, nIdx; 
        CString str; 
        CToolBar *pTB; 
    
        str.Format ("Toolbar #%d", ++nToolbars); 
        nIdx = AddToolbar (nIdr, str); 
        for (n=0 ; n < m_vToolbars.GetSize () ; n++) { 
            pTB = m_vToolbars.GetAt (n); 
            if (n != nIdx) { 
                ShowControlBar (pTB, 0, 0); 
            } 
            else 
                ShowControlBar (pTB, 1, 1); 
        } 
        m_wndToolBar.GetParentFrame()->RecalcLayout(); 
    } 
    
    int CMainFrame::AddToolbar (int nIDR, CString strWndTxt) 
    /***************************************************************\ 
    | This function returns the index of the toolbar, that have the | 
    | ID of 'nIDR', in the toolbar array, 'm_vToolbars'.            | 
    | If the ID does not exist in the loaded ID array, 'm_idrLoaded'| 
    | the function creates a new toolbar, ads the toolbar pointer   | 
    | to the toolbar array and the ID to the loaded toolbar IDs     | 
    | array.                                                        | 
    | The function also docks the toolbar left to the mainframe     | 
    | toolbar.                                                      | 
    | External functions used:                                      | 
    | ShowControlBar                                                | 
    | FindInArray                                                   | 
    \***************************************************************/ 
    { 
        CToolBar *pTB; 
        BOOL f; 
        CRect rc; 
        int nIdx; 
    
        if (nIDR == 0) 
            return (-1); 
        if ((nIdx = ::FindInArray (m_idrLoaded, nIDR)) < 0) { 
            pTB = new CToolBar; 
            pTB->Create (this); 
            f = pTB->LoadToolBar (nIDR); 
            if (f == 0) { 
                pTB->DestroyWindow (); 
                delete pTB; 
                return (-1); 
            } 
            pTB->SetBarStyle(m_wndToolBar.GetBarStyle() | 
                                CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC); 
            pTB->EnableDocking(CBRS_ALIGN_TOP); 
            m_wndToolBar.GetWindowRect (&rc); 
            rc.OffsetRect(1,0); 
            DockControlBar(pTB, AFX_IDW_DOCKBAR_TOP, &rc); 
            pTB->SetWindowText (strWndTxt); 
            pTB->ShowWindow (SW_RESTORE); 
            ShowControlBar (pTB, 1, 1); 
            nIdx = m_vToolbars.Add (pTB); 
            m_idrLoaded.Add (nIDR); 
        } 
        return (nIdx); 
    } 
    
    

I hope someone out there find this helpful. I'll be thankful for any comment and/or correction.

Download demo project - 47 KB



Comments

  • Another Solution

    Posted by Legacy on 06/07/2002 12:00am

    Originally posted by: Amit Gefen

    
    


    //On creation XView the template ID is stored in the
    //view. The tool bar is with the same ID as template.
    void CXView::OnSetFocus(CWnd* pOldWnd)
    {
    CView::OnSetFocus(pOldWnd);

    m_wndUserText.SetFocus();

    //AG (SetToolBar)
    if (m_ResourceID!=0)
    ((CIRCApp*) ::AfxGetApp())->SetToolBar(m_ResourceID);
    }

    App is accessable naywhere by ::AfxGetApp() & easily find
    the main-frame:

    //Create this new method. Cancel tool bar creation in
    //usual place OnCreate(). m_pToolBar become a pointer
    //to CToolBar & needs to be deleted also in destructor.
    BOOL CMainFrame::SelectToolBar(UINT nID)//AG
    {
    if (m_pToolBar)
    delete m_pToolBar;
    m_pToolBar = new CToolBar();

    if (!m_pToolBar->Create(this, WS_CHILD | WS_VISIBLE | CBRS_TOP) ||
    !m_pToolBar->LoadToolBar(nID))
    {
    TRACE0("Failed to create toolbar\n");
    return -1; // fail to create
    }

    // TODO: Delete these three lines if you don't want the toolbar to
    // be dockable
    m_pToolBar->EnableDocking(CBRS_ALIGN_ANY);
    EnableDocking(CBRS_ALIGN_ANY);
    DockControlBar(m_pToolBar);

    return TRUE;
    };

    • An improvement for Visual C++ .Net

      Posted by Jim McCreary on 06/30/2005 03:03pm

      With the code above the toolbar did not show up properly for me using Visual C++ .Net (version 7)
      Basically I had to sub CreateEx for Create as:
      
      bool CMainFrame::SelectToolBar(UINT unIDR)
      {
      	if ( m_pToolBar ) 
      	{
      		delete m_pToolBar;
      	}
      	m_pToolBar = new CToolBar();
      	if (!m_pToolBar->CreateEx(this, TBSTYLE_FLAT, WS_CHILD | WS_VISIBLE | CBRS_TOP
      		| CBRS_GRIPPER | CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC) ||
      		!m_pToolBar->LoadToolBar(unIDR))
      	{
      		TRACE("Failed to create toolbar %d\n", unIDR);
      		return false;
      	}
      	// delete the 3 lines below if toolbar is not to be dockable
      	m_pToolBar->EnableDocking(CBRS_ALIGN_ANY);
      	EnableDocking(CBRS_ALIGN_ANY);
      	DockControlBar(m_pToolBar);
      
      	return true;
      }

      Reply
    Reply
  • Drag and drop between views in a splitter window

    Posted by Legacy on 05/22/1999 12:00am

    Originally posted by: Gene Rodrigues

    Doesn't MFC handle this automatically based on the individual document templates you create using their assocaited IDR_DOCTYPE? If the menu name uses the same IDR_DOCTYPE defined for the template, the switch to the appropriate menu associated with the document template should be handled automatically.

    Reply
  • comments

    Posted by Legacy on 03/26/1999 12:00am

    Originally posted by: subrosa

    You could also just stick the toolbar on the the client frame so you wouldn't have to switch all the time

    Reply
  • Demo project aborts after selecting "switch" or "switch1"

    Posted by Legacy on 03/23/1999 12:00am

    Originally posted by: Steve Quick

    Using VC++ 6.0, windows98. After selecting "switch" or "switch1" the program aborts with:
    
    

    SWITCHTB caused a general protection fault
    in module MACXW4.DRV at 000a:000016a7.
    Registers:
    EAX=00000053 CS=0357 EIP=000016a7 EFLGS=00010246
    EBX=0001d83b SS=2d3f ESP=ca6f7d90 EBP=00008270
    ECX=00000020 DS=0000 ESI=00000000 FS=0000
    EDX=00000000 ES=0157 EDI=00000000 GS=0000
    Bytes at CS:EIP:
    67 66 c7 45 24 03 00 66 c5 7d 24 c7 85 58 ff ff
    Stack dump:
    5f490849 5f4908d0 0065ee80 005301c0 0065f0f8 00431104 24398de0 00001747 02000001 00200408 0002f448 8e2816bf 000c0000 00280065 bff728a2 00781a04


    Looking forward to using this in my project.

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

Top White Papers and Webcasts

  • Protecting business operations means shifting the priorities around availability from disaster recovery to business continuity. Enterprises are shifting their focus from recovery from a disaster to preventing the disaster in the first place. With this change in mindset, disaster recovery is no longer the first line of defense; the organizations with a smarter business continuity practice are less impacted when disasters strike. This SmartSelect will provide insight to help guide your enterprise toward better …

  • Live Event Date: August 14, 2014 @ 2:00 p.m. ET / 11:00 a.m. PT Data protection has long been considered "overhead" by many organizations in the past, many chalking it up to an insurance policy or an extended warranty you may never use. The realities of today make data protection a must-have, as we live in a data driven society. The digital assets we create, share, and collaborate with others on must be managed and protected for many purposes. Check out this upcoming eSeminar and join eVault Chief Technology …

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds