关于 CMFCToolBar 的一些说明 – 停靠大按钮和 MDIClientArea 的使用
将 CToolBar 更新到 MFC 功能包 (2008)
引言
本文讨论 CMFCToolBar
类,并记录了从 CToolBar
更改的许多细节,以及如何使 CMFCToolBar
按预期方式工作。
背景
CMFCToolBar
是 2008 年添加的 MFC 功能包的一部分,它取代了 CToolBar
。本文使用了 Visual Studio 2012 Update 2。
通常,创建工具栏的代码位于 CMainFrame::OnCreate
方法中。下面展示了创建两个工具栏,以处理带有大按钮的工具栏和带有标准大小按钮的另一个工具栏。如果只使用标准大小的按钮,可以跳过处理 szImage
和 szButton
的代码。 此代码对于正确显示大按钮是必要的。首先,声明 m_nToolBar
和 m_nAnalysisTool
为 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);
请注意,如果 CMFCToolBarInfo
为空,则 LoadToolBarEx
会填充它,因此每个工具栏使用一个新的 CMFCToolBarInfo
。
CMFCToolBar 放置示例
在测试代码之前,请务必删除注册表项 *HKCU\Software\CompanyName\ProgramName\Workspace*,否则将使用上次的工具栏放置,如果您使用 WindowPlacement
加载注册表。
如果使用 CBRS_ALIGN_ANY
,工具栏默认在顶部对齐。
现在,有三个例子说明工具栏的放置
示例 1
默认行为是工具栏堆叠在顶部。 这种外观并不总是令人满意。
m_nToolBar.EnableDocking(CBRS_ALIGN_ANY);
DockPane(&m_nToolBar);
m_nAnalysisTool.EnableDocking(CBRS_ALIGN_ANY);
DockPane(&m_nAnalysisTool);
示例 2
要使 CMFCToolBar
最初停靠在一侧,然后可以自由地停靠在任何一侧,首先仅通过 EnableDocking
强制所需的侧,然后调用 DockPane
。这样可以正确放置工具栏。然后可以再次调用 EnableDocking
并允许更多侧有效。 图下方的三行显示了这一点。
m_nToolBar.EnableDocking(CBRS_ALIGN_ANY);
DockPane(&m_nToolBar);
m_nAnalysisTool.EnableDocking(CBRS_ALIGN_BOTTOM);
DockPane(&m_nAnalysisTool);
m_nAnalysisTool.EnableDocking(CBRS_ALIGN_ANY);
示例 3
要在顶部的同一栏上强制并排停靠,首先停靠最右侧的窗格,然后将额外的窗格添加到其左侧。
m_nToolBar.EnableDocking(CBRS_ALIGN_ANY);
m_nAnalysisTool.EnableDocking(CBRS_ALIGN_ANY);
DockPane(&m_nAnalysisTool);
m_dockManager.DockPaneLeftOf(&m_nToolBar, &m_nAnalysisTool);
编码说明
不要尝试使用 DockToFrameWindow()
,此方法仅适用于 CDockablePane
类,而 CMFCToolBar
的层次结构中没有 CDockablePane
。 当从 CMFCTooBar
类调用时,DockToFrameWindow()
方法除了返回 false
之外什么也不做。人们会认为 CMFCToolBar
类会继承自 CDockablePane
类,但事实并非如此。
CMFCToolBar
不负责自行切换。 如果你使用了 ID_VIEW_TOOLBAR
,CToolBar
之前会负责切换。 要现在处理切换,需要在消息映射中进行以下添加。(这通常在 CMainFrame
类中完成。)
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)
然后添加方法。
// 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 和在 MDIClientArea 上绘图
CMFCToolBar
和在 MDIClientArea
上绘图存在问题。 打开和关闭工具栏或移动停靠位置可能会导致 MDIClientArea
上的图像损坏。 CToolBar
类总是触发到 MDIClient
的 OnDraw
消息。 对于 MFC 2008 功能包,无法进行子类化,因此无法覆盖 OnDraw
例程。
除非在 MDIClientArea
上绘图,否则上面的 OnView
… 方法不需要 m_wndClientArea.Invalidate()
行。 在客户端区域中绘图时,当工具栏操作导致客户端区域更改大小时,需要这些行,因为调整大小会破坏你的图像。
包含 RecalcLayout()
调用是为了处理关闭具有较大按钮大小的工具栏并且两个工具栏位于同一行的情况;如果没有强制,工具栏的区域不会针对较小的按钮调整大小。 使用旧的 CToolBar
类会自动处理这些情况。
移动工具栏也可能会破坏你在客户端区域上的绘图。 我编写了一个小类 CMFCToolBarEx
来扩展 CMFCToolBar
并覆盖 OnAfterChangeParent()
。 *CMFCToolBarEx.h* 中的代码是
#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);
};
*CMFCToolBarEx.cpp* 中的代码是
// 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);
}
如果你有两组图像想要显示在工具栏上而不更改按钮的 ID,则以下代码将更改按钮上的图像。 IDR_ANANLYSIS_TOOL
和 IDR_ANALYSIS_TOOL_RATE
是具有相同大小的工具栏。 工具栏仅在按钮上的图像上有所不同。
在 CMFCToolBar 上交换图像
如果你有两组图像想要显示在工具栏上而不更改按钮的 ID,则以下代码将更改按钮上的图像。 IDR_ANANLYSIS_TOOL
和 IDR_ANALYSIS_TOOL_RATE
是具有相同大小的工具栏。 工具栏仅在按钮上的图像上有所不同。
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();
}