65.9K
CodeProject 正在变化。 阅读更多。
Home

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

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.38/5 (7投票s)

2000 年 2 月 25 日

viewsIcon

100225

downloadIcon

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)

  1. StdAfx.hMainFrm.h 中包含 "ToolBarEx.h"
  2. 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 ChinVictor VogelpoelPatrick Liechty(三只“小白鼠” :-) 帮助测试和修复此版本的 CToolBarEx 类!

增强版包含两大功能:

  1. ALT 拖动功能现已实现。这意味着可以通过同时按住 ALT 键和鼠标左键,将按钮和控件从一个工具栏拖放到另一个工具栏。CToolBarEx 使用相同的技术来实现可自定义性,就像 CToolBarCtrl 一样,因此如果您不熟悉可调整的工具栏,请参阅在线帮助和/或查看增强示例
  2. 一种简单、通用的方法,用于将自定义控件添加到工具栏。CToolBarEx 类允许您添加/替换/重新定位工具栏上的控件,并负责这些控件。

这张(放大)的图像让您对新功能有所感受。

稍后需要做的事情

  1. 仍然无法保存/恢复自定义的工具栏。当使用自定义控件和/或将按钮/控件从一个栏移动到另一个栏时,底层 CToolBarCtrl 类的方法将不可用...
  2. 仍然没有(Office97-或 DevStudio 式的)自定义对话框

限制

  1. 通常,使用带有按钮文本的工具栏和不带按钮文本的工具栏是不可取的。要么所有工具栏都有带文本的按钮,要么都不带。否则,当用户将按钮从一个没有文本的工具栏移动到一个有文本的工具栏(反之亦然)时,会感到困惑。请参阅示例了解会发生什么。

新方法

// 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"));
}
// ...

正如您所见,添加控件是一项简单的任务。

如果您计划使用其他类型的控件(非 CComboBoxCEdit),您必须派生一个该类型的类,并实现 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 LochJohn ArmstrongAnatoly Ivasyuk,他们帮助我发布了这个版本。

  • 被选中禁用的按钮现在看起来正常了
  • 如果工具栏不可停靠,则不绘制抓手
  • 在“经典”模式下,不为抓手调整间距
  • 为工具栏本身提供真正的 3D 外观(如 Office 或 DevStudio 中所示)(参见下图)
  • 一些小的代码改进


旧式工具栏的 3D 外观。


新式(如 Office 或 DevStudio 中所示)。

要启用新的 3D 样式,您必须在 CMainFrameOnCreate() 方法中将对 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;
}

帮助!

由于我的商业项目优先级高于我的业余项目,我没有足够的时间在合理的时间内完成工具栏的持久化和自定义。我正在寻找想帮助我完成这项工作的人。我有一个相对更高级版本的类,它实现了这些问题的一些方面(给我发邮件以获取源代码并查看下图)。这两个问题比我预期的要复杂得多...

Context Menu
工具栏的上下文菜单

Toolbar Dialog Resource
资源中的“工具栏”自定义对话框

Toolbar Dialog
正在运行的“工具栏”自定义对话框

CToolBarEx 仍然由两个文件组成

  • CToolBarEx.h
  • CToolBarEx.cpp

下载增强源代码 - 25K下载增强示例 - 56K

© . All rights reserved.