Setting a background color


Like any other control, you can handle the WM_CTLCOLOR message for the tree view control too. You can set the background color and it works - kind of. When the control draws the tree items, it ignores the color set by the WM_CTLCOLOR handler and uses the system color. The net effect is that portions of the control not covered by any tree item gets the custom color but the tree items have the system color as the background. I bet, that's not the kind of effect you are looking for.

The approach we will take to get a uniform background color is that whenever the control needs painting, we will paint the background, we will let the control draw the items in a memory device context and then paint this image transparently onto the control surface.

Step 1: Add handler function for WM_PAINT

Whenever the control needs to be updated the OnPaint() function gets called. We first create a memory device context that matches the paint DC in terms of the bitmap selected in it and the clip region. We let the default window procedure of the control draw in the memory DC using the system color as the background color.

Next we create a mask bitmap using yet another device context. A mask bitmap is a monochrome bitmap in which one color indicates the background color in the source bitmap and the other color indicates all the bits that are not the background color. We need the mask bitmap to copy only the foreground color from the memory DC to the paint DC.

Once we have the mask bitmap, we draw the background color using the paint DC and then draw the image in the memory DC transparently over the paint DC. I had initially used MaskBlt() for drawing the image transparently but found out that it was supported on NT only and not Windows 95. Here's what we do. The image in memDC is the foreground image. When drawing this image we have to somehow make the background color have no effect. We achieve this by setting the background to black using the mask bitmap. When we later use the SRCPAINT raster operation, the black color has no effect on the destination color. Similarly we use the mask bitmap to set the foreground color of the image in the paint DC to black. We finally combine the two images.

Thanks to Matthias Kerkhoff from Germany for pointing out the problem with MaskBlt() and suggesting the new method for drawing the image transparently.

void CTreeCtrlX::OnPaint() 
{
	CPaintDC dc(this);
	
	// Create a memory DC compatible with the paint DC
	CDC memDC;
	memDC.CreateCompatibleDC( &dc );

	CRect rcClip, rcClient;
	dc.GetClipBox( &rcClip );
	GetClientRect(&rcClient);

	// Select a compatible bitmap into the memory DC
	CBitmap bitmap;
	bitmap.CreateCompatibleBitmap( &dc, rcClient.Width(), rcClient.Height() );
	memDC.SelectObject( &bitmap );
	
	// Set clip region to be same as that in paint DC
	CRgn rgn;
	rgn.CreateRectRgnIndirect( &rcClip );
	memDC.SelectClipRgn(&rgn);
	rgn.DeleteObject();
	
	// First let the control do its default drawing.
	CWnd::DefWindowProc( WM_PAINT, (WPARAM)memDC.m_hDC, 0 );


	// Now create a mask
	CDC maskDC;
	maskDC.CreateCompatibleDC(&dc);
	CBitmap maskBitmap;

	// Create monochrome bitmap for the mask
	maskBitmap.CreateBitmap( rcClip.Width(), rcClip.Height(), 1, 1, NULL );
	maskDC.SelectObject( &maskBitmap );
	memDC.SetBkColor( ::GetSysColor( COLOR_WINDOW ) );

	// Create the mask from the memory DC
	maskDC.BitBlt( 0, 0, rcClip.Width(), rcClip.Height(), &memDC, 
				rcClip.left, rcClip.top, SRCCOPY );

	// Fill the background with custom color
	// Use a protected member variable to save the color
	// rather than hard coding it.
	dc.FillRect(rcClip, &CBrush(RGB(255,255,192)) );
	
	// Copy the image in memDC transparently

	// MaskBlt works in NT only - so we use another method
	//	dc.MaskBlt( rcClip.left, rcClip.top, rcClip.Width(), rcClip.Height(), &memDC, 
	//			rcClip.left, rcClip.top, maskBitmap, 0, 0, 
	//			MAKEROP4(SRCAND,SRCCOPY) );


	// Set the background in memDC to black. Using SRCPAINT with black and any other
	// color results in the other color, thus making black the transparent color
	memDC.SetBkColor(RGB(0,0,0));          
	memDC.SetTextColor(RGB(255,255,255));  
	memDC.BitBlt(rcClip.left, rcClip.top, rcClip.Width(), rcClip.Height(), &maskDC, rcClip.left, rcClip.top, SRCAND);

	// Set the foreground to black. See comment above.
	dc.SetBkColor(RGB(255,255,255));
	dc.SetTextColor(RGB(0,0,0));
	dc.BitBlt(rcClip.left, rcClip.top, rcClip.Width(), rcClip.Height(), &maskDC, rcClip.left, rcClip.top, SRCAND);
	
	// Combine the foreground with the background
	dc.BitBlt(rcClip.left, rcClip.top, rcClip.Width(), rcClip.Height(), &memDC, 
					rcClip.left, rcClip.top,SRCPAINT);
}

Step 2: Add handler for WM_ERASEBKGND

Since we are already drawing the background in the OnPaint() function handling this function and simply returning TRUE ensures that the default window procedure does not erase the background. Adding this handler prevents extra updates to the control's client area and thus reduces flicker.

In actual production code, you'd probably check for a non default background color before returning TRUE;

BOOL CTreeCtrlX::OnEraseBkgnd(CDC* pDC) 
{
 	// To prevent flickering when using non default bkcolor
	return TRUE;
}



