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