Microsoft & .NETVisual C#Full-Featured 24-bit Color Toolbar

Full-Featured 24-bit Color Toolbar

Developer.com content and product recommendations are editorially independent. We may make money when you click on links to our partners. Learn More.


Environment: VC6.0 SP4, Win95/NT3.51 or later

Creating a CToolbar with 16 or 24 bit buttons is easy if you aren’t worried about disabled buttons and your buttons don’t have any pixels that should be set to the toolbar’s background color. Things quickly get more complicated if you want to correctly handle those other cases. This sample program attaches 24 bit images to the standard MFC main frame toolbar and handles both disabled and hot buttons as well as transparent button backgrounds. It can easily be extended to handle 16 bit images or different toolbar configurations.

If your application can be run with the display set to less than 16 bits per pixel, you should add code to check the display depth and not load the high color toolbars in those cases, since they won’t look good.

Specifying separate bitmaps for the enabled, disabled, and hot buttons isn’t too hard–CToolbar::GetToolbarCtrl() gets the toolbar control, then CToolbarCtrl::SetImageList(), CToolbarCtrl::SetDisabledImageList(), and CToolbarCtrl::SetHotImageList() are used to assign the bitmaps.

Making the image lists 24 bits deep requires creating the CImageLists with no images, then loading the images as CBitmaps, and finally copying the CBitmaps into the CImageLists.

Loading the bitmaps as 24 bits per pixel even when the user’s screen is set to a different bit depth requires extra work, too. CBitmap::LoadBitmap() converts the bitmap to the screen’s bit depth, so the Win32 function ::LoadImage() is used instead to create a 24 bit DIBSECTION, which is then attached to a CBitmap so it can be passed to CImageList::Add().

When a toolbar is created in the Visual Studio toolbar editor, any pixel that is light gray (RGB (192, 192, 192)) is replaced with the user’s chosen button color at runtime, making those pixels effectively transparent. For some reason this doesn’t happen to 24 bit CImageLists. Specifying a mask color when the CImageList is created just ends up replacing that color with black, not with the system button color. This code does that color subsitution “manually.” Before a bitmap is added to an image list, the code iterates over the pixels in the bitmap, replacing each RGB (192, 192, 192) pixel with the system button color (::GetSysColor (COLOR_BTNFACE)). Since the bitmaps are DIBSECTIONs, the code can access the pixels directly for maximum efficiency.

Since the Visual Studio graphics editor can’t handle 24 bit images, you must edit the images in another program that can, such as GIMP or Adobe Photoshop. Save them as 24 bit .bmp files in the project’s “res” folder. You can then import them into the project using the “import” command in the Developer Studio “Resource” tab.

Downloads

// these constants represent the dimensions and number of buttons in
// the default MFC-generated toolbar. If you need something different,
// feel free to change them. For extra credit, you can load the
// toolbar's existing image list at runtime and copy the parameters from
// there.
static const int	kImageWidth (16);
static const int	kImageHeight (15);
static const int	kNumImages (8);

static const UINT	kToolBarBitDepth (ILC_COLOR24);

// this color will be treated as transparent in the loaded bitmaps --
// in other words, any pixel of this color will be set at runtime to
// the user's button color. The Visual Studio toolbar editor defaults
// to 192, 192, 192 (light gray).
static const RGBTRIPLE	kBackgroundColor = {192, 192, 192};


int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
 if (CFrameWnd::OnCreate(lpCreateStruct) == -1)
  return -1;

 if (!m_wndToolBar.CreateEx(this, TBSTYLE_FLAT, WS_CHILD | WS_VISIBLE | CBRS_TOP
 | CBRS_GRIPPER | CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC) ||
 !m_wndToolBar.LoadToolBar(IDR_MAINFRAME))
 {
  TRACE0("Failed to create toolbarn");
  return -1; <span class="codeComment">// fail to create
 }

 // attach the hicolor bitmaps to the toolbar
 AttachToolbarImages (IDB_HICOLOR_TOOLBAR,
 IDB_HICOLOR_TOOLBAR_DISABLED,
 IDB_HICOLOR_TOOLBAR_HOT);

 // the rest of your CMainFrame::OnCreate() code goes here
}

// find every pixel of the default background color in the specified
// bitmap and set each one to the user's button color.
static void	ReplaceBackgroundColor (CBitmap& ioBM)
{
 // figure out how many pixels there are in the bitmap
 BITMAP		bmInfo;

 VERIFY (ioBM.GetBitmap (&bmInfo));

 // add support for additional bit depths if you choose
 VERIFY (bmInfo.bmBitsPixel == 24);
 VERIFY (bmInfo.bmWidthBytes == (bmInfo.bmWidth * 3));

 const UINT		numPixels (bmInfo.bmHeight * bmInfo.bmWidth);

 // get a pointer to the pixels
 DIBSECTION  ds;

 VERIFY (ioBM.GetObject (sizeof (DIBSECTION), &ds) == sizeof (DIBSECTION));

 RGBTRIPLE*		pixels = reinterpret_cast<RGBTRIPLE*>(ds.dsBm.bmBits);
 VERIFY (pixels != NULL);

 // get the user's preferred button color from the system
 const COLORREF		buttonColor (::GetSysColor (COLOR_BTNFACE));
 const RGBTRIPLE		userBackgroundColor = {
 GetBValue (buttonColor), GetGValue (buttonColor), GetRValue (buttonColor)};

 // search through the pixels, substituting the user's button
 // color for any pixel that has the magic background color
 for (UINT i = 0; i < numPixels; ++i)
 {
  if (pixels [i].rgbtBlue == kBackgroundColor.rgbtBlue 
  && pixels [i].rgbtGreen == kBackgroundColor.rgbtGreen 
  && pixels [i].rgbtRed == kBackgroundColor.rgbtRed)
  {
   pixels [i] = userBackgroundColor;
  }
 }
}


// create an image list for the specified BMP resource
static void	MakeToolbarImageList (UINT inBitmapID,
                                  CImageList&	outImageList)
{
 CBitmap		bm;

 // if we use CBitmap::LoadBitmap() to load the bitmap, the colors
 // will be reduced to the bit depth of the main screen and we won't
 // be able to access the pixels directly. To avoid those problems,
 // we'll load the bitmap as a DIBSection instead and attach the
 // DIBSection to the CBitmap.
 VERIFY (bm.Attach (::LoadImage (::AfxFindResourceHandle(
 MAKEINTRESOURCE (inBitmapID), RT_BITMAP),
 MAKEINTRESOURCE (inBitmapID), IMAGE_BITMAP, 0, 0,
 (LR_DEFAULTSIZE | LR_CREATEDIBSECTION))));

 // replace the specified color in the bitmap with the user's
 // button color
 ::ReplaceBackgroundColor (bm);

 // create a 24 bit image list with the same dimensions and number
 // of buttons as the toolbar
 VERIFY (outImageList.Create (
 kImageWidth, kImageHeight, kToolBarBitDepth, kNumImages, 0));

 // attach the bitmap to the image list
 VERIFY (outImageList.Add (&bm, RGB (0, 0, 0)) != -1);
}


// load the high color toolbar images and attach them to m_wndToolBar
void CMainFrame::AttachToolbarImages (UINT inNormalImageID,
                                      UINT inDisabledImageID,
                                      UINT inHotImageID)
{
  // make high-color image lists for each of the bitmaps
 ::MakeToolbarImageList (inNormalImageID, m_ToolbarImages);
 ::MakeToolbarImageList (inDisabledImageID, m_ToolbarImagesDisabled);
 ::MakeToolbarImageList (inHotImageID, m_ToolbarImagesHot);

  // get the toolbar control associated with the CToolbar object
 CToolBarCtrl&	barCtrl = m_wndToolBar.GetToolBarCtrl();

  // attach the image lists to the toolbar control
 barCtrl.SetImageList (&m_ToolbarImages);
 barCtrl.SetDisabledImageList (&m_ToolbarImagesDisabled);
 barCtrl.SetHotImageList (&m_ToolbarImagesHot);
}


Download demo project – 37 Kb

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Latest Posts

Related Stories