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; }
CSize szImage, szButton;
szImage = m_nToolBar.GetImageSize();
szButton.cx = szImage.cx + 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; }
if (!m_nAnalysisTool.CreateEx( this, dwCtrlStyle, dwStyle, r1, IDR_ANALYSIS_TOOL) ||
!m_nAnalysisTool.LoadToolBarEx(IDR_ANALYSIS_TOOL, tbiA, TRUE))
{
return -1; }
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.
void CMainFrame::OnViewControls() {
m_nToolOpen = !m_nToolOpen;
ShowPane(&m_nToolBar, m_nToolOpen, FALSE, TRUE);
if (!m_nToolOpen) RecalcLayout();
m_wndClientArea.Invalidate(); }
void CMainFrame::OnUpdateViewControls(CCmdUI* pCmdUI)
{
pCmdUI->SetCheck(m_nToolOpen);
}
{
m_nAnalysisToolOpen = !m_nAnalysisToolOpen;
ShowPane(&m_nAnalysisTool, m_nAnalysisToolOpen , FALSE, TRUE);
m_wndClientArea.Invalidate(); }
void CMainFrame::OnUpdateViewAnalysisControls(CCmdUI* pCmdUI)
{
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
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:
#include "stdafx.h"
#include "MainFrm.h"
#include "MFCToolBarEx.h"
IMPLEMENT_DYNAMIC(CMFCToolBarEx, CMFCToolBar)
CMFCToolBarEx::CMFCToolBarEx()
{
}
CMFCToolBarEx::~CMFCToolBarEx()
{
}
BEGIN_MESSAGE_MAP(CMFCToolBarEx, CMFCToolBar)
END_MESSAGE_MAP()
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();
}