Comments

  • Is there a way to change the background colour of an idiviual tree item

    Posted by Legacy on 08/16/2002 12:00am

    Originally posted by: Adam

    Hi i am trying to write an explorer like view pair
    (a tree view and a list view) when i select an item via programing this works fine however when I added the list view and set it as the active view (which is required to activate the toolbar option [largeIcon, SmallIcon, List and details] the selection on the tree disappears.

    Is there a way Ican leave the selection visible in the treeview while having the listview as my active view?

    Alternativly is there a way to change the background colour of an individual item ?

    Then I could change it in the treeviews on sel changing event ( reseting the colour of the currently selected item before setting the colo of the new selected item).

    Any help you can give (or problems you can forsee would be great [with solutions would be briliant])

    Thanks in advance for any help you can give

    Adam

    Reply
  • Setting color in scroll bar of tree control right side.

    Posted by Legacy on 01/18/2001 12:00am

    Originally posted by: Seokho Lee

    I want to set color in scroll Bar of tree control right side .

    but, I could not do it.

    please help me!

    I am poor at English.

    Sorry.

    Reply
  • Bug correction

    Posted by Legacy on 12/28/2000 12:00am

    Originally posted by: Sebastien Dubreucq

    The window is not properly updated when the updated area doesn't match with the entire client area. So it works only when the entire window has been invalidated with a call to InvalidateRect with the parameter NULL.

    2 lines need to be corrected, read comments in the code below:

    // Set the background in memDC to black. Using SRCPAINT with black and any other
    // color results in the other color, thus making black the transparent color
    memDC.SetBkColor(RGB(0,0,0));
    memDC.SetTextColor(RGB(255,255,255));
    memDC.BitBlt(rcClip.left, rcClip.top, rcClip.Width(), rcClip.Height(), &maskDC, 0,0/* Replacement here! old parameter : rcClip.left, rcClip.top*/, SRCAND);

    // Set the foreground to black. See comment above.
    dc.SetBkColor(RGB(255,255,255));
    dc.SetTextColor(RGB(0,0,0));
    dc.BitBlt(rcClip.left, rcClip.top, rcClip.Width(), rcClip.Height(), &maskDC, 0,0 /* Replacement here! old parameter : rcClip.left, rcClip.top*/, SRCAND);

    (I use Visual C++ 5)

    Reply
  • Why not SetBkColor(...)

    Posted by Legacy on 11/24/2000 12:00am

    Originally posted by: Bruno Leclerc

    I simply tryed a GetTreeCtrl().SetBkColor(MyColor) in OnInitDialog, i don't see anything wrong ?!

    Reply
  • SetBkColor and latest comctrl32.dll

    Posted by Legacy on 03/26/1999 12:00am

    Originally posted by: Martin Rubas

    It looks like problems with SetBkColor has been solved with new common control library. I have installed MSIE5.0 which uses COMCTL32.DLL version 5.80 . It is also marked as part of MS Windows 2000 with product version 5.00.2014.216.

    I have called SetBkColor (w/out ModifyStyle trick) before and after I added some items to the tree control and it always worked fine.


    Reply
  • You had better use such macros as TreeView_SetBkColor,TreeView_SetTextColor

    Posted by Legacy on 03/22/1999 12:00am

    Originally posted by: J-Shin

    comctl32.dll version 4.71 or later is required.
    It doesn't seem to dither the required color.

    Good Luck!

    Reply
  • There is a fix from Microsoft

    Posted by Legacy on 03/21/1999 12:00am

    Originally posted by: l.e.rousseau

    See q216140.

    You need to turn off the TVS_HASLINES
    and back on again.

    like this :
    l_pTreeCtrl->ModifyStyle( TVS_HASLINES, 0, 0 );
    l_pTreeCtrl->ModifyStyle( 0, TVS_HASLINES, 0 );

    The only problem I've had is that with
    a dark gray background the square
    vanishes - possibly a XOR problem.

    Reply
  • Problem with MFC-Handling for Tree-chenging

    Posted by Legacy on 03/01/1999 12:00am

    Originally posted by: Joerg Dettmann

    This method has problems when the MFC change the display of the Tree
    like collapse, changing focus, ...

    Reply
  • SetBkColor(..) works if ...

    Posted by Legacy on 01/20/1999 12:00am

    Originally posted by: Cathy Yang

    Funciton CTreeCtrl::SetBkColor(...) works very well if you call it BEFORE you build the tree, not after.

    Reply
  • It doesn't seem to work in my computer.

    Posted by Legacy on 01/06/1999 12:00am

    Originally posted by: Peng Jianrong

    I'm sorry,it doesn't seem to work in my computer. I use appwizard to
    generate a project with a view derived from CFormView and add a CTreeCtrl control into it. I copy these codes to my program,but when I
    add a node into the tree control,the background color was changed to mosaic(Don't laugh at my poor English,I'm from China). I try to use the control's SetBkColor (not CDC 's member function), only change the color of the right of the nodes, but when I use SetBkColor twice, it do work. At last I find only 16 colors can be set as uniform background.
    I don't handle the WM_PAINT and other windows message and I do this
    by overloading the CFormView's OnInitialUpdate function.
    I'm from China (P.R.C)and I use Windows 95(OSR) simply Chinese edition
    and VC++ 6.0, I like this site very much, thank you.


    Reply
Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • On-demand Event Event Date: September 10, 2014 Modern mobile applications connect systems-of-engagement (mobile apps) with systems-of-record (traditional IT) to deliver new and innovative business value. But the lifecycle for development of mobile apps is also new and different. Emerging trends in mobile development call for faster delivery of incremental features, coupled with feedback from the users of the app "in the wild." This loop of continuous delivery and continuous feedback is how the best mobile …

  • Webinar on September 23, 2014, 2 p.m. ET / 11 a.m. PT Mobile commerce presents an array of opportunities for any business -- from connecting with your customers through mobile apps to enriching operations with mobile enterprise solutions. Join guest speaker, Michael Facemire, Forrester Research, Inc. Principal Analyst, as he discusses the new demands of mobile engagement and how application program interfaces (APIs) play a crucial role. Check out this upcoming webinar to learn about the new set of …

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds