Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Some Notes on CMFCToolBar – Docking Large Buttons and Use of the MDIClientArea

0.00/5 (No votes)
23 Jul 2013 1  
Updating CToolBar to MFC Feature Pack (2008)

Introduction

This article deals with the CMFCToolBar class, and documents a number of details that changed from CToolBar and how to get CMFCToolBar to behave.

Background

CMFCToolBar is part of the MFC Feature Pack added in 2008, it replaces CToolBar. Visual Studio 2012 Update 2 was used in this article.

The code to create toolbars generally goes in the CMainFrame::OnCreate method. The creation of two toolbars is shown below to handle a toolbar with large buttons and another toolbar with standard size buttons. If only standard size buttons are used, one can skip the code that deals with szImage and szButton. This code is necessary to correctly display large buttons. First, declare m_nToolBar and m_nAnalysisTool to be CMFCToolBar.

DWORD dwCtrlStyle = TBSTYLE_FLAT | TBSTYLE_TOOLTIPS | CBRS_SIZE_DYNAMIC;
DWORD dwStyle = AFX_DEFAULT_TOOLBAR_STYLE;
CMFCToolBarInfo tbi, tbiA;
const CRect r1(1, 1, 1, 1); 
if (!m_nToolBar.CreateEx(this, dwCtrlStyle, dwStyle, r1, IDR_MAINFRAME) ||
        !m_nToolBar.LoadToolBarEx(IDR_MAINFRAME, tbi, TRUE)
{
    return -1;      // fail to create
} 
// this permits large buttons (>16x15) to display correctly
CSize szImage, szButton;
szImage = m_nToolBar.GetImageSize();
szButton.cx = szImage.cx + 6; // button size must be at least image size + 6
szButton.cy = szImage.cy + 6;
m_nToolBar.SetMenuSizes(szButton, szImage);

if (!m_wndStatusBar.Create(this) || 
        !m_wndStatusBar.SetIndicators(indicators, sizeof(indicators)/sizeof(UINT)))
{
    return -1;      // fail to create
}

// Adding Extra ToolBar
if (!m_nAnalysisTool.CreateEx( this, dwCtrlStyle, dwStyle, r1, IDR_ANALYSIS_TOOL) ||
        !m_nAnalysisTool.LoadToolBarEx(IDR_ANALYSIS_TOOL, tbiA, TRUE))
{
    return -1;      // fail to create
} 
szImage = m_nAnalysisTool.GetImageSize();
szButton.cx = szImage.cx + 6;
szButton.cy = szImage.cy + 6;
m_nAnalysisTool.SetMenuSizes(szButton, szImage);

dwStyle = CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC;
m_nToolBar.SetPaneStyle(m_nToolBar.GetPaneStyle() | dwStyle);
m_nAnalysisTool.SetPaneStyle(m_nToolBar.GetPaneStyle() | dwStyle); 
EnableDocking(CBRS_ALIGN_ANY);

Note that LoadToolBarEx fills CMFCToolBarInfo if it is empty, so use a new one for each toolbar.

CMFCToolBar Placement Examples

Be sure to remove registry entries HKCU\Software\CompanyName\ProgramName\Workspace before testing the code, otherwise the last toolbar placement will be used, if you are loading the registry with your WindowPlacement.

Toolbars default to align at the top if CBRS_ALIGN_ANY is used.

Now for three examples illustrating placement of tool bars:

Example 1

The default behavior is for tool bars to stack on the top. This look is not always desired.

Figure 1 Default Docking
m_nToolBar.EnableDocking(CBRS_ALIGN_ANY);
DockPane(&m_nToolBar);

m_nAnalysisTool.EnableDocking(CBRS_ALIGN_ANY);
DockPane(&m_nAnalysisTool);

Example 2

For a CMFCToolBar to initially dock on a side but then have freedom to dock on any side, first force the desired side by EnableDocking only to the side you wish, then call DockPane. This correctly places the toolbar. Then can call EnableDocking again and permit more sides to be valid. The three lines below the figure show this.

Figure 2 Docking top and bottom
m_nToolBar.EnableDocking(CBRS_ALIGN_ANY); 
DockPane(&m_nToolBar); 
m_nAnalysisTool.EnableDocking(CBRS_ALIGN_BOTTOM); 
DockPane(&m_nAnalysisTool);
m_nAnalysisTool.EnableDocking(CBRS_ALIGN_ANY);

Example 3

To force docking side by side on the same bar at the top, first dock the rightmost pane and then add the additional pane to the left of it.

Figure 3 Side by side docking
m_nToolBar.EnableDocking(CBRS_ALIGN_ANY); 
m_nAnalysisTool.EnableDocking(CBRS_ALIGN_ANY);
DockPane(&m_nAnalysisTool);  
m_dockManager.DockPaneLeftOf(&m_nToolBar, &m_nAnalysisTool);

Notes on the Coding

Do not attempt to use DockToFrameWindow(), this method only works with the CDockablePane class, and CMFCToolBar does not have CDockablePane in its hierarchy. The DockToFrameWindow() method does nothing but return false when called from the CMFCTooBar class. One would think that the CMFCToolBar class would inherit from the CDockablePane class, but it does not.

CMFCToolBar does not take care of toggling itself on or off. CToolBar previously did take care of toggling if you used ID_VIEW_TOOLBAR. To handle toggling now, one needs to make the following additions to the message map. (This is generally done in the CMainFrame class.)

ON_COMMAND(ID_VIEW_TOOLBAR, OnViewControls)
ON_UPDATE_COMMAND_UI(ID_VIEW_TOOLBAR, OnUpdateViewControls) 
ON_COMMAND(ID_VIEW_PLOTCONTROLS, OnViewAnalysisControls) 
ON_UPDATE_COMMAND_UI(ID_VIEW_PLOTCONTROLS, OnUpdateViewAnalysisControls)

Then add the methods.

// Toggle the ToolBar on and off.
void CMainFrame::OnViewControls() // Called when View->ToolBar menuitem is pressed.
{
  m_nToolOpen = !m_nToolOpen;
  ShowPane(&m_nToolBar, m_nToolOpen, FALSE, TRUE);
  // Force resize of toolbar when the larger toolbar is removed
  // (a problem if both toolbars are on the same line)   
  if (!m_nToolOpen) RecalcLayout();
  m_wndClientArea.Invalidate();  // force redraw to correct for image shifting
}

void CMainFrame::OnUpdateViewControls(CCmdUI* pCmdUI)
// Called when View is pressed or when View->ToolBar menuitem is displayed
{
    pCmdUI->SetCheck(m_nToolOpen);
}

// Toggle the Analysis ToolBar on and off. void CMainFrame::OnViewAnalysisControls()
// Called when View->Analysis ToolBar menuitem is pressed.
{
  m_nAnalysisToolOpen = !m_nAnalysisToolOpen;
  ShowPane(&m_nAnalysisTool, m_nAnalysisToolOpen , FALSE, TRUE);
  m_wndClientArea.Invalidate();  // force redraw to correct for image shifting
}

void CMainFrame::OnUpdateViewAnalysisControls(CCmdUI* pCmdUI)
// Called when View is pressed or when View->ToolBar menuitem is displayed
{
    pCmdUI->SetCheck(m_nAnalysisToolOpen);
}

CMFCToolBar and Drawing on the MDIClientArea

There are issues with CMFCToolBar and drawing on the MDIClientArea. Toggling tool bars on and off or moving the docking location may cause the image on the MDIClientArea to be corrupted. The CToolBar class always triggers an OnDraw message to the MDIClient. With the MFC 2008 Feature pack, it is not possible to subclass, so one cannot override the OnDraw routine.

The methods OnView… shown above do not need the m_wndClientArea.Invalidate() lines, unless drawing on the MDIClientArea. When drawing in the client area, these lines are needed when toolbar manipulation causes the client area to change size as the resizing will corrupt your image.

The RecalcLayout() call was included to handle the situation where the toolbar with a larger button size was turned off and both toolbars were on the same line; the area with the toolbars did not resize for smaller buttons without being forced. These situations were handled automatically with the older CToolBar class.

The moving of toolbars may also corrupt your drawing on the client area. I wrote a small class CMFCToolBarEx to extend CMFCToolBar and override OnAfterChangeParent(). The code in CMFCToolBarEx.h is:

#pragma once
 
// CMFCToolBarEx.h
 
class CMFCToolBarEx : public CMFCToolBar
{
    DECLARE_DYNAMIC(CMFCToolBarEx)
 
public:
    CMFCToolBarEx();
    virtual ~CMFCToolBarEx();
 
protected:
    DECLARE_MESSAGE_MAP()
public:
    virtual void OnAfterChangeParent(CWnd* pWndOldParent);
};

The code in CMFCToolBarEx.cpp is:

// MFCToolBarEx.cpp : implementation file
//
 
#include "stdafx.h"
#include "MainFrm.h"
#include "MFCToolBarEx.h"
 
// CMFCToolBarEx
 
IMPLEMENT_DYNAMIC(CMFCToolBarEx, CMFCToolBar)
 
CMFCToolBarEx::CMFCToolBarEx()
{
}
 
CMFCToolBarEx::~CMFCToolBarEx()
{
}
 
BEGIN_MESSAGE_MAP(CMFCToolBarEx, CMFCToolBar)
END_MESSAGE_MAP()
 
// CMFCToolBarEx message handlers
 
 
// Overrides
 
void CMFCToolBarEx::OnAfterChangeParent(CWnd* pWndOldParent)
{
    CMainFrame* pFrame = (CMainFrame*) AfxGetMainWnd();
    if (pFrame)
    {
        CRect rect;
        pFrame->GetClientRect(rect);
        pFrame->InvalidateRect(rect);
    }
    CMFCToolBar::OnAfterChangeParent(pWndOldParent);
}

If you have two sets of images that you wish to display on a toolbar without changing the ID of the buttons, the following code will change out the images on the buttons. IDR_ANANLYSIS_TOOL and IDR_ANALYSIS_TOOL_RATE are toolbars that have the same size. The toolbars only differ by the images on the buttons.

Swapping Images on CMFCToolBar

If you have two sets of images that you wish to display on a toolbar without changing the ID of the buttons, the following code will change out the images on the buttons. IDR_ANANLYSIS_TOOL and IDR_ANALYSIS_TOOL_RATE are toolbars that have the same size. The toolbars only differ by the images on the buttons.

void CMainFrame::SetAnalysisBar(void)
{
    BOOL b;
 
    m_nAnalysisTool.ResetAllImages();
    if (m_productionDataSet.m_PRODUCTION_MODE == 1)
        b = m_nAnalysisTool.LoadBitmap(IDR_ANALYSIS_TOOL);
    else
        b = m_nAnalysisTool.LoadBitmap(IDR_ANALYSIS_TOOL_RATE);
 
    if (b) m_nAnalysisTool.Invalidate();
}

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here