Figure 3    CFlatToolBar

FlatBar.h

////////////////////////////////////////////////////////////////
// CFlatToolBar 1997 Microsoft Systems Journal. 
// If this code works, it was written by Paul DiLascia.
// If not, I don't know who wrote it.
// This code compiles with Visual C++ 5.0 on Windows 95
//
#ifndef TBSTYLE_FLAT
#define TBSTYLE_FLAT 0x0800   // (in case you don't have the new commctrl.h)
#endif

//////////////////
// "Flat" style tool bar. Use instead of CToolBar in your CMainFrame
// or other window to create a tool bar with the flat look.
//
// CFlatToolBar fixes the display bug described in the article. It also has
// overridden load functions that modify the style to TBSTYLE_FLAT. If you
// don't create your toolbar by loading it from a resource, you should call
// ModifyStyle(0, TBSTYLE_FLAT) yourself.
//
class CFlatToolBar : public CToolBar {
public:
   BOOL LoadToolBar(LPCTSTR lpszResourceName);
   BOOL LoadToolBar(UINT nIDResource)
      { return LoadToolBar(MAKEINTRESOURCE(nIDResource)); }
protected:
   DECLARE_DYNAMIC(CFlatToolBar)
   DECLARE_MESSAGE_MAP()
   afx_msg void OnWindowPosChanging(LPWINDOWPOS lpWndPos);
};

FlatBar.cpp
////////////////////////////////////////////////////////////////
// CFlatToolBar 1997 Microsoft Systems Journal. 
// If this code works, it was written by Paul DiLascia.
// If not, I don't know who wrote it.
//
#include "StdAfx.h"
#include "FlatBar.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

////////////////////////////////////////////////////////////////
// CFlatToolBar--does flat tool bar in MFC.
//
IMPLEMENT_DYNAMIC(CFlatToolBar, CToolBar)

BEGIN_MESSAGE_MAP(CFlatToolBar, CToolBar)
   ON_WM_WINDOWPOSCHANGING()
END_MESSAGE_MAP()

////////////////
// Load override modifies the style after loading toolbar.
//
BOOL CFlatToolBar::LoadToolBar(LPCTSTR lpszResourceName)
{
   if (!CToolBar::LoadToolBar(lpszResourceName))
      return FALSE;
   ModifyStyle(0, TBSTYLE_FLAT); // make it flat
   return TRUE;
}

//////////////////
// MFC doesn't handle moving a TBSTYLE_FLAT toolbar correctly.
// The simplest way to fix it is to repaint the old rectangle and
// toolbar itself whenever the toolbar moves.
// 
void CFlatToolBar::OnWindowPosChanging(LPWINDOWPOS lpwp)
{
   CToolBar::OnWindowPosChanging(lpwp);

//#define ILLUSTRATE_DISPLAY_BUG        //  remove comment to see the bug
#ifndef ILLUSTRATE_DISPLAY_BUG

   if (!(lpwp->flags & SWP_NOMOVE)) {   //  if moved:
      CRect rc;                         //  Fill rectangle with..
      GetWindowRect(&rc;);               //  ..my (toolbar) rectangle.
      CWnd* pParent = GetParent();      //  get parent (dock bar/frame) win..
      pParent->ScreenToClient(&rc;);     //  .. and convert to parent coords

      // Ask parent window to paint the area beneath my old location.
      // Typically, this is just solid grey.
      //
      pParent->InvalidateRect(&rc;);     //  paint old rectangle

      // Now paint my non-client area at the new location.
      // This is the extra bit of border space surrounding the buttons.
      // Without this, you will still have a partial display bug (try it!)
      //
      PostMessage(WM_NCPAINT);
   }
#endif
}

Figure 6   Classes in CoolBar.h and CoolBar.cpp

CCoolBar
An MFC wrapper for ReBarWindow32. It has wrapper functions like GetBarInfo and GetBandCount to wrap coolbar messages like RB_GETBANDINFO and RB_GETBANDCOUNT. You must derive from this and override OnCreateBands to add bands to your coolbar. It contains code to work around MFC display bugs.
CRebarInfo
A C++ version of REBARINFO. Constructor initializes itself to zero and sets cbSize properly.
CRebarBandInfo
A C++ version of REBARBANDINFO. Constructor initializes itself to zero and sets cbSize properly.
CCoolToolBar
A specialization of CToolBar you should use inside a rebar/coolbar. Overrides CToolBar functions to work around MFC display bugs.


Figure 7   How to Use CcoolBar

MainFrm.h


 ////////////////////////////////////////////////////////////////
 // 1997 Microsoft Systems Journal. 
 // If this code works, it was written by Paul DiLascia.
 // If not, I don't know who wrote it. See MainFrm.cpp
 //
 #include "CoolBar.h"
 
 ////////////////
 // Special combo box handles drop down event
 //
 class CMyComboBox : public CComboBox {
 protected:
     DECLARE_DYNAMIC(CMyComboBox)
     DECLARE_MESSAGE_MAP()
     afx_msg void OnDropDown();
 };
 
 /////////////////
 // My Cool bar: specialized CCoolBar creates bands.
 //
 class CMyCoolBar : public CCoolBar {
 protected:
     DECLARE_DYNAMIC(CMyCoolBar)
     CCoolToolBar    m_wndToolBar;           // toolbar
     CMyComboBox     m_wndCombo;             // combo box
     CBitmap         m_bmBackground;         // background bitmap
     virtual BOOL    OnCreateBands();
 };
 
 /////////////////
 // Main frame window has cool bar and status bar
 //
 class CMainFrame : public CFrameWnd {
 protected:
     DECLARE_DYNCREATE(CMainFrame)
     CStatusBar  m_wndStatusBar;
     CMyCoolBar  m_wndCoolBar;                 // here's the coolbar
     virtual BOOL PreCreateWindow(CREATESTRUCT& cs);
 
     DECLARE_MESSAGE_MAP()
     afx_msg int  OnCreate(LPCREATESTRUCT lpCreateStruct);
     afx_msg void OnUpdateFileOpen(CCmdUI* pCmdUI);
 };
MainFrm.cpp

 ////////////////////////////////////////////////////////////////
 // COOLBAR 1997 Microsoft Systems Journal. 
 // If this program works, it was written by Paul DiLascia.
 // If not, I don't know who wrote it.
 // Shows how to use my CCoolBar class to implement a coolbar in MFC.
 // Compiles with Visual C++ 5.0 on Windows 95
 
 #include "StdAfx.h"
 #include "MainFrm.h"
 #include "resource.h"
 
 #ifdef _DEBUG
 #define new DEBUG_NEW
 #undef THIS_FILE
 static char THIS_FILE[] = __FILE__;
 #endif
 
 ////////////////////////////////////////////////////////////////
 // CMainFrame
 //
 IMPLEMENT_DYNCREATE(CMainFrame, CFrameWnd)
 
 BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)
    ON_WM_CREATE()
 END_MESSAGE_MAP()
 
 static UINT indicators[] = {
    ID_SEPARATOR,           // status line indicator
    ID_INDICATOR_CAPS,
    ID_INDICATOR_NUM,
    ID_INDICATOR_SCRL,
 };
 
 //////////////////
 // Create handler creates control bars
 //
 int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
 {
    if (CFrameWnd::OnCreate(lpCreateStruct) == -1)
       return -1;
    
    // Create cool bar
    if (!m_wndCoolBar.Create(this, WS_CHILD|WS_VISIBLE|WS_BORDER|
                             WS_CLIPSIBLINGS|WS_CLIPCHILDREN|
                             RBS_TOOLTIPS|RBS_BANDBORDERS|RBS_VARHEIGHT)) {
       TRACE0("Failed to create cool bar\n");
       return -1;      // fail to create
    }
 
    // Create status bar
    if (!m_wndStatusBar.Create(this) ||
       !m_wndStatusBar.SetIndicators(indicators,
                                     sizeof(indicators)/sizeof(UINT))) {
       TRACE0("Failed to create status bar\n");
       return -1;      // fail to create
    }
 
    return 0;
 }
 
 ////////////////
 // Override for flicker-free drawing with no CS_VREDRAW and CS_HREDRAW.
 // This has nothing to do with coolbars, but I threw it in because it's
 // a good thing to do.
 //
 BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs)
 {
    cs.lpszClass = AfxRegisterWndClass(
       CS_DBLCLKS,                       // if you need double-clicks
       NULL,                             // no cursor (use default)
       NULL,                             // no background brush
       AfxGetApp()->LoadIcon(IDR_MAINFRAME)); // app icon
    ASSERT(cs.lpszClass);
    return CFrameWnd::PreCreateWindow(cs);
 }
 
 ////////////////////////////////////////////////////////////////
 // CMyCoolBar
 //
 IMPLEMENT_DYNAMIC(CMyCoolBar, CCoolBar)
 
 const CSize COMBO_MINSIZE(150,25);
 
 ////////////////
 // This is the virtual function you have to override to add bands
 //
 BOOL CMyCoolBar::OnCreateBands()
 {
    // Create tool bar
    CCoolToolBar& tb = m_wndToolBar;
    if (!tb.Create(this,
                   WS_CHILD|WS_VISIBLE|WS_CLIPSIBLINGS|WS_CLIPCHILDREN|
                   CBRS_TOOLTIPS|CBRS_SIZE_DYNAMIC) ||
        !tb.LoadToolBar(IDR_MAINFRAME)) {
       TRACE0("Failed to create toolbar\n");
       return FALSE; // failed to create
    }
    tb.ModifyStyle(0, TBSTYLE_FLAT);
 
    // Create combo box
    CRect rc(0,0,0,0);
    m_wndCombo.Create(WS_VISIBLE|WS_CHILD|WS_VSCROLL|CBS_DROPDOWNLIST|
                      WS_CLIPCHILDREN|WS_CLIPSIBLINGS, rc, this, 1001);
 
 // Following is not needed since I'm not using an image list
 // CRebarInfo rbi;
 // ... set stuff in rbi...
 // SetBarInfo(&rbi);
 
    // Get minimum size of bands
    CSize szHorz = tb.CalcDynamicLayout(-1, 0);        // get min horz size
    CSize szVert = tb.CalcDynamicLayout(-1, LM_HORZ);  // get min vert size
    VERIFY(m_bmBackground.LoadBitmap(IDB_BITMAP1));    // load background bmp
 
    // Band 1: Add toolbar band
    CRebarBandInfo rbbi;
    rbbi.fMask = RBBIM_STYLE|RBBIM_CHILD|RBBIM_CHILDSIZE|
                 RBBIM_BACKGROUND|RBBIM_COLORS;
    rbbi.fStyle = RBBS_FIXEDBMP;
    rbbi.hwndChild = m_wndToolBar;
    rbbi.cxMinChild = szHorz.cx;
    rbbi.cyMinChild = szVert.cy;
    rbbi.hbmBack = m_bmBackground;
    rbbi.clrFore = GetSysColor(COLOR_BTNTEXT);
    rbbi.clrBack = GetSysColor(COLOR_BTNFACE);
    if (!InsertBand(-1, &rbbi))
       return FALSE;
 
    // Band 2: Add combo box band. Most settings in rbbi same from tool bar
    rbbi.fMask |= RBBIM_TEXT;
    rbbi.lpText = _T("Address:");
    rbbi.cxMinChild = COMBO_MINSIZE.cx;
    rbbi.cyMinChild = COMBO_MINSIZE.cy;
    rbbi.hwndChild = m_wndCombo;
    if (!InsertBand(-1, &rbbi))
       return FALSE;
 
    return 0; // OK
 }
 
 ////////////////////////////////////////////////////////////////
 // CMyComboBox
 //
 IMPLEMENT_DYNAMIC(CMyComboBox, CComboBox)
 
 BEGIN_MESSAGE_MAP(CMyComboBox, CComboBox)
    ON_CONTROL_REFLECT(CBN_DROPDOWN, OnDropDown)
 END_MESSAGE_MAP()
 
 //////////////////
 // Note that I have to resize the window when I get CBN_DROPDOWN
 //    
 void CMyComboBox::OnDropDown()
 {
    CRect rc;
    GetWindowRect(&rc);
    SetWindowPos(NULL,0,0,rc.Width(),200,      // use same width but taller height
                 SWP_NOMOVE|SWP_NOACTIVATE);
    ResetContent();
    for (int i=1; i<=20; i++) {
       CString s;
       s.Format("http://www.msj%d.com", i);
       AddString(s);
    }
 }

Figure 8   CcoolBar

CoolBar.h


 ////////////////////////////////////////////////////////////////
 // CCoolBar 1997 Microsoft Systems Journal. 
 // If this program works, it was written by Paul DiLascia.
 // If not, I don't know who wrote it.
 // Compiles with Visual C++ 5.0 on Windows 95
 
 //////////////////
 // CCoolBar encapsulates IE 4.0 common coolbar for MFC.
 //
 class CCoolBar : public CControlBar {
 protected:
    DECLARE_DYNAMIC(CCoolBar)
 
 public:
    CCoolBar();
    virtual ~CCoolBar();
 
    BOOL Create(CWnd* pParentWnd, DWORD dwStyle,
       DWORD dwAfxBarStyle = CBRS_ALIGN_TOP,
       UINT nID = AFX_IDW_TOOLBAR);
 
    // Message wrappers
    BOOL GetBarInfo(LPREBARINFO lp)
       { ASSERT(::IsWindow(m_hWnd));
         return (BOOL)SendMessage(RB_GETBARINFO, 0, (LPARAM)lp); }
    BOOL SetBarInfo(LPREBARINFO lp)
       { ASSERT(::IsWindow(m_hWnd));
         return (BOOL)SendMessage(RB_SETBARINFO, 0, (LPARAM)lp); }
    BOOL GetBandInfo(int iBand, LPREBARBANDINFO lp)
       { ASSERT(::IsWindow(m_hWnd));
         return (BOOL)SendMessage(RB_GETBANDINFO, iBand, (LPARAM)lp); }
    BOOL SetBandInfo(int iBand, LPREBARBANDINFO lp)
       { ASSERT(::IsWindow(m_hWnd));
         return (BOOL)SendMessage(RB_SETBANDINFO, iBand, (LPARAM)lp); }
    BOOL InsertBand(int iWhere, LPREBARBANDINFO lp)
       { ASSERT(::IsWindow(m_hWnd));
         return (BOOL)SendMessage(RB_INSERTBAND, (WPARAM)iWhere, (LPARAM)lp); }
    BOOL DeleteBand(int nWhich)
       { ASSERT(::IsWindow(m_hWnd));
         return (BOOL)SendMessage(RB_INSERTBAND, (WPARAM)nWhich); }
    int GetBandCount()
       { ASSERT(::IsWindow(m_hWnd));
         return (int)SendMessage(RB_GETBANDCOUNT); }
    int GetRowCount()
       { ASSERT(::IsWindow(m_hWnd));
         return (int)SendMessage(RB_GETROWCOUNT); }
    int GetRowHeight(int nWhich)
       { ASSERT(::IsWindow(m_hWnd));
         return (int)SendMessage(RB_GETROWHEIGHT, (WPARAM)nWhich); }
 
 protected:
    // new virtual functions you must/can override
    virtual BOOL OnCreateBands() = 0; // return -1 if failed
    virtual void OnHeightChange(const CRect& rcNew);
 
    // CControlBar Overrides
    virtual CSize CalcFixedLayout(BOOL bStretch, BOOL bHorz);
    virtual CSize CalcDynamicLayout(int nLength, DWORD nMode);
    virtual void OnUpdateCmdUI(CFrameWnd* pTarget, BOOL bDisableIfNoHndler);
 
    // message handlers
    DECLARE_MESSAGE_MAP()
    afx_msg int  OnCreate(LPCREATESTRUCT lpcs);
    afx_msg void OnPaint();
    afx_msg void OnHeigtChange(NMHDR* pNMHDR, LRESULT* pRes);
    afx_msg BOOL OnEraseBkgnd(CDC* pDC);
 };
 
 //////////////////
 // Specialized CToolBar fixes display problems in MFC.
 //
 class CCoolToolBar : public CToolBar {
 public:
    CCoolToolBar();
    virtual ~CCoolToolBar();
 protected:
    DECLARE_DYNAMIC(CCoolToolBar)
    DECLARE_MESSAGE_MAP()
    afx_msg void OnNcPaint();
    afx_msg void OnPaint();
    afx_msg void OnNcCalcSize(BOOL, NCCALCSIZE_PARAMS*);
 };
 
 //////////////////
 // Programmer-friendly REBARINFO initializes itself
 //
 class CRebarInfo : public REBARINFO {
 public:
    CRebarInfo() {
       memset(this, 0, sizeof(REBARINFO));
       cbSize = sizeof(REBARINFO);
    }
 };
 
 //////////////////
 // Programmer-friendly REBARBANDINFO initializes itself
 //
 class CRebarBandInfo : public REBARBANDINFO {
 public:
    CRebarBandInfo() {
       memset(this, 0, sizeof(REBARBANDINFO));
       cbSize = sizeof(REBARBANDINFO);
    }
 };
CoolBar.cpp

 ////////////////////////////////////////////////////////////////
 // CCoolBar 1997 Microsoft Systems Journal. 
 // If this program works, it was written by Paul DiLascia.
 // If not, I don't know who wrote it.
 // CCoolBar implements coolbars for MFC.
 //
 #include "StdAfx.h"
 #include "CoolBar.h"
 
 #ifdef _DEBUG
 #define new DEBUG_NEW
 #undef THIS_FILE
 static char THIS_FILE[] = __FILE__;
 #endif
 
 IMPLEMENT_DYNAMIC(CCoolBar, CControlBar)
 
 BEGIN_MESSAGE_MAP(CCoolBar, CControlBar)
    //{{AFX_MSG_MAP(CCoolBar)
    ON_WM_CREATE()
    ON_WM_PAINT()
    ON_WM_ERASEBKGND()
    ON_NOTIFY_REFLECT(RBN_HEIGHTCHANGE, OnHeigtChange)
    //}}AFX_MSG_MAP
 END_MESSAGE_MAP()
 
 CCoolBar::CCoolBar()
 {
 }
 
 CCoolBar::~CCoolBar()
 {
 }
 
 //////////////////
 // Create coolbar
 //
 BOOL CCoolBar::Create(CWnd* pParentWnd, DWORD dwStyle,
                       DWORD dwAfxBarStyle, UINT nID)
 {
    ASSERT_VALID(pParentWnd);   // must have a parent
 
    // dynamic coolbar not supported
    dwStyle &= ~CBRS_SIZE_DYNAMIC;
 
    // save the style (this code copied from MFC--probably unnecessary)
    m_dwStyle = dwAfxBarStyle;
    if (nID == AFX_IDW_TOOLBAR)
       m_dwStyle |= CBRS_HIDE_INPLACE;
 
    // MFC requires these:
    dwStyle |= CCS_NODIVIDER|CCS_NOPARENTALIGN;
 
    // Initialize cool common controls
    static BOOL bInit = FALSE;
    if (!bInit) {
       INITCOMMONCONTROLSEX sex;
       sex.dwSize = sizeof(INITCOMMONCONTROLSEX);
       sex.dwICC = ICC_COOL_CLASSES;
       InitCommonControlsEx(&sex);
       bInit = TRUE;
    }
 
    // Create the cool bar using style and parent.
    CRect rc;
    rc.SetRectEmpty();
    return CWnd::CreateEx(WS_EX_TOOLWINDOW, REBARCLASSNAME, NULL,
                          dwStyle, rc, pParentWnd, nID);
 }
 
 //////////////////
 // Handle WM_CREATE. Call virtual fn so derived class can create bands.
 //
 int CCoolBar::OnCreate(LPCREATESTRUCT lpcs)
 {
    return CControlBar::OnCreate(lpcs) == -1 ? -1
       : OnCreateBands();   // call pure virtual fn to create bands
 }
 
 //////////////////
 // Standard UI handler updates any controls in the coolbar.
 //
 void CCoolBar::OnUpdateCmdUI(CFrameWnd* pTarget, BOOL bDisableIfNoHndler)
 {
    UpdateDialogControls(pTarget, bDisableIfNoHndler);
 }
 
 /////////////////
 // These two functions are called by MFC to calculate the layout of
 // the main frame. Since CCoolBar is not designed to be dynamic, the
 // size is always fixed, and the same as the window size. 
 //
 CSize CCoolBar::CalcDynamicLayout(int nLength, DWORD dwMode)
 {
    return CalcFixedLayout(dwMode & LM_STRETCH, dwMode & LM_HORZ);
 }
 
 CSize CCoolBar::CalcFixedLayout(BOOL bStretch, BOOL bHorz)
 {
    CRect rc;
    GetWindowRect(&rc);
    CSize sz(bHorz && bStretch ? 0x7FFF : rc.Width(),
             !bHorz && bStretch ? 0x7FFF : rc.Height());
    return sz;
 }
 
 //////////////////
 // Low-level height-changed handler just passes to virtual fn w/nicer args.
 //
 void CCoolBar::OnHeigtChange(NMHDR* pNMHDR, LRESULT* pRes)
 {
    CRect rc;
    GetWindowRect(&rc);
    OnHeightChange(rc);
    *pRes = 0; // why not?
 }
 
 //////////////////
 // Height changed:
 // Notify the parent frame by posting a WM_SIZE message. This will cause the
 // frame to do RecalcLayout. The message must be posted, not sent, because
 // the coolbar could send RBN_HEIGHTCHANGE while the user is sizing, which
 // would be in the middle of a CFrame::RecalcLayout, and RecalcLayout doesn't
 // let you re-enter it. Posting guarantees that CFrameWnd can finish any recalc
 // it may be in the middle of before handling my posted WM_SIZE. Very confusing.
 //
 void CCoolBar::OnHeightChange(const CRect& rcNew)
 {
    CWnd* pParent = GetParent();
    CRect rc;
    pParent->GetWindowRect(&rc);
    pParent->PostMessage(WM_SIZE, 0, MAKELONG(rc.Width(),rc.Height()));
 }
 
 void CCoolBar::OnPaint()
 {
    Default();  // bypass CControlBar
 }
 
 BOOL CCoolBar::OnEraseBkgnd(CDC* pDC)
 {
    return (BOOL)Default();  // bypass CControlBar
 }
 
 ////////////////////////////////////////////////////////////////
 // Special tool bar to use in cool bars.
 // Mainly, it overides yukky stuff in CToolBar.
 //
 IMPLEMENT_DYNAMIC(CCoolToolBar, CToolBar)
 
 BEGIN_MESSAGE_MAP(CCoolToolBar, CToolBar)
    ON_WM_NCPAINT()
    ON_WM_PAINT()
    ON_WM_NCCALCSIZE()
 END_MESSAGE_MAP()
 
 CCoolToolBar::CCoolToolBar()
 {
 }
 
 CCoolToolBar::~CCoolToolBar()
 {
 }
 
 void CCoolToolBar::OnNcPaint()
 {
    Default();  // bypass CToolBar/CControlBar
 }
 
 void CCoolToolBar::OnPaint()
 {
    Default();  // bypass CToolBar/CControlBar
 }
 
 void CCoolToolBar::OnNcCalcSize(BOOL, NCCALCSIZE_PARAMS*)
 {
    Default();  // bypass CToolBar/CControlBar
 }