不需要 Internet Explorer 的扁平化工具栏






4.38/5 (7投票s)
2000 年 2 月 25 日

100225

4831
一个扁平工具栏实现,无需 Internet Explorer 的更新版通用控件库。
对于那些已经阅读过 CodeGuru 上这篇文章的读者来说,主要增强功能从这里开始。我正在寻找有时间帮助我的人!
不久前,我看到了 Roger Onslow 的扁平工具栏实现。需要特殊产品 (MSIE)(甚至 DLL -> comctl32.dll)的事实对我来说有点不方便。所以我开始开发我自己的扁平外观工具栏版本,无需此类要求。结果是一个名为 CToolBarEx
的类。
使用 CToolBarEx
,可以在扁平模式和“经典”模式之间切换。会立即显示适当的外观。
如果您觉得代码的某些部分看起来很熟悉,请不要惊讶。分隔符和抓手的绘制(或多或少)是从 Roger 的工具栏“偷”来的(为什么我要做所有困难的部分呢 ;-)
在扁平模式下,CToolBarEx
会自行绘制所有内容;在经典模式下,MFC 会完成这项工作。
由于 VC++ >= 4.2 提供了自定义绘制功能,因此该功能将在扁平模式下通过本地辅助类进行模拟,这样无论当前模式(扁平或经典)如何,都可以使用该功能。有关所有者绘制的更多信息,请查看实现文件 ToolBarEx.cpp。位于 示例应用程序中的 MainFrm.cpp 文件如果还不熟悉工具栏上的所有者绘制,可能会提供更多信息。
但是,CToolBarEx
也应该可以与 VC++ 的旧版本一起使用...
CToolBarEx
由两个文件组成
- ToolBarEx.h
- ToolBarEx.cpp
要在 MFC 应用程序中使用 CToolBarEx
,您需要执行以下步骤(假设您使用 App-/Class-Wizard)
- 在 StdAfx.h 或 MainFrm.h 中包含
"ToolBarEx.h"
- 将
CMainFrame::m_wndToolBar
的类型从CToolBar
更改为CToolBarEx
CToolBarEx
类提供了以下公共方法(除了其祖类之外)
// Set the mode of the toolbar. The mode changes immediately // on the screen. void SetFlatLook( BOOL bFlat = TRUE ); // determine wether the current mode is "flat" BOOL IsFlatLook() const; // This function I've personally missed in CToolBar // for more than one time ... void GetSizes( CSize &sizeButton, CSize &sizeImage ) const; // Get the window to which the toolbar initially was docked. This // is not necessarily the window returned by CWnd::GetParent() or // CControlBar::GetDockingFrame(). Both these functions don't // return the expected window, if the toolbar is floating ... CWnd * GetParentFrame() const;
更新:代码现已增强。
- 按钮上的文本现在可以工作了
- 抓手已改进,外观更接近 Office97
- 禁用的图像现在看起来像浮雕
- (感谢 Victor Vogelpoel)
(Zafir:也感谢 Oscar,他提交了相同的修复) - 仅当分隔符没有设置 WRAP 状态时才绘制它
主要增强版
首先请注意,此增强版的CToolBarEx
目前在 NT < 4.0 上无法正常运行,因此如果您计划在该系统上使用 CToolBarEx
,请使用上面的标准版本!非常感谢 Jonathan Chin、Victor Vogelpoel 和 Patrick Liechty(三只“小白鼠” :-) 帮助测试和修复此版本的 CToolBarEx 类!
增强版包含两大功能:
- ALT 拖动功能现已实现。这意味着可以通过同时按住 ALT 键和鼠标左键,将按钮和控件从一个工具栏拖放到另一个工具栏。
CToolBarEx
使用相同的技术来实现可自定义性,就像CToolBarCtrl
一样,因此如果您不熟悉可调整的工具栏,请参阅在线帮助和/或查看增强示例。 - 一种简单、通用的方法,用于将自定义控件添加到工具栏。
CToolBarEx
类允许您添加/替换/重新定位工具栏上的控件,并负责这些控件。
这张(放大)的图像让您对新功能有所感受。
稍后需要做的事情
- 仍然无法保存/恢复自定义的工具栏。当使用自定义控件和/或将按钮/控件从一个栏移动到另一个栏时,底层
CToolBarCtrl
类的方法将不可用... - 仍然没有(Office97-或 DevStudio 式的)自定义对话框
限制
- 通常,使用带有按钮文本的工具栏和不带按钮文本的工具栏是不可取的。要么所有工具栏都有带文本的按钮,要么都不带。否则,当用户将按钮从一个没有文本的工具栏移动到一个有文本的工具栏(反之亦然)时,会感到困惑。请参阅示例了解会发生什么。
新方法
// determine wether this toolbar supports buttons with text BOOL HasButtonText() const; // invalidate the button with the given index (force a repaint // of this button) void InvalidateButton(int nIndex); // Check wether the button with index <idx> is a "real" separator. // Thus it is *not* a control. BOOL IsSeparator(int idx) const; // Check wether the button with the given index is a control in // real life BOOL; IsControl(int idx) >const; // Get the control that is associated with the given index // The CWnd * returned may be temporary (if you don't use // MFC to add a control to the toolbar) // Set <IdxIsID> to TRUE, if <idx> represents the ID of the control CWnd * GetControl(int idx, BOOL IdxIsID = FALSE) const; // Retrieve the bitmap associated with the button with ID <ID>. // The background of the bitmap is the current setting for // COLOR_BTNFACE. // Don't forget to destroy the bitmap, if it is not longer needed! HBITMAP GetBitmap(int ID); // Replace the button with ID <id> with a custom control // of type <pClass>. The custom control gets the ID <id>. // <rc> gives width & height of the control. // The stylebits WS_CHILD and WS_VISIBLE will be added automatically. // to <dwStyle>. // The control shall support the DECLARE_DYNCREATE! (except // CComboBox and CEdit which are handled separatly) // You must not "delete" the return-value ! CWnd * CtrlReplace( CRuntimeClass * pClass, CRect &rc, UINT id, DWORD dwStyle = 0 ); // Insert a custom control before the button with index <before>. // if <before> == -1, then the control is appended behind the last // For more information see CtrlReplace() above. CWnd * CtrlInsert( CRuntimeClass * pClass, CRect &rc, UINT id, int before = 0, DWORD dwStyle = 0 ); // call "RepositionControls()"; if you have added one or more controls // and want to add more buttons/controls. // This function will automatically be called for controls when calling // CtrlReplace() and CtrlInsert(). void RepositionControls(); // Recalculate the size of the toolbar and recalculate the // layout of its parent. // There should be no need to call this method from outside // the toolbar. void RecalcLayout();
没有必要使用 CtrlInsert()
或 CtrlReplace()
来添加控件。您也可以用更传统的方式进行,工具栏仍然可以正常工作(即,它负责所有子控件,无论插入方式如何)。因此,您无需更改现有代码。如果您以首选方式以外的其他方式添加更多控件,则应自行调用 RepositionControls()
。
您可以通过以下方式添加组合框(假设您的工具栏资源包含一个 ID 为 IDC_COMBOBOX
的按钮,该按钮将被控件替换)
// ... // replace a button by a CComboBox-control on the toolbar DWORD dwComboStyle = WS_VSCROLL|CBS_AUTOHSCROLL|CBS_DROPDOWN| CBS_HASSTRINGS; CComboBox * pBox = (CComboBox*) m_wndToolBar.CtrlReplace( RUNTIME_CLASS(CComboBox), // control type IDC_COMBOBOX, // id of the button dwComboStyle // style bits ); if ( pBox ) { pBox->AddString(TEXT("Line 1")); pBox->AddString(TEXT("Line 2")); pBox->AddString(TEXT("Line 3")); } // ...
正如您所见,添加控件是一项简单的任务。
如果您计划使用其他类型的控件(非 CComboBox
或 CEdit
),您必须派生一个该类型的类,并实现 DECLARE_DYNCREATE
宏,并重载以下方法:
virtual BOOL Create( LPCTSTR lpszClassName, LPCTSTR lpszWindowName, DWORD dwStyle, const RECT &rect, CWnd* pParentWnd, UINT nID, CCreateContext* pContext = NULL);和
virtual void PostNcDestroy();
假设您计划在工具栏中插入一个静态文本
- 使用类向导创建一个派生自 CStatic 的新类。
- 使用类向导或类视图重载上面描述的 2 个虚方法。
- 结果应该看起来像这样(假设您已将类命名为
"CText"
)///////////////////////////////////////////////////////// // CText window class CText : public CStatic { DECLARE_DYNCREATE(CText) // <<= YOU HAVE TO INSERT THIS // Construction public: CText(); // Attributes public: // Operations public: // Overrides // ClassWizard generated virtual function overrides //{{AFX_VIRTUAL(CText) public: virtual BOOL Create(LPCTSTR lpszClassName, LPCTSTR lpszWindowName, DWORD dwStyle, const RECT &rect, CWnd* pParentWnd, UINT nID, CCreateContext* pContext = NULL); protected: virtual void PostNcDestroy(); //}}AFX_VIRTUAL // Implementation public: virtual ~CText(); // Generated message map functions protected: //{{AFX_MSG(CText) //}}AFX_MSG DECLARE_MESSAGE_MAP() };
在实现文件中,您必须填写 2 个重载的方法
IMPLEMENT_DYNCREATE(CText, CStatic); // <<== DO NOT FORGET THIS BOOL CText::Create(LPCTSTR lpszClassName, LPCTSTR lpszWindowName, DWORD dwStyle, const RECT &rect, CWnd* pParentWnd, UINT nID, CCreateContext* pContext = NULL) { return CWnd::Create(TEXT("STATIC"), TEXT("text control"), dwStyle|SS_LEFT, rect, pParentWnd, nID); } void CText::PostNcDestroy() { delete this; }
您只需要做这些。其他类型的控件的处理方式类似。
现在您可以如下插入这样的控件:
int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct) { // ... m_wndToolBar.CtrlInsert( RUNTIME_CLASS(CText), // the control's runtime class CRect(-100, -22, 0, 0), // width is 100, height is 22 IDC_TEXT, // control's ID 2 // insert before button with index 2 ); // ... }
修订 2 的更改
非常感谢 Wolfgang Loch、John Armstrong 和 Anatoly Ivasyuk,他们帮助我发布了这个版本。
- 被选中且禁用的按钮现在看起来正常了
- 如果工具栏不可停靠,则不绘制抓手
- 在“经典”模式下,不为抓手调整间距
- 为工具栏本身提供真正的 3D 外观(如 Office 或 DevStudio 中所示)(参见下图)
- 一些小的代码改进
旧式工具栏的 3D 外观。
新式(如 Office 或 DevStudio 中所示)。
要启用新的 3D 样式,您必须在 CMainFrame
的 OnCreate()
方法中将对 EnableDocking()
的调用替换为对 FrameEnableDocking()
的调用。
// original code: int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct) // ... EnableDocking(CBRS_ALIGN_ANY); DockControlBar(&m_wndToolBar); // ... return 0; // changed code to get "real" 3D style: int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct) // ... FrameEnableDocking(this, CBRS_ALIGN_ANY); DockControlBar(&m_wndToolBar); // ... return 0; }
帮助!
由于我的商业项目优先级高于我的业余项目,我没有足够的时间在合理的时间内完成工具栏的持久化和自定义。我正在寻找想帮助我完成这项工作的人。我有一个相对更高级版本的类,它实现了这些问题的一些方面(给我发邮件以获取源代码并查看下图)。这两个问题比我预期的要复杂得多...
工具栏的上下文菜单
资源中的“工具栏”自定义对话框
正在运行的“工具栏”自定义对话框
- CToolBarEx.h
- CToolBarEx.cpp