Introduction
The problem with current implementations of rollover toolbars is that they assume that you are
in charge of source code, which is not always true.
For example, if you are designing a container that is supposed to host some controls
then the source code of the toolbar implementation of those controls is not
accessible by the container. Another scenario would also include the case of Toolbars
that are dynamically created by reading images from external sources that
usually don't present all 3 possible icon states (normal, hot and disabled).
The approach presented here doesn't show you how to implement hooking or
subclassing to achieve rollover results on 3d party toolbars loaded into your
application, but with a little effort you can modify and reuse the following
approach.
I'm presenting this sample using VC5 grammar, because not all readers have switched
to VC6 or VS .NET and/or have the latest common control libraries.
Let's get started
OK, idea is simple: after standard creation of a ToolBar the following code
will query for current toolbar ImageList, create a Hot and Disabled List and
set them to that toolbar.
In your CMainFrame::OnCreate implementation add the following:
...
if (!m_wndToolBar.Create(this) ||
!m_wndToolBar.LoadToolBar(IDR_MAINFRAME))
{
TRACE0("Failed to create toolbar\n");
return -1; }
CHotToolbar(m_wndToolBar.GetToolBarCtrl());
...
Now, here is the CHotToolbar Class:
class CHotToolbar
{
public:
CHotToolbar(CToolBarCtrl& Bar,
// see comments about crHighlightColor below
COLORREF crHighlightNormal = RGB(0,0,0) ,
// see comments about crHighlightColor below
COLORREF crHighlightDisabled = RGB(160,160,160),
DWORD dwStyle = TBSTYLE_FLAT )
// CreateGrayScaleIcon -- will mix original Icon with "every-other-pixel"
// pattern brush
// crHighlightColor could be set to any color to give "special" effect
// BLACK(RGB(0,0,0)) will correcspond to extremely contrast,
// while WHITE(RGB(255,255,255)) will be less visible => default
// RGB(160,160,160) is good for Disabled Buttons
static HICON CreateGrayScaleIcon(HICON hIcon,
COLORREF crHighlightColor = RGB(160,160,160));
};
/////////////////////////////////////////////////////////////////////////////
// CHotToolbar
CHotToolbar::CHotToolbar(CToolBarCtrl& Bar,
COLORREF crHighlightNormal /* = RGB(0,0,0) */,
COLORREF crHighlightDisabled /* = RGB(160,160,160)*/,
DWORD dwStyle /* = TBSTYLE_FLAT */)
{
CImageList* pImageList = CImageList::FromHandle(
(HIMAGELIST)::SendMessage(Bar.m_hWnd, TB_GETIMAGELIST, 0, 0L));
if(pImageList)
{
int numButtons = pImageList->GetImageCount( );
if(numButtons > 0)
{
IMAGEINFO ImageInfo;
if(pImageList->GetImageInfo(0, &ImageInfo ))
{
// OK, let CImageList img, imgHot, imgDisabled;
CSize size(CRect(ImageInfo.rcImage).Width(),
CRect(ImageInfo.rcImage).Height());
if( img.Create(size.cx, size.cy, TRUE, 0, 1)&&
imgHot.Create(size.cx, size.cy, TRUE, 0, 1)&&
imgDisabled.Create(size.cx, size.cy, TRUE, 0, 1))
{
for(int i=0; i<numButtons; i++)
{
HICON hIcon = pImageList->ExtractIcon(i);
HICON hIconNormal = CreateGrayScaleIcon(hIcon, crHighlightNormal);
img.Add( hIconNormal );
DeleteObject( hIconNormal );
HICON hIconDisabled = CreateGrayScaleIcon(hIcon, crHighlightDisabled);
imgDisabled.Add( hIconDisabled );
DeleteObject( hIconDisabled );
imgHot.Add(hIcon);
DeleteObject( hIcon );
}
// Now Standard Images Grabed => Now we can SetImageList
// ATTN: after that operation pImageList will not be valid anymore
::SendMessage(Bar.m_hWnd, TB_SETHOTIMAGELIST, 0,
(LPARAM)imgHot.GetSafeHandle());
imgHot.Detach();
::SendMessage(Bar.m_hWnd, TB_SETIMAGELIST, 0,
(LPARAM)img.GetSafeHandle());
img.Detach();
::SendMessage(Bar.m_hWnd, TB_SETDISABLEDIMAGELIST, 0,
(LPARAM)imgDisabled.GetSafeHandle());
imgDisabled.Detach();
if(dwStyle)
Bar.ModifyStyle(0, TBSTYLE_FLAT);
}
}
}
}
}
//
// CreateGrayScaleIcon -- will mix original Icon with "every-other-pixel"
// pattern brush
// crHighlightColor could be set to any color to give "special" effect
// BLACK(RGB(0,0,0)) will correcspond to extremely contrast, while WHITE(RGB(255,255,255))
// will be less visible => default RGB(160,160,160) is good for Disabled Buttons
//
/* static */
HICON CHotToolbar::CreateGrayScaleIcon(HICON hIcon,
COLORREF crHighlightColor /*=RGB(160,160,160)*/)
{
// The bitmap bits are for a monochrome "every-other-pixel"
// bitmap (for a pattern brush)
static const WORD Bits[8] = { 0x0055, 0x00aa,
0x0055, 0x00aa,
0x0055, 0x00aa,
0x0055, 0x00aa };
ICONINFO iconinfo;
GetIconInfo(hIcon, &iconinfo);
BITMAP bm;
GetObject(iconinfo.hbmColor, sizeof(bm), &bm);
HDC hScreenDC = ::GetDC(NULL);
HDC hDC = ::CreateCompatibleDC(hScreenDC);
HBITMAP hOldColor = (HBITMAP)::SelectObject(hDC, iconinfo.hbmColor);
// The Width and Height of the Icon Image
int nWidth = bm.bmWidth + 1;
int nHeight = bm.bmHeight + 1;
// Create pattern bitmap
HBITMAP hBrushBitmap = CreateBitmap( 8, 8, 1, 1, &Bits );
// Original bitmap
HBITMAP hBitmap = CreateCompatibleBitmap( hScreenDC, nWidth, nHeight );
// Create memory DC to work on
HDC hMemDC = CreateCompatibleDC( hScreenDC );
// Create pattern brush
HBRUSH hBrush = CreatePatternBrush( hBrushBitmap );
// If somewthing is wrong => return with NULL
if( ( !hBrushBitmap ) ||
( !hBitmap ) ||
( !hMemDC ) ||
( !hBrush ) )
{
if( hBrushBitmap )
DeleteObject(hBrushBitmap);
if( hBitmap )
DeleteObject( hBitmap );
if( hMemDC )
DeleteDC( hMemDC );
if( hBrush )
DeleteObject( hBrush );
return NULL;
}
// Select bitmap into the memory DC
HBITMAP hOldMemBitmap = (HBITMAP)SelectObject( hMemDC, hBitmap );
// Original Rectangle:
RECT rcRect = { 0, 0, nWidth, nHeight};
// Lay down the pattern in the memory DC
FillRect( hMemDC, &rcRect, hBrush );
// Fill in the non-color pixels with the original image
BitBlt( hMemDC, 0, 0, nWidth, nHeight, hDC, 0, 0, SRCAND);
// Set the color scheme
COLORREF crOldTextColor = SetTextColor( hDC, crHighlightColor );
COLORREF crOldBkColor = SetBkColor( hDC, RGB(0,0,0) );
int OldBkMode = SetBkMode( hDC, OPAQUE );
// Select the pattern brush
HBRUSH hOldBrush = (HBRUSH)SelectObject( hDC, hBrush );
// Fill in the color pixels, and set the others to black
FillRect( hDC, &rcRect, hBrush );
// Fill in the black ones with the original image
BitBlt( hDC, 0, 0, nWidth, nHeight, hMemDC, 0, 0, SRCPAINT );
// Restore target DC settings
SetBkMode( hDC, OldBkMode );
SetBkColor( hDC, crOldBkColor );
SetTextColor( hDC, crOldTextColor );
// Clean up
SelectObject( hMemDC, hOldMemBitmap );
DeleteObject( hBitmap );
DeleteDC( hMemDC );
DeleteObject( hBrushBitmap );
SelectObject( hDC, hOldBrush );
DeleteObject( hBrush );
SelectObject( hDC, hOldColor );
ReleaseDC( NULL, hScreenDC );
return CreateIconIndirect(&iconinfo);
}