Click here to Skip to main content
15,884,778 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
I am working on a transparent treeview control. In order to achieve transparency I have subclassed the tree and am overriding WM_PAINT ( in my WM_ERASEBKGND handler I just return TRUE. Scrolling, mousewheel and other relevant messages are handled properly ). To make tree’s background transparent I am using the following algorithm ( based on this CodeGuru[^] article ):

1. Let tree do its default painting in memory DC ( saved in memDC ).

2. Get parent’s background in another memory DC ( saved in finalDC ).

3. Map tree’s and parent’s coordinates so I can grab correct portion of parent’s background bitmap.

4. Combine these two images using TransparentBlt and tree’s background color ( TransparentBlt( finalDC, ... , memDC, ... ); ).

In parent window I have implemented WM_PRINTCLIENT, so I can copy its background into memory DC ( step 2 ) with a simple ::SendMessage( GetParent(hwnd), WM_PRINTCLIENT, (WPARAM)finalDC, (LPARAM)(PRF_CLIENT) ); call. The result I get[^] is correct both on Windows XP and Windows7 .

I obtain tree’s default bitmap ( step 1 ) with ::DefSubclassProc( hwnd, WM_PAINT, (WPARAM)memDC, 0 ); call. Here too, the result is correct both on Windows XP[^] and Windows7[^].

However, after TransparentBlt() call, final picture is not drawn properly. On Windows XP[^] checkboxes are the problem, and on Windows7[^] some white is left in letters.

These pictures are result of exporting bitmaps from device contexts into file ( I have modified this code[^] to achieve that ).

Here is the code snippet for WM_PAINT:
C++
case WM_PAINT:
    {
        // usual stuff
        PAINTSTRUCT ps;

        RECT rcClient = {0};
        GetClientRect( hwnd, &rcClient );

        HDC hdc = BeginPaint( hwnd, &ps );

        // create helper memory DCs 
        HDC memDC = CreateCompatibleDC(hdc), finalDC = CreateCompatibleDC(hdc);

        // create helper bitmaps
        HBITMAP memBmp,  // default tree's paint
            finalBmp,    // parent's background image
            bmpOld, bmpOldFinal;  // needed for cleanup

        memBmp = CreateCompatibleBitmap( hdc, 
            rcClient.right - rcClient.left,
            rcClient.bottom - rcClient.top );

        bmpOld = (HBITMAP)SelectObject( memDC, memBmp );

        // map parent and child rectangles

        RECT rcParent; 
        GetClientRect( GetParent(hwnd), &rcParent );

        // upper left corners of the treeview, parent window
        POINT ptTreeUL, ptParentUL; 

        // map tree's coordinates
        ptTreeUL.x = rcClient.left;
        ptTreeUL.y = rcClient.top;

        ClientToScreen( hwnd, &ptTreeUL );

        // map parent's coordinates
        ptParentUL.x = rcParent.left;
        ptParentUL.y = rcParent.top;

        ScreenToClient( GetParent(hwnd), &ptParentUL );

        /********* get parent's background image *******/

        finalBmp = CreateCompatibleBitmap( hdc, 
            rcParent.right - rcParent.left,
            rcParent.bottom - rcParent.top );

        bmpOldFinal = (HBITMAP)SelectObject( finalDC, finalBmp );

        ::SendMessage( GetParent(hwnd), WM_PRINTCLIENT,(WPARAM)finalDC, 
            (LPARAM)(PRF_CLIENT) );

        /********* capture default tree image *********/

        ::DefSubclassProc( hwnd, WM_PAINT, (WPARAM)memDC, 0 );

        // get tree's background color 

        COLORREF clrMask = TreeView_GetBkColor(hwnd);

        if( clrMask == -1 )  // this means tree uses default system color
            clrMask = ::GetSysColor(COLOR_WINDOW);

        /**** combine tree's default image with parent's background ****/
        /**** so we can erase default background with parent's background ****/

        TransparentBlt( finalDC, 
            ptParentUL.x + ptTreeUL.x, 
            ptParentUL.y + ptTreeUL.y, 
            rcClient.right - rcClient.left, 
            rcClient.bottom - rcClient.top,
            memDC, 
            0, 0, 
            rcClient.right - rcClient.left, 
            rcClient.bottom - rcClient.top,
            clrMask );

        // draw the result into tree's DC
        BitBlt( hdc, 
            0, 0, 
            rcClient.right - rcClient.left, 
            rcClient.bottom - rcClient.top,
            finalDC, 
            ptParentUL.x + ptTreeUL.x, 
            ptParentUL.y + ptTreeUL.y, SRCCOPY);

        // cleanup
        SelectObject( memDC, bmpOld );
        DeleteDC( memDC );
        DeleteObject( memBmp );
        SelectObject( finalDC, bmpOldFinal );
        DeleteDC( finalDC );
        DeleteObject( finalBmp );

        EndPaint( hwnd, &ps );
    }
    return 0L;


EDIT:

I was able to solve the problem with checkboxes by ditching TransparentBlt() and doing manual transparency. Only font issue remains to be solved. In order to improve my chances of solving this I submit a demo application with subclassing procedure and main window with sample treeview control that uses it :

C++
#include <windows.h>
#include <windowsx.h>
#include <commctrl.h>

