For those of you, who have already read this article CodeGuru, the
major enhancements start here. I'm looking for
someone who has enough time to help me! .
Some time ago, I saw Roger Onslow's flat toolbar implementation. The fact, that I need
a special product (MSIE) (or even DLL -> comctl32.dll) was somewhat inconvenient
to me. So I started to develop my own version of a flat looking toolbar without such
requirements. The result is a class called CToolBarEx.
With CToolBarEx
one can toggle between flat- and "classic"-mode. The
appropriate look will be shown immediataly.
Don't wonder if some parts of the code seem to be well known to you. The drawing of
separators and the gripper was (more or less) stolen from Roger's toolbar (why should I do
all of the hard bits again ;-)
In flat-mode CToolBarEx
makes all of the drawings by itself; in classic-mode,
MFC does the work.
Since VC++ >= 4.2 provides custom-draw abilities, this feature will be emulated in
flat mode by a local helper class, so one can use this feature regardless of the current
mode (flat or classic). To get some further informations on owner-drawing, have a look at
the implementation file ToolBarEx.cpp. The MainFrm.cpp in the
sample application may provide even more
informations, if you're not familiar with owner-drawing on toolbars.
However, CToolBarEx
should be able to work with older versions of VC++ too...
CToolBarEx
consists of two files:
- ToolBarEx.h
- ToolBarEx.cpp
To use CToolBarEx
in an MFC application, you have to perform the following
steps (I assume you use App-/Class-Wizard):
#include "ToolBarEx.h"
in either StdAfx.h or
MainFrm.h
- Change the type of
CMainFrame::m_wndToolBar
from
CToolBar
to CToolBarEx
The CToolBarEx
class provides the following public methods (in addition to its
ancestor-classes):
void SetFlatLook( BOOL bFlat = TRUE );
BOOL IsFlatLook() const;
void GetSizes( CSize &sizeButton, CSize &sizeImage ) const;
CWnd * GetParentFrame() const;
Update: The code has now been enhanced.
- texts on buttons now work
- gripper improved for a closer look like Office97
- disabled images now look embossed
- (thanks to Victor Vogelpoel)
(Zafir: Also to Oscar who sent in the same fix)
- a separator is drawn only if it has no WRAP state set
Major Enhanced Version
First note that this enhanced version of
CToolBarEx
currently does not
run properly on NT < 4.0, so if you plan to use the
CToolBarEx
on
such a system, please use the standard version above!
Many thanks to Jonathan Chin,
Victor Vogelpoel and
Patrick Liechty (the three guinea-pigs :-) for
their help in testing and fixing this version of the CToolBarEx class !
The enhanced version covers two major features:
- The ALT-drag feature is now implemented. That means that one can drag'n'drop buttons and
controls from one toolbar to another by pressing the ALT-key and the left mouse button
simultanously.
CToolBarEx
uses the same technique to allow customization as
CToolBarCtrl
do, so if you're not familiar with adjustable toolbars, please
refer to the online help and/or have a look at the
enhanced sample.
- Easy, generalized way to add custom controls to a toolbar. The
CToolBarEx
class allows you to add/replace/reposition controls onto the toolbar and takes care of
these controls.
This (zoomed) image gives you a feeling of the new features.
Things that have to be done later:
- It is still impossible to save/restore a customized toolbar. The methods from the
underlaying
CToolBarCtrl
class are not usable, if one uses custom
controls and/or moved buttons/controls from one bar to another ...
- There is still no (Office97- or DevStudio-like) customization dialog
Limitations:
- It is generally a bad idea to use toolbars one with texts on its buttons and another
without. Either all bars have buttons with text or no bar has it. Otherwise you will get
the user confused, if he/she moves a button from a bar without text to one with text or
vice versa. See the sample for what happens.
New methods:
BOOL HasButtonText() const;
void InvalidateButton(int nIndex);
BOOL IsSeparator(int idx) const;
BOOL; IsControl(int idx) >const;
CWnd * GetControl(int idx, BOOL IdxIsID = FALSE) const;
HBITMAP GetBitmap(int ID);
CWnd * CtrlReplace(
CRuntimeClass * pClass,
CRect &rc,
UINT id,
DWORD dwStyle = 0
);
CWnd * CtrlInsert(
CRuntimeClass * pClass,
CRect &rc,
UINT id,
int before = 0,
DWORD dwStyle = 0
);
void RepositionControls();
void RecalcLayout();
There is no must to add controls with either CtrlInsert()
or
CtrlReplace()
. You can do it in a more traditional fashion too and the
toolbar still works (i.e. it takes care of all its children, regardless of the way of
insertion). So there is no need to change your existing code. You should call
RepositionControls()
by yourself, if you add more controls in another
way than the prefered one.
You might add a combo-box in the following way (assuming your toolbar resource includes
a button with the id IDC_COMBOBOX
, that is to be replaced by the control):
DWORD dwComboStyle = WS_VSCROLL|CBS_AUTOHSCROLL|CBS_DROPDOWN|
CBS_HASSTRINGS;
CComboBox * pBox = (CComboBox*) m_wndToolBar.CtrlReplace(
RUNTIME_CLASS(CComboBox),
IDC_COMBOBOX,
dwComboStyle
);
if ( pBox ) {
pBox->AddString(TEXT("Line 1"));
pBox->AddString(TEXT("Line 2"));
pBox->AddString(TEXT("Line 3"));
}
As you can see, adding a control is an easy task.
If you plan to use other types of controls (other than CComboBox
or
CEdit
), you have to derive a class from this type and to implement the
DECLARE_DYNCREATE
macro and to overload the following methods:
virtual BOOL Create( LPCTSTR lpszClassName,
LPCTSTR lpszWindowName, DWORD dwStyle,
const RECT &rect, CWnd* pParentWnd,
UINT nID, CCreateContext* pContext = NULL);
and
virtual void PostNcDestroy();
Lets say you plan to insert a static text into the toolbar:
- Create a new class using class-wizard to create a class derived from CStatic.
- Use class-wizard or class-view to overload the 2 virtual methods described above.
- The result should look like this (assuming you have named the class
"CText"
):
class CText : public CStatic
{
DECLARE_DYNCREATE(CText)
public:
CText();
public:
public:
public:
virtual BOOL Create(LPCTSTR lpszClassName,
LPCTSTR lpszWindowName,
DWORD dwStyle, const RECT &rect,
CWnd* pParentWnd, UINT nID,
CCreateContext* pContext = NULL);
protected:
virtual void PostNcDestroy();
public:
virtual ~CText();
protected:
DECLARE_MESSAGE_MAP()
};
In the implementation file you have to fill out the 2 overloaded methods:
IMPLEMENT_DYNCREATE(CText, CStatic);
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;
}
That's all you have to do. The proceeding with other types of controls is similar.
Now you can insert such a control like follows:
int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
m_wndToolBar.CtrlInsert(
RUNTIME_CLASS(CText),
CRect(-100, -22, 0, 0),
IDC_TEXT,
2
);
}
Changes in Revision 2
Many thanks to
Wolfgang Loch,
John Armstrong and
Anatoly Ivasyuk who helped me to
release that version.
- buttons that are checked and disabled are now looking ok
- don't draw a gripper if the bar is not dockable
- do not adjust space for gripper in "classic" mode
- give the bar itself a real 3D look (as in Office or DevStudio) (see images below)
- some minor code improvements
Old style 3D look of a toolbar.
New style (as in Office or DevStudio).
To enable the new 3D style you have to exchange the call to EnableDocking()
in your CMainFrame
's OnCreate()
method with a call to
FrameEnableDocking()
:
int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
EnableDocking(CBRS_ALIGN_ANY);
DockControlBar(&m_wndToolBar);
return 0;
int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
FrameEnableDocking(this, CBRS_ALIGN_ANY);
DockControlBar(&m_wndToolBar);
return 0;
}
Help!
Since my commercial projects have a higher priority than my hobby projects, I don't
have enough time to complete the persistence and customization of the toolbar in a
reasonable time. I'm looking for someone who wants to help me complete this. I have a
somewhat more advanced version of the class that implements some aspects of these issues
(send me mail to get the source and see the images below). Both these problems became more
complex than I expected ...
context menu of the toolbar
"Toolbar" customization dialog in the resource
"Toolbar" customization dialog in action
CToolBarEx
still consists of two files:
- CToolBarEx.h
- CToolBarEx.cpp
Download enhanced source - 25K Download enhanced sample - 56K