#pragma comment( lib, "comctl32.lib")
#pragma comment( lib,"Msimg32.lib")   // needed for GradientFill() API
#pragma comment( linker, "/manifestdependency:\"type='win32' \
                         name='Microsoft.Windows.Common-Controls' version='6.0.0.0' \
                         processorArchitecture='*' publicKeyToken='6595b64144ccf1df' \
                         language='*'\"")

// HINSTANCE of our window
HINSTANCE hInst;

// helper function for drawing parent background
void GradientTriangle( HDC MemDC, 
    LONG x1, LONG y1, 
    LONG x2, LONG y2, 
    LONG x3, LONG y3, 
    COLORREF top, COLORREF bottom )
{
    TRIVERTEX vertex[3];

    vertex[0].x     = x1;
    vertex[0].y     = y1;
    vertex[0].Red   = GetRValue(bottom) << 8;
    vertex[0].Green = GetGValue(bottom) << 8;
    vertex[0].Blue  = GetBValue(bottom) << 8;
    vertex[0].Alpha = 0x0000;

    vertex[1].x     = x3;
    vertex[1].y     = y3; 
    vertex[1].Red   = GetRValue(bottom) << 8;
    vertex[1].Green = GetGValue(bottom) << 8;
    vertex[1].Blue  = GetBValue(bottom) << 8;
    vertex[1].Alpha = 0x0000;

    vertex[2].x     = x2;
    vertex[2].y     = y2;
    vertex[2].Red   = GetRValue(top) << 8;
    vertex[2].Green = GetGValue(top) << 8;
    vertex[2].Blue  = GetBValue(top) << 8;
    vertex[2].Alpha = 0x0000;

    // Create a GRADIENT_TRIANGLE structure that
    // references the TRIVERTEX vertices.

    GRADIENT_TRIANGLE gTriangle;

    gTriangle.Vertex1 = 0;
    gTriangle.Vertex2 = 1;
    gTriangle.Vertex3 = 2;

    // Draw a shaded triangle.

    GradientFill( MemDC, vertex, 3, &gTriangle, 1, GRADIENT_FILL_TRIANGLE);
}

// subclass procedure for transparent tree
LRESULT CALLBACK TreeProc( HWND hwnd, UINT message, 
    WPARAM wParam, LPARAM lParam, 
    UINT_PTR uIdSubclass, DWORD_PTR dwRefData )
{
    switch (message)
    {
    // handle messages that paint tree without WM_PAINT
    case WM_TIMER:  // handles autoscrolling when item is partially visible
    case TVM_DELETEITEM:
    case TVM_INSERTITEM: 
    case WM_MOUSEWHEEL: 
    case WM_HSCROLL:  
    case WM_VSCROLL:  
        {
            ::SendMessage( hwnd, WM_SETREDRAW, (WPARAM)FALSE, 0 );

            LRESULT lres = ::DefSubclassProc( hwnd, message, wParam, lParam );

            ::SendMessage( hwnd, WM_SETREDRAW, (WPARAM)TRUE, 0 );

            ::RedrawWindow( hwnd, NULL, NULL, 
                RDW_FRAME | RDW_INVALIDATE | RDW_UPDATENOW );

            return lres;
        }
    case WM_PAINT:
        {
            // usual stuff

            PAINTSTRUCT ps;
            HDC hdc = BeginPaint( hwnd, &ps );

            // get client coordinates of parent and tree window

            RECT rcClient = {0}, rcParent = {0};

            GetClientRect( hwnd, &rcClient );
            GetClientRect( GetParent(hwnd), &rcParent );

            // create helper DCs and bitmaps

            HDC memDC = CreateCompatibleDC(hdc),    //default tree paint
                finalDC = CreateCompatibleDC(hdc),  // parent's WM_PRINTCLIENT
                maskDC = CreateCompatibleDC(hdc);   // DC that holds monochrome mask

            HBITMAP memBmp,  // default tree's paint
                finalBmp,    // parent's background image
                maskBmp,      // monochrome mask
                bmpOld, bmpOldFinal, bmpOldMask;  // needed for cleanup

            memBmp = CreateCompatibleBitmap( hdc, rcClient.right - rcClient.left,
                rcClient.bottom - rcClient.top );

            bmpOld = (HBITMAP)SelectObject( memDC, memBmp );

            /****** get parent's background image *******/

            finalBmp = CreateCompatibleBitmap( hdc, rcParent.right - rcParent.left,
                rcParent.bottom - rcParent.top );

            bmpOldFinal = (HBITMAP)SelectObject( finalDC, finalBmp );

            ::SendMessage( GetParent(hwnd), WM_PRINTCLIENT,(WPARAM)finalDC,
                (LPARAM)(PRF_CLIENT) );

            /****** capture default tree image *********/

            ::SendMessage( hwnd, WM_PRINTCLIENT,(WPARAM)memDC, 
                (LPARAM)(PRF_CLIENT) );

            /********** create monochrome mask *******/

            // get tree's background color 

            COLORREF clrMask = TreeView_GetBkColor(hwnd);

            if( clrMask == -1 )
                clrMask = ::GetSysColor(COLOR_WINDOW);

            maskBmp = CreateBitmap( rcClient.right - rcClient.left,
               rcClient.bottom - rcClient.top, 1, 1, NULL );

            bmpOldMask = (HBITMAP)SelectObject( maskDC, maskBmp );

            SetBkColor( memDC, clrMask ); // this color becomes white, all others black

            BitBlt( maskDC, 0, 0, rcClient.right - rcClient.left, 
                rcClient.bottom - rcClient.top, memDC, 0, 0, SRCCOPY );

            /***** map tree's coordinates to parent window *****/

            POINT ptTreeUL;

            ptTreeUL.x = rcClient.left;
            ptTreeUL.y = rcClient.top;

            ClientToScreen( hwnd, &ptTreeUL );
            ScreenToClient( GetParent(hwnd), &ptTreeUL );

            /***** creating transparent background ********/

            // mask the original image

            SetBkColor( memDC, RGB( 0, 0, 0 ) ); 
            SetTextColor( memDC, RGB( 255, 255, 255 ) );

            BitBlt( memDC, 0, 0, 
                rcClient.right - rcClient.left, 
                rcClient.bottom - rcClient.top,
                maskDC, 0, 0, SRCAND );

            // create transparent treeview image 

            SetBkColor( finalDC, RGB ( 255, 255, 255 ) ); 
            SetTextColor( finalDC, RGB ( 0, 0, 0 ) );

            BitBlt( finalDC, ptTreeUL.x, ptTreeUL.y, 
                rcClient.right - rcClient.left, 
                rcClient.bottom - rcClient.top,
                maskDC, 0, 0, SRCAND );

            BitBlt( finalDC, ptTreeUL.x, ptTreeUL.y, 
                rcClient.right - rcClient.left, 
                rcClient.bottom - rcClient.top,
                memDC, 0, 0, SRCPAINT );

            // display the result 

            BitBlt( hdc, 0, 0, 
                rcClient.right - rcClient.left, 
                rcClient.bottom - rcClient.top,
                finalDC, ptTreeUL.x, ptTreeUL.y, SRCCOPY );

            /***************** cleanup ******************/

            SelectObject( memDC, bmpOld );
            DeleteDC( memDC );
            DeleteObject( memBmp );

            SelectObject( finalDC, bmpOldFinal );
            DeleteDC( finalDC );
            DeleteObject( finalBmp );

            SelectObject( maskDC, bmpOldMask );
            DeleteDC( maskDC );
            DeleteObject( maskBmp );

            EndPaint( hwnd, &ps );
        }
        return 0L;
    case WM_ERASEBKGND:
        return TRUE;
    case WM_NCDESTROY:
        ::RemoveWindowSubclass( hwnd, TreeProc, 0 );
        return ::DefSubclassProc( hwnd, message, wParam, lParam);
    }
    return ::DefSubclassProc( hwnd, message, wParam, lParam);
}

LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
	switch(msg)
	{
	case WM_CREATE:
		{
        		HWND TreeView = CreateWindowEx(0, WC_TREEVIEW, 
        			TEXT("Tree View"), WS_VISIBLE | WS_CHILD | WS_BORDER
				| TVS_HASBUTTONS | TVS_HASLINES | TVS_LINESATROOT, 
				50, 10, 200, 200, 
				hwnd, (HMENU)7000, hInst, NULL);

			/************ enable checkboxes **************/

			DWORD dwStyle = GetWindowLong( TreeView , GWL_STYLE);
			dwStyle |= TVS_CHECKBOXES;
			SetWindowLongPtr( TreeView , GWL_STYLE, dwStyle );

			/**** subclass it ****/

			SetWindowSubclass( TreeView, TreeProc, 0, 0 );

			/************ add image list ********/

			HIMAGELIST hImages = ImageList_Create( 16, 16, ILC_MASK, 1, 0 );

			// load system icon so we can dodge the deletion and rc.file
			HICON hiBG = reinterpret_cast<HICON>( LoadImage( 0, 
				MAKEINTRESOURCE(IDI_WARNING), 
                                IMAGE_ICON, 0, 0, LR_SHARED ) );

			ImageList_AddIcon( hImages, hiBG );

			TreeView_SetImageList( GetDlgItem( hwnd, 7000 ), hImages, 
                            TVSIL_NORMAL );

			/************ add items and subitems **********/

			// add root item

			TVINSERTSTRUCT tvis = {0};

			tvis.item.mask = TVIF_TEXT;
			tvis.item.pszText = L"This is root item";
			tvis.hInsertAfter = TVI_LAST;
			tvis.hParent = TVI_ROOT;

			HTREEITEM hRootItem = reinterpret_cast<HTREEITEM>( 
                            SendMessage( TreeView , TVM_INSERTITEM, 0, 
                            reinterpret_cast<LPARAM>( &tvis ) ) );

			// add subitems for the hTreeItem

			for( int i = 0; i < 15; i++ )
			{
				memset( &tvis, 0, sizeof(TVINSERTSTRUCT) );

				tvis.item.mask = TVIF_TEXT;
				tvis.item.pszText = L"This is subitem";
				tvis.hInsertAfter = TVI_LAST;
				tvis.hParent = hRootItem;

				SendMessage( TreeView , TVM_INSERTITEM, 0,
                                    reinterpret_cast<LPARAM>( &tvis ) );
			}
		}
		return 0L;
	case WM_NOTIFY:
		{
			if( ((LPNMHDR)lParam)->code == TVN_SELCHANGING )
			{
				InvalidateRect( ((LPNMHDR)lParam)->hwndFrom, 
                                    NULL, FALSE );
				break;
			}

			if( ((LPNMHDR)lParam)->code == TVN_ITEMEXPANDING )
			{
				InvalidateRect( ((LPNMHDR)lParam)->hwndFrom, 
                                    NULL, FALSE );
				break;
			}
		}
		break;
	case WM_PRINTCLIENT:
		{
			RECT r;
			GetClientRect( hwnd, &r );

			GradientTriangle( (HDC)wParam, r.right, r.bottom - r.top, 
				r.left, r.bottom - r.top,
				r.left, r.top,
				RGB( 0x0, 0x0, 0xFF ), 
                                RGB( 0xFF, 0xFF, 0x0 ) );

			GradientTriangle( (HDC)wParam, 
                                r.right, r.bottom - r.top, 
				r.right, r.top,
				r.left, r.top, 
				RGB( 0xFF, 0x0, 0x0 ), 
                                RGB( 0x0, 0xFF, 0x0 ) );

			return 0L;
		}
		break;
	case WM_ERASEBKGND:
		return 1L;
	case WM_PAINT:
		{
			PAINTSTRUCT ps;
			HDC hdc = BeginPaint( hwnd, &ps );

			RECT r;
			GetClientRect( hwnd, &r );

			GradientTriangle( hdc, r.right, 
                                r.bottom - r.top, 
				r.left, r.bottom - r.top,
				r.left, r.top,
				RGB( 0x0, 0x0, 0xFF ), 
                                RGB( 0xFF, 0xFF, 0x0 ) );

			GradientTriangle( hdc, r.right, 
                                r.bottom - r.top, 
				r.right, r.top,
				r.left, r.top, 
				RGB( 0xFF, 0x0, 0x0 ), 
                                RGB( 0x0, 0xFF, 0x0 ) );

			EndPaint( hwnd, &ps );
		}
		return 0L;
	case WM_CLOSE:
		{
			// delete tree's imagelist
			HIMAGELIST hImages = reinterpret_cast<HIMAGELIST>( 
                            SendMessage( GetDlgItem( hwnd, 7000 ), 
                            TVM_GETIMAGELIST, 0, 0 ) );

			ImageList_Destroy(hImages);

			DestroyWindow(hwnd);
		}
		break;
	case WM_DESTROY:
		PostQuitMessage(0);
		break;
	default:
		return DefWindowProc(hwnd, msg, wParam, lParam);
	}
	return 0;
}

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
	WNDCLASSEX wc;
	HWND hwnd;
	MSG Msg;

	// initialize common controls
	INITCOMMONCONTROLSEX iccex;
	iccex.dwSize = sizeof(INITCOMMONCONTROLSEX);
	iccex.dwICC = ICC_LISTVIEW_CLASSES | ICC_UPDOWN_CLASS | ICC_STANDARD_CLASSES ;
	InitCommonControlsEx(&iccex);

	hInst = hInstance;

	wc.cbSize		 = sizeof(WNDCLASSEX);
	wc.style		 = 0;
	wc.lpfnWndProc	 = WndProc;
	wc.cbClsExtra	 = 0;
	wc.cbWndExtra	 = 0;
	wc.hInstance	 = hInstance;
	wc.hIcon		 = LoadIcon(NULL, IDI_APPLICATION);
	wc.hCursor		 = LoadCursor(NULL, IDC_ARROW);
	wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
	wc.lpszMenuName  = NULL;
	wc.lpszClassName = L"Transparent TreeView";
	wc.hIconSm		 = LoadIcon(NULL, IDI_APPLICATION);

	if(!RegisterClassEx(&wc))
	{
		MessageBox(NULL, L"Window Registration Failed!", L"Error!", 
			MB_ICONEXCLAMATION | MB_OK);
		return 0;
	}

	hwnd = CreateWindowEx( WS_EX_CLIENTEDGE, L"Transparent TreeView",
		L"The title of my window", 
                WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 
		300, 300, NULL, NULL, hInstance, NULL);

	if(hwnd == NULL)
	{
		MessageBox(NULL, L"Window Creation Failed!", L"Error!", 
                    MB_ICONEXCLAMATION | MB_OK);
		return 0;
	}

	ShowWindow(hwnd, nCmdShow);
	UpdateWindow(hwnd);

	while(GetMessage(&Msg, NULL, 0, 0) > 0)
	{
		TranslateMessage(&Msg);
		DispatchMessage(&Msg);
	}
	return Msg.wParam;
}

How can I properly combine default tree bitmap with parent’s background, achieving transparency without visual artifacts ?
Posted
Updated 5-May-14 10:13am
v4

This line wont do what I think you expect expect and will be windows version dependent you seem to expect it to paint into your supplied memDC.

DefSubclassProc( hwnd, WM_PAINT, (WPARAM)memDC, 0 );


Look at your own version of WM_PAINT ... see the issue you don't use the WPARAM at all yourself and your code would fail at this itself.

That is the whole reason WM_PRINTCLIENT message was added and is needed for Windows API.
 
Share this answer
 
v2
Comments
AlwaysLearningNewStuff 4-May-14 14:21pm    
I was able to fix the problem with checkboxes by ditching TransparentBlt() and doing manual transparency.

The white leftover in text is due to ClearType font set in Control Panel, and after I change this option in control panel to Standard text is painted properly. This is the only thing left for me to solve.

I have rewritten my code to use WM_PRINTCLIENT instead of the line you pointed out and still I have trouble with ClearType font. If you have any suggestions I would highly appreciate it.

Thank you.

Best regards.
leon de boer 4-May-14 20:36pm    
I would have simply painted the whole area myself rather than try to use Microsoft's draw at all which is likely to change anyhow.

That said if you really want to stay with you current scheme can't you simply put rectangle over the area the text is currently in on the memory DC and then write your own text over the now solid clear area?
AlwaysLearningNewStuff 5-May-14 1:52am    
I do not know how to do all you suggest. Perhaps you could give me some instructions ?

Drawing everything myself could be acceptable solution, but I would like to keep the default tree functionality. I do not understand quite clear how to "put rectangle" over already painted item ( in memory DC ) and to paint text.

Thank you for an imaginative suggestion ( I apologize for responding now, but I am on the other part of the world ).

Best regards.
leon de boer 5-May-14 3:25am    
I will need to write some code and post up for the complete draw and I don't have time right this minute so will get back to you on that one.

However drawing the rectangle is simple you have a DC called FinalDC it doesn't matter that it's a memory DC windows doesn't care it still draws on it like it is the screen.

So simply select a pen and brush onto the DC and draw a rectangle over the text that is cause int the problem and then do a TextOut over it .. you know the usual make a pen and brush and use them

SelectObject(FinalDC, Pen);
SelectObject(FinalDC, Brush);
Rectangle(FinalDC, x1, y1, x2, y2); /// x1,y1 & x2,y2 are local cordinates to block out offending text
TextOut(FinalDC, x1, y1, Txt, TextLen); // write your text over the area you cleared

Now BitBLT your final bit as usual.

By the way did you see the other code I did to explain number entry subclassing
http://www.codeproject.com/Tips/767325/Validating-Window-Controls-by-Subclassing

AlwaysLearningNewStuff 5-May-14 4:19am    
Article seems awesome, I will look at the files as soon as I fix this problem. I tried to vote it 5 stars but seems that I can not... Also, they "invented" new rules and I just can't figure out how to download the files... This is so frustrating!

As for rectangle and text, I just do not know how to get the coordinates of the tree's item. If I could get those precise coordinates I could use TVITEM structure to get the item text and try to draw the damn thing myself...
All fixed .. I made the Listview background bright pink to get better transparency an use a color unlikely to exist in any microsoft or user scheme. The text was still a bit ugly because of cleartype so I disabled cleartype on the listview.

I also fixed up the window so when it resized it viewed properly and I cut down the paint code sorry it all annoyed me :smile:

You can pick up all my changes by search for the text LdB

Image of result: http://s30.postimg.org/dwr7j500h/fixed.jpg[^]

#include <windows.h>
#include <windowsx.h>
#include <commctrl.h>

#pragma comment( lib, "comctl32.lib")
#pragma comment( lib,"Msimg32.lib")   // needed for GradientFill() API
#pragma comment( linker, "/manifestdependency:\"type='win32' \
                         name='Microsoft.Windows.Common-Controls' version='6.0.0.0' \
                         processorArchitecture='*' publicKeyToken='6595b64144ccf1df' \
                         language='*'\"")
 
// HINSTANCE of our window
HINSTANCE hInst;
 
// helper function for drawing parent background
void GradientTriangle( HDC MemDC, 
    LONG x1, LONG y1, 
    LONG x2, LONG y2, 
    LONG x3, LONG y3, 
    COLORREF top, COLORREF bottom )
{
    TRIVERTEX vertex[3];
 
    vertex[0].x     = x1;
    vertex[0].y     = y1;
    vertex[0].Red   = GetRValue(bottom) << 8;
    vertex[0].Green = GetGValue(bottom) << 8;
    vertex[0].Blue  = GetBValue(bottom) << 8;
    vertex[0].Alpha = 0x0000;
 
    vertex[1].x     = x3;
    vertex[1].y     = y3; 
    vertex[1].Red   = GetRValue(bottom) << 8;
    vertex[1].Green = GetGValue(bottom) << 8;
    vertex[1].Blue  = GetBValue(bottom) << 8;
    vertex[1].Alpha = 0x0000;
 
    vertex[2].x     = x2;
    vertex[2].y     = y2;
    vertex[2].Red   = GetRValue(top) << 8;
    vertex[2].Green = GetGValue(top) << 8;
    vertex[2].Blue  = GetBValue(top) << 8;
    vertex[2].Alpha = 0x0000;
 
    // Create a GRADIENT_TRIANGLE structure that
    // references the TRIVERTEX vertices.

    GRADIENT_TRIANGLE gTriangle;
 
    gTriangle.Vertex1 = 0;
    gTriangle.Vertex2 = 1;
    gTriangle.Vertex3 = 2;
 
    // Draw a shaded triangle.

    GradientFill( MemDC, vertex, 3, &gTriangle, 1, GRADIENT_FILL_TRIANGLE);
}
 

// subclass procedure for transparent tree
LRESULT CALLBACK TreeProc( HWND hwnd, UINT message, 
    WPARAM wParam, LPARAM lParam, 
    UINT_PTR uIdSubclass, DWORD_PTR dwRefData )
{

    switch (message)
    {
    // handle messages that paint tree without WM_PAINT
    case WM_TIMER:  // handles autoscrolling when item is partially visible
    case TVM_DELETEITEM:
    case TVM_INSERTITEM: 
    case WM_MOUSEWHEEL: 
    case WM_HSCROLL:  
    case WM_VSCROLL:  
        {
            ::SendMessage( hwnd, WM_SETREDRAW, (WPARAM)FALSE, 0 );
 
            LRESULT lres = ::DefSubclassProc( hwnd, message, wParam, lParam );
 
            ::SendMessage( hwnd, WM_SETREDRAW, (WPARAM)TRUE, 0 );
 
            ::RedrawWindow( hwnd, NULL, NULL, 
                RDW_FRAME | RDW_INVALIDATE | RDW_UPDATENOW );
 
            return lres;
        }
    case WM_PAINT:
        {
            // usual stuff

            PAINTSTRUCT ps;
			POINT ptZero = { 0 };
            HDC hdc = BeginPaint( hwnd, &ps );
 
            // get client coordinates of parent and tree window

            RECT rcClient = {0}, rcParent = {0};
 
            GetClientRect( hwnd, &rcClient );
            GetClientRect( GetParent(hwnd), &rcParent );
 
			/* LdB these are used over and over save some time */
			SIZE ptSize;
			ptSize.cx = rcClient.right-rcClient.left;
			ptSize.cy = rcClient.bottom-rcClient.top;

			/** LdB killed a number of unneccessary steps **/
            // create helper DCs and bitmaps

            HDC memDC = CreateCompatibleDC(hdc),    //default tree paint
                finalDC = CreateCompatibleDC(hdc);  // parent's WM_PRINTCLIENT

            HBITMAP tempBmp,  // temp bitmap used to resize the memory DC's
                     bmpOld;  // needed for cleanup

            /****** get parent's background image *******/
 
			// Create a bitmap the width and height of parent
            tempBmp = CreateCompatibleBitmap( hdc, rcParent.right - rcParent.left,
                rcParent.bottom - rcParent.top );
            // Select tempBmp onto FinalDC to force size change on the DC
            bmpOld = (HBITMAP)SelectObject( finalDC, tempBmp );

			// Get WM_PRINTCLIENT to put the window image into our context FinalDC
            ::SendMessage( GetParent(hwnd), WM_PRINTCLIENT,(WPARAM)finalDC,
                (LPARAM)(PRF_CLIENT) );
 
			// Now we can dispose of the bitmap no longer needed
			DeleteObject(tempBmp);

			/****** capture default tree image *********/

			// Create a bitmap the width and height of client
			tempBmp = CreateCompatibleBitmap( hdc, ptSize.cx, ptSize.cy);
            // Select tempBmp onto memDC to force size change on the DC
			bmpOld = (HBITMAP)SelectObject( memDC, tempBmp);
			
			::SendMessage( hwnd, WM_PRINTCLIENT,(WPARAM)memDC, 
                (LPARAM)(PRF_CLIENT) );

			// Now we can dispose of the bitmap no longer needed
			DeleteObject(tempBmp);

 
            /***** map tree's coordinates to parent window *****/
            POINT ptTreeUL;
            ptTreeUL.x = rcClient.left;
            ptTreeUL.y = rcClient.top;
            ClientToScreen( hwnd, &ptTreeUL );
            ScreenToClient( GetParent(hwnd), &ptTreeUL );

			/*** LdB transfer the treeview DC onto finalDC excluding pink  *****/
			TransparentBlt(finalDC, ptTreeUL.x, ptTreeUL.y, ptSize.cx, ptSize.cy,
				memDC, 0, 0, ptSize.cx, ptSize.cy, RGB(0xFF, 0x00, 0xFF));

			/** LdB Now simply draw the completed finalDC to screen **/ 
			BitBlt( hdc, 0, 0,  ptSize.cx, ptSize.cy,
                finalDC, ptTreeUL.x, ptTreeUL.y, SRCCOPY );


           /***************** cleanup ******************/
            DeleteDC( memDC );
            DeleteDC( finalDC );
            EndPaint( hwnd, &ps );
        }
        return 0L;
    case WM_ERASEBKGND:
        return TRUE;
    case WM_NCDESTROY:
        ::RemoveWindowSubclass( hwnd, TreeProc, 0 );
        return ::DefSubclassProc( hwnd, message, wParam, lParam);
    }
    return ::DefSubclassProc( hwnd, message, wParam, lParam);
}
 
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
	switch(msg)
	{
	case WM_CREATE:
		{

			/*** LdB positions all set to zero as positioning set in WM_WINDOWPOSCHANGED etc **/
        	HWND TreeView = CreateWindowEx(0, WC_TREEVIEW, 
        			TEXT("Tree View"), WS_VISIBLE | WS_CHILD | WS_BORDER
				| TVS_HASBUTTONS | TVS_HASLINES | TVS_LINESATROOT, 
				0, 0, 0, 0,	hwnd, (HMENU)7000, hInst, NULL);
 
			/************ enable checkboxes **************/
 
			DWORD dwStyle = GetWindowLong( TreeView , GWL_STYLE);
			dwStyle |= TVS_CHECKBOXES;
			SetWindowLongPtr( TreeView , GWL_STYLE, dwStyle );
 
			/**** subclass it ****/
 
			SetWindowSubclass( TreeView, TreeProc, 0, 0 );
			
 
			/************ add image list ********/
 
			HIMAGELIST hImages = ImageList_Create( 16, 16, ILC_MASK, 1, 0 );
 
			// load system icon so we can dodge the deletion and rc.file
			HICON hiBG = reinterpret_cast<hicon>( LoadImage( 0, 
				MAKEINTRESOURCE(IDI_WARNING), 
                                IMAGE_ICON, 0, 0, LR_SHARED ) );
 
			ImageList_AddIcon( hImages, hiBG );
 
			TreeView_SetImageList( GetDlgItem( hwnd, 7000 ), hImages, 
                            TVSIL_NORMAL );

			/** LdB SET THE BACKGROUND BRIGHT PINK FOR TRANSPARENCY **/
			TreeView_SetBkColor(TreeView, RGB(0xFF, 0, 0xFF));
			/** LdB TURN OFF CLEARTYPE ON LISTVIEW SO WE CAN TRANSPARENT TEXT PROPERLY **/
			LOGFONT lf;
			HFONT Font, Newfont;
			Font = (HFONT) SendMessage(TreeView, WM_GETFONT, 0, 0);
			GetObject(Font, sizeof(lf), &lf);
			lf.lfQuality = NONANTIALIASED_QUALITY;
			Newfont = CreateFontIndirect(&lf);
			SendMessage(TreeView, WM_SETFONT, (WPARAM)Newfont, FALSE);

 
			/************ add items and subitems **********/
 
			// add root item

			TVINSERTSTRUCT tvis = {0};
 
			tvis.item.mask = TVIF_TEXT;
			tvis.item.pszText = L"This is root item";
			tvis.hInsertAfter = TVI_LAST;
			tvis.hParent = TVI_ROOT;
 
			HTREEITEM hRootItem = reinterpret_cast<htreeitem>( 
                            SendMessage( TreeView , TVM_INSERTITEM, 0, 
                            reinterpret_cast<lparam>( &tvis ) ) );
 
			// add subitems for the hTreeItem

			for( int i = 0; i < 15; i++ )
			{
				memset( &tvis, 0, sizeof(TVINSERTSTRUCT) );
 
				tvis.item.mask = TVIF_TEXT;
				tvis.item.pszText = L"This is subitem";
				tvis.hInsertAfter = TVI_LAST;
				tvis.hParent = hRootItem;
 
				SendMessage( TreeView , TVM_INSERTITEM, 0,
                                    reinterpret_cast<lparam>( &tvis ) );
			}
		}
		return 0L;
	case WM_NOTIFY:
		{
			
			if( ((LPNMHDR)lParam)->code == TVN_SELCHANGING )
			{
				InvalidateRect( ((LPNMHDR)lParam)->hwndFrom, 
                                    NULL, FALSE );
				break;
			}
 
			if( ((LPNMHDR)lParam)->code == TVN_ITEMEXPANDING )
			{
				InvalidateRect( ((LPNMHDR)lParam)->hwndFrom, 
                                    NULL, FALSE );
				break;
			}
		}
		break;
	case WM_PRINTCLIENT:
		{
			RECT r;
			GetClientRect( hwnd, &r );
 
			GradientTriangle( (HDC)wParam, r.right, r.bottom - r.top, 
				r.left, r.bottom - r.top,
				r.left, r.top,
				RGB( 0x0, 0x0, 0xFF ), 
                                RGB( 0xFF, 0xFF, 0x0 ) );
 
			GradientTriangle( (HDC)wParam, 
                                r.right, r.bottom - r.top, 
				r.right, r.top,
				r.left, r.top, 
				RGB( 0xFF, 0x0, 0x0 ), 
                                RGB( 0x0, 0xFF, 0x0 ) );
 
			return 0L;
		}
		break;

		/** LdB  **/
		// These next two messages are better to use rather than WM_MOVE/WM_SIZE.
		// Remember WM_MOVE/WM_SIZE are from 16bit windows. In 32bit windows the window
		// manager only sends these two messages and the DefWindowProc() handler actually
		// accepts them and converts them to WM_MOVE/WM_SIZE.
		// 
		// We accept this so we can scale our controls to the client size.
		case WM_WINDOWPOSCHANGING:
		case WM_WINDOWPOSCHANGED:
			{
				HDWP hDWP;
				RECT rc;
				
				// Create a deferred window handle.
				if(hDWP = BeginDeferWindowPos(1)){ // Deferring 1 child control at the moment
					GetClientRect(hwnd, &rc);
					
					hDWP = DeferWindowPos(hDWP, GetDlgItem(hwnd, 7000), NULL,
						50, 10, 200, 200, SWP_NOZORDER | SWP_NOREDRAW);

					// Resize all windows under the deferred window handled at the same time.
					EndDeferWindowPos(hDWP);
					
					// We told DeferWindowPos not to redraw the controls so we can redraw
					// them here all at once.
					RedrawWindow(hwnd, NULL, NULL, RDW_INVALIDATE | RDW_ALLCHILDREN | 
						RDW_ERASE | RDW_NOFRAME | RDW_UPDATENOW);
				}
			}
			return 0;
	case WM_ERASEBKGND:
		return 1L;
	case WM_PAINT:
		{
			PAINTSTRUCT ps;
			HDC hdc = BeginPaint( hwnd, &ps );
 
			RECT r;
			GetClientRect( hwnd, &r );
 
			GradientTriangle( hdc, r.right, 
                                r.bottom - r.top, 
				r.left, r.bottom - r.top,
				r.left, r.top,
				RGB( 0x0, 0x0, 0xFF ), 
                                RGB( 0xFF, 0xFF, 0x0 ) );
 
			GradientTriangle( hdc, r.right, 
                                r.bottom - r.top, 
				r.right, r.top,
				r.left, r.top, 
				RGB( 0xFF, 0x0, 0x0 ), 
                                RGB( 0x0, 0xFF, 0x0 ) );
 
			EndPaint( hwnd, &ps );
		}
		return 0L;
	case WM_CLOSE:
		{
			// delete tree's imagelist
			HIMAGELIST hImages = reinterpret_cast<himagelist>( 
                            SendMessage( GetDlgItem( hwnd, 7000 ), 
                            TVM_GETIMAGELIST, 0, 0 ) );
 
			ImageList_Destroy(hImages);
 
			DestroyWindow(hwnd);
		}
		break;
	case WM_DESTROY:
		PostQuitMessage(0);
		break;
	default:
		return DefWindowProc(hwnd, msg, wParam, lParam);
	}
	return 0;
}
 
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
	WNDCLASSEX wc;
	HWND hwnd;
	MSG Msg;
 
	// initialize common controls
	INITCOMMONCONTROLSEX iccex;
	iccex.dwSize = sizeof(INITCOMMONCONTROLSEX);
	iccex.dwICC = ICC_LISTVIEW_CLASSES | ICC_UPDOWN_CLASS | ICC_STANDARD_CLASSES ;
	InitCommonControlsEx(&iccex);
 
	hInst = hInstance;
 
	wc.cbSize		 = sizeof(WNDCLASSEX);
	wc.style		 = 0;
	wc.lpfnWndProc	 = WndProc;
	wc.cbClsExtra	 = 0;
	wc.cbWndExtra	 = 0;
	wc.hInstance	 = hInstance;
	wc.hIcon		 = LoadIcon(NULL, IDI_APPLICATION);
	wc.hCursor		 = LoadCursor(NULL, IDC_ARROW);
	wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
	wc.lpszMenuName  = NULL;
	wc.lpszClassName = L"Transparent TreeView";
	wc.hIconSm		 = LoadIcon(NULL, IDI_APPLICATION);
 
	if(!RegisterClassEx(&wc))
	{
		MessageBox(NULL, L"Window Registration Failed!", L"Error!", 
			MB_ICONEXCLAMATION | MB_OK);
		return 0;
	}
 
	hwnd = CreateWindowEx( WS_EX_CLIENTEDGE, L"Transparent TreeView",
		L"The title of my window", 
                WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 
		300, 300, NULL, NULL, hInstance, NULL);
 
	if(hwnd == NULL)
	{
		MessageBox(NULL, L"Window Creation Failed!", L"Error!", 
                    MB_ICONEXCLAMATION | MB_OK);
		return 0;
	}
 
	ShowWindow(hwnd, nCmdShow);
	UpdateWindow(hwnd);
 
	while(GetMessage(&Msg, NULL, 0, 0) > 0)
	{
		TranslateMessage(&Msg);
		DispatchMessage(&Msg);
	}
	return Msg.wParam;
}
 
Share this answer
 
v3
Comments
AlwaysLearningNewStuff 6-May-14 7:18am    
I came to the same conclusion ( to disable ClearType by setting my own font for treeview ) but I used ANTIALIASED_QUALITY to get better result.

I guess that this is the only way since no one can suggest solution for ClearType. The difference is slight so I think that this will do.

So many useful things I saw in your code. WM_WINDOWPOSCHANGED is something new for me since all tutorials use WM_SIZE. In my main project I have multiple controls. Can you point me in the right direction with implementing WM_POSCHANGED so I can ditch the WM_SIZE?

Someone suggested me to "write my own treeview" but being inexperienced I do not know what that means. Can you explain me concept of doing it so I can search for code examples/tutorials?

And why can't I still download files from your article?

Thanks again, I have officially accepted your answer and voted 5 stars.

One last, but important question: Shouldn't I delete the font after I finish using it? The way you coded for now, it seems like a memory leak from my point of view.

Best regards.
leon de boer 6-May-14 9:10am    
WM_MOVE and WM_SIZE are for 16 bit windows code and you are on Visual Studio you can't build a 16 bit app.

The font will leak memory in it's current form I created a font through create font indirect so it needs to be deleted. Do you know the easy way to do that by property attachment and cleaning it up at WM_DESTROY or do you need me to show you?

In it's current form you probably need to pick off WM_SETFONT and do the same check to make sure they don't give you a ClearType font so taht is another bit of house keeping.

I actually haven't given up on ClearType it is solvable I just needed more time and didn't want to hold you up so I will post you a more final solution.

The problem with downloading the code must be something with you and code project I will put it up on a publci FTP site when I get a moment and give you the link.
AlwaysLearningNewStuff 6-May-14 16:22pm    
Being inexperienced, I will stick with WM_SIZE in my original code until I understand how to apply WM_WINDOWPOSCHANGED.

As for font leaking, I make it a static variable and then delete it in WM_DESTROY ( again my inexperience kicks in ).

I can wait, so if you manage to solve ClearType please post your solution, I will keep trying myself.

As for article files, well I can download files from other articles but if you can upload them to FTP that would be great.

Thank you so much for finding time and good will to help a novice.

Best regards.

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900