Re: Transparent tracker control

"tsehonkit" <tsehonkit-/[email protected]>
Newsgroups gmane.comp.windows.wtl
Message-ID <[email protected]>
Thanks for the help.

I found that no message WM_CTLCOLORSTATIC can be recieved from the control. No luck to manage this message. I studied some transparent slider code in code project but none of them using WTL. 

From those information, I finally make a simple transparent slider control in WTL. It does not have much features and may be some mistake inside I guess. As I use many WTL code contributed form others, here is my code.

Please comment!

class CThemedSliderCtrl : 
		public CWindowImpl<CThemedSliderCtrl, CTrackBarCtrl>,
		public CCustomDraw< CThemedSliderCtrl >
{
protected:
	CWindow	m_wndThemeParent;
	CBrush	m_brBack;
	COLORREF m_ChannelColor;
	COLORREF m_ChannelColorShadow;
	COLORREF m_ChannelColorHilite;
	CRect ChannelRc;				// channel rectangle
	CRect TicsRc;					// Ticks rectangle
	DWORD dwStyle;					// store slider style
	int TicsFreq;

public:

	BEGIN_MSG_MAP(CThemedSliderCtrl)
		MESSAGE_HANDLER(WM_MOVE, Refresh)
		CHAIN_MSG_MAP_ALT(CCustomDraw< CThemedSliderCtrl >, 1)
		REFLECT_NOTIFICATIONS()
	END_MSG_MAP()

	void SetTicFreq(int nFreq)
	{
		ATLASSERT(::IsWindow(m_hWnd));
		::SendMessage(m_hWnd, TBM_SETTICFREQ, nFreq, 0L);
		TicsFreq = nFreq;	// need to remember to take care tick drawing
	}

	LRESULT Refresh(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled)
	{
		// slider control do not repaint itself unless it take focus
		// So call this routine to repaint the control when the control is moved
		int minvalue, maxvalue;
		GetRange(minvalue, maxvalue);
		SetRange(minvalue, maxvalue, TRUE);
		return TRUE;
	}

	DWORD OnPrePaint(int idCtrl, LPNMCUSTOMDRAW lpNMCustomDraw)		// Before the painting cycle begins.
	{
		// call item draw routine
		return CDRF_NOTIFYITEMDRAW;
	}

	DWORD OnPreErase(int idCtrl, LPNMCUSTOMDRAW lpNMCustomDraw)		// Before the erasing cycle begins.
	{
		return CDRF_DODEFAULT;
	}

	DWORD OnPostPaint(int idCtrl, LPNMCUSTOMDRAW lpNMCustomDraw)	// After the painting cycle is complete.
	{
		return CDRF_DODEFAULT; 
	}

	DWORD OnPostErase(int idCtrl, LPNMCUSTOMDRAW lpNMCustomDraw)	// After the erasing cycle is complete. 
	{
		return CDRF_DODEFAULT;
	}

	DWORD OnItemPrePaint(int idCtrl, LPNMCUSTOMDRAW lpNMCustomDraw)
	{
		CDCHandle dc = lpNMCustomDraw->hdc;
		CRect rcPaint = lpNMCustomDraw->rc;
		UINT itemSpec = lpNMCustomDraw->dwItemSpec;
		UINT itemState = lpNMCustomDraw->uItemState;
		dwStyle = GetStyle();

		switch (itemSpec)
		{
		case TBCD_CHANNEL:
			ChannelRc = rcPaint;
			return CDRF_DODEFAULT; break;
		case TBCD_TICS:
			return CDRF_DODEFAULT; break;
		case TBCD_THUMB: 
			DrawSlider(dc, rcPaint);		// we draw our own background 
			return CDRF_DODEFAULT; break;	// we keep the orginal thumb drawing
		default: return CDRF_DODEFAULT;
		}
	}

	DWORD OnItemPostPaint(int idCtrl, LPNMCUSTOMDRAW lpNMCustomDraw)
	{
		CDCHandle dc = lpNMCustomDraw->hdc;
		CRect rcPaint = lpNMCustomDraw->rc;
		UINT itemSpec = lpNMCustomDraw->dwItemSpec;
		UINT itemState = lpNMCustomDraw->uItemState;

		switch (itemSpec) 
		{ 
		case TBCD_CHANNEL:
			return CDRF_DODEFAULT; break;
		case TBCD_TICS:	 
			return CDRF_DODEFAULT; break;
		case TBCD_THUMB:
			return CDRF_DODEFAULT; break;
		default: return CDRF_DODEFAULT;
		}; 
	}

	DWORD OnItemPreErase(int idCtrl, LPNMCUSTOMDRAW lpNMCustomDraw)
	{
		return CDRF_DODEFAULT;
	}

	DWORD OnItemPostErase(int idCtrl, LPNMCUSTOMDRAW lpNMCustomDraw)
	{
		return CDRF_DODEFAULT;
	}

	DWORD OnSubItemPrePaint(int idCtrl, LPNMCUSTOMDRAW lpNMCustomDraw)
	{
		return CDRF_DODEFAULT;
	}

	BOOL SubclassWindow(HWND hWnd)
	{
		ATLASSERT(m_hWnd==NULL);
		ATLASSERT(::IsWindow(hWnd));
		BOOL bRet = CWindowImpl<CThemedSliderCtrl, CTrackBarCtrl>::SubclassWindow(hWnd);

		if (bRet) 
		{
			m_wndThemeParent = GetTopLevelParent();
			SetLineColor(RGB(200,200,200));			// set default color to light gray
			TicsFreq = 1;
		}
		return bRet;
	}

	COLORREF SetLineColor (COLORREF ch)
	{
		COLORREF oldch = m_ChannelColor;
		m_ChannelColor = ch;
		m_ChannelColorHilite = ::ColorAdjustLuma( ch, 500, TRUE );	// increase by 50%
		m_ChannelColorShadow = ::ColorAdjustLuma( ch, -210, TRUE );	// decrease by 33.3%
		return oldch;
	}

	void DrawSlider(CDCHandle pDC, CRect rc)
	{
		CRect rcClient;
		GetClientRect(&rcClient);

		CDC dc;
		dc.CreateCompatibleDC(pDC);
		CBitmap dcBmp;
		dcBmp.CreateCompatibleBitmap(pDC, rcClient.Width(), rcClient.Height());	// create from pDC
		HBITMAP oldbm = (HBITMAP)::SelectObject(dc, dcBmp);

		// draw background get from parent 
		// background code copy from viksoe.dk
		m_brBack = AtlGetBackgroundBrush(m_hWnd, m_wndThemeParent);
		HBRUSH hOldBrush = dc.SelectBrush(m_brBack);
		dc.PatBlt(rcClient.left, rcClient.top, rcClient.Width(), rcClient.Height(), PATCOPY);

		// draw channel
		RECT rrc = {ChannelRc.left + 1, ChannelRc.top + 1, ChannelRc.right - 1, ChannelRc.bottom - 1}; 
		dc.Draw3dRect(&rrc, m_ChannelColorHilite, m_ChannelColorShadow);
		//dc.Draw3dRect(&ChannelRc, m_ChannelColorShadow, m_ChannelColorHilite);

		// draw tics info
		// TBS_RIGHT, TBS_BOTTOM and TBS_HORZ are all defined as 0x0000, so avoid testing on them 
		if( !(dwStyle & TBS_NOTICKS) )
		{
			int tickcount = GetNumTics();
			RECT tickloc;
			int i;

			if (dwStyle & TBS_VERT)	// draw vertical ticks
			{	
				// define vertical drawing rectangle
				TicsRc.top = ChannelRc.top + rc.Height()/2;
				TicsRc.bottom = ChannelRc.bottom - rc.Height()/2;
				TicsRc.left = rc.left - 7;
				TicsRc.right = rc.right + 7;

				bool printleft = false, printright = false;
				if (dwStyle & TBS_BOTH)
				{
					printleft = true;
					printright = true;
				}
				else
				{
					if (dwStyle & TBS_LEFT)
					{
						printleft = true;
						printright = false;
					}
					else
					{
						printleft = false;
						printright = true;
					}
				}

				// draw first tick and last tick
				if (printleft)
				{
					tickloc.left = TicsRc.left;
					tickloc.top = TicsRc.top - 1;
					tickloc.right = TicsRc.left + 5;
					tickloc.bottom = TicsRc.top + 1;
					dc.Draw3dRect(&tickloc, m_ChannelColorHilite, m_ChannelColorShadow);

					tickloc.left = TicsRc.left;
					tickloc.top = TicsRc.bottom - 1;
					tickloc.right = TicsRc.left + 5;
					tickloc.bottom = TicsRc.bottom + 1;
					dc.Draw3dRect(&tickloc, m_ChannelColorHilite, m_ChannelColorShadow);
				}

				// draw first tick and last tick
				if (printright)
				{
					tickloc.left = TicsRc.right - 5;
					tickloc.top = TicsRc.top - 1;
					tickloc.right = TicsRc.right;
					tickloc.bottom =TicsRc.top + 1;
					dc.Draw3dRect(&tickloc, m_ChannelColorHilite, m_ChannelColorShadow);

					tickloc.left = TicsRc.right - 5;
					tickloc.top = TicsRc.bottom - 1;
					tickloc.right = TicsRc.right;
					tickloc.bottom = TicsRc.bottom + 1;
					dc.Draw3dRect(&tickloc, m_ChannelColorHilite, m_ChannelColorShadow);

				}

				// draw ticks between
				for ( i = 0; i < tickcount - 2; i++)
				{
					if (printleft)
					{
						tickloc.left = TicsRc.left;
						tickloc.top = GetTicPos(TicsFreq * (i + 1) - 1) - 1;		// take care of SetTicFreq
						tickloc.right = TicsRc.left + 5;
						tickloc.bottom = GetTicPos(TicsFreq * (i + 1) - 1) + 1;
						dc.Draw3dRect(&tickloc, m_ChannelColorHilite, m_ChannelColorShadow);
					}
					if (printright)
					{
						tickloc.left = TicsRc.right - 5;
						tickloc.top = GetTicPos(TicsFreq * (i + 1) - 1) - 1;
						tickloc.right = TicsRc.right;
						tickloc.bottom = GetTicPos(TicsFreq * (i + 1) - 1) + 1;
						dc.Draw3dRect(&tickloc, m_ChannelColorHilite, m_ChannelColorShadow);
					}
				}
			}
			else // draw horizontal ticks
			{	
				// define horizontal drawing rectangle
				TicsRc.left = ChannelRc.left + rc.Width()/2;
				TicsRc.right = ChannelRc.right - rc.Width()/2;
				TicsRc.top = rc.top - 7;
				TicsRc.bottom = rc.bottom + 7;

				bool printtop = false, printbtm = false;
				if (dwStyle & TBS_BOTH)
				{
					printtop = true;
					printbtm = true;
				}
				else
				{
					if (dwStyle & TBS_TOP)
					{
						printtop = true;
						printbtm = false;
					}
					else
					{
						printtop = false;
						printbtm = true;
					}
				}

				// draw first tick and last tick
				if (printtop)
				{
					tickloc.left = TicsRc.left - 1;
					tickloc.top = TicsRc.top;
					tickloc.right = TicsRc.left + 1;
					tickloc.bottom = TicsRc.top + 5;
					dc.Draw3dRect(&tickloc, m_ChannelColorHilite, m_ChannelColorShadow);

					tickloc.left = TicsRc.right - 1;
					tickloc.top = TicsRc.top;
					tickloc.right = TicsRc.right + 1;
					tickloc.bottom = TicsRc.top + 5;
					dc.Draw3dRect(&tickloc, m_ChannelColorHilite, m_ChannelColorShadow);
				}

				// draw first tick and last tick
				if (printbtm)
				{
					tickloc.left = TicsRc.left - 1;
					tickloc.top = TicsRc.bottom - 5;
					tickloc.right = TicsRc.left + 1;
					tickloc.bottom = TicsRc.bottom;
					dc.Draw3dRect(&tickloc, m_ChannelColorHilite, m_ChannelColorShadow);

					tickloc.left = TicsRc.right - 1;
					tickloc.top = TicsRc.bottom - 5;
					tickloc.right = TicsRc.right + 1;
					tickloc.bottom = TicsRc.bottom;
					dc.Draw3dRect(&tickloc, m_ChannelColorHilite, m_ChannelColorShadow);
				}

				// draw ticks between
				for ( i = 0; i < tickcount - 2; i++)
				{
					if (printtop)
					{
						tickloc.left = GetTicPos(TicsFreq * (i + 1) - 1) - 1;
						tickloc.top = TicsRc.top;
						tickloc.right = GetTicPos(TicsFreq * (i + 1) - 1) + 1;
						tickloc.bottom =TicsRc.top + 5;
						dc.Draw3dRect(&tickloc, m_ChannelColorHilite, m_ChannelColorShadow);
					}
					if (printbtm)
					{
						tickloc.left = GetTicPos(TicsFreq * (i + 1) - 1) - 1;
						tickloc.top = TicsRc.bottom - 5;
						tickloc.right = GetTicPos(TicsFreq * (i + 1) - 1) + 1;
						tickloc.bottom =TicsRc.bottom;
						dc.Draw3dRect(&tickloc, m_ChannelColorHilite, m_ChannelColorShadow);
					}
				}
			}
		}

		pDC.BitBlt(0, 0, rcClient.Width(), rcClient.Height(), dc, 0, 0, SRCCOPY);
		dc.SelectBrush(hOldBrush);
	}
};

Thanks

Kit

--- In [email protected], "domehead100" <domehead100@...> wrote:
>
> I think your parent (m_wndThemeParent) should be obtained via GetParent() rather than GetTopLevelParent().
> 
> I have no idea if this would work for what you need, but one way to get the parent to draw the background is to create a memory DC and pass that to the parent via WM_PAINT or WM_PRINT or WM_PRINTCLIENT.  The parent should then draw using the DC that you provide.
> 
> It's possible that the parent's WS_CLIPCHILDREN setting might affect how this works.
> 
> Others probably have better approaches; I'm not very familiar with "transparent" controls.
> 
> Maybe something like this:
> 
> // Draw the background from the parent
> CWindow wndParent(GetParent());
> wndParent.SetRedraw(FALSE);
> if(wndParent.IsWindow())
> {
> 	CMemoryDC dcMemory(dc.m_hDC, m_rcClient);
> 	CPoint ptOrigin(0);
> 	MapWindowPoints(wndParent, &ptOrigin, 1);
> 	dcMemory.OffsetWindowOrg(ptOrigin.x, ptOrigin.y, &ptOrigin);
> 	wndParent.SendMessage(WM_PAINT, (WPARAM)dcMemory.m_hDC);
> 	dcMemory.SetWindowOrg(ptOrigin);
> }
> wndParent.SetRedraw(TRUE);
> 
> Best of luck with this :o)
> 
> ~Mike
> 
> 
> --- In [email protected], "tsehonkit" <tsehonkit@> wrote:
> >
> > Thanks for your advise!
> > I tried this one but no effect!
> > 
> > Here is the code I used.
> > If the background is ok, I will try to custom draw the slider. So, there are some code preparing for these.
> > 
> > class CThemedSliderCtrl : 
> > 		public CWindowImpl<CThemedSliderCtrl, CTrackBarCtrl>,
> > 		public CCustomDraw< CThemedSliderCtrl >
> > {
> > public:
> > 	CWindow m_wndThemeParent;
> > 	CBrush m_brBack;
> > 
> > 	BEGIN_MSG_MAP(CThemedSliderCtrl)
> > 		MESSAGE_HANDLER(WM_CTLCOLORSTATIC, OnBackground)
> > 		CHAIN_MSG_MAP_ALT(CCustomDraw< CThemedSliderCtrl >, 1)
> > 	END_MSG_MAP()
> > 
> > 	DWORD OnPrePaint(int idCtrl, LPNMCUSTOMDRAW lpNMCustomDraw)
> > 	{
> > 		return CDRF_NOTIFYITEMDRAW | CDRF_NOTIFYPOSTPAINT;
> > 	}
> > 
> > 	DWORD OnPreErase(int idCtrl, LPNMCUSTOMDRAW lpNMCustomDraw)
> > 	{
> > 		return CDRF_DODEFAULT;
> > 	}
> > 
> > 	DWORD OnPostPaint(int idCtrl, LPNMCUSTOMDRAW lpNMCustomDraw)
> > 	{
> > 		return CDRF_DODEFAULT; 
> > 	}
> > 
> > 	DWORD OnPostErase(int idCtrl, LPNMCUSTOMDRAW lpNMCustomDraw)
> > 	{
> > 		return CDRF_DODEFAULT;
> > 	}
> > 
> > 	DWORD OnItemPrePaint(int idCtrl, LPNMCUSTOMDRAW lpNMCustomDraw)
> > 	{
> > 		CDCHandle dc = lpNMCustomDraw->hdc;
> > 		CRect rcPaint = lpNMCustomDraw->rc;
> > 		UINT itemSpec = lpNMCustomDraw->dwItemSpec;
> > 		UINT itemState = lpNMCustomDraw->uItemState;
> > 
> > 		switch (itemSpec)
> > 		{
> > 		case TBCD_CHANNEL:
> > 			return CDRF_DODEFAULT; break;
> > 		case TBCD_TICS: 
> > 			return CDRF_DODEFAULT; break;
> > 		case TBCD_THUMB: 
> > 			return CDRF_DODEFAULT; break;
> > 		default: return CDRF_DODEFAULT;
> > 		}
> > 	}
> > 
> > 	DWORD OnItemPostPaint(int idCtrl, LPNMCUSTOMDRAW lpNMCustomDraw)
> > 	{
> > 		CDCHandle dc = lpNMCustomDraw->hdc;
> > 		CRect rcPaint = lpNMCustomDraw->rc;
> > 		UINT itemSpec = lpNMCustomDraw->dwItemSpec;
> > 		UINT itemState = lpNMCustomDraw->uItemState;
> > 
> > 		switch (itemSpec) 
> > 		{ 
> > 		case TBCD_CHANNEL:
> > 			return CDRF_DODEFAULT; break;
> > 		case TBCD_TICS:	 
> > 			return CDRF_DODEFAULT; break;
> > 		case TBCD_THUMB:	
> > 			return CDRF_DODEFAULT; break;
> > 		default: return CDRF_DODEFAULT;
> > 		}; 
> > 	}
> > 
> > 	DWORD OnItemPreErase(int idCtrl, LPNMCUSTOMDRAW lpNMCustomDraw)
> > 	{
> > 		return CDRF_DODEFAULT;
> > 	}
> > 
> > 	DWORD OnItemPostErase(int idCtrl, LPNMCUSTOMDRAW lpNMCustomDraw)
> > 	{
> > 		return CDRF_DODEFAULT;
> > 	}
> > 
> > 	DWORD OnSubItemPrePaint(int idCtrl, LPNMCUSTOMDRAW lpNMCustomDraw)
> > 	{
> > 		return CDRF_DODEFAULT;
> > 	}
> > 
> > 	BOOL SubclassWindow(HWND hWnd)
> > 	{
> > 		ATLASSERT(m_hWnd==NULL);
> > 		ATLASSERT(::IsWindow(hWnd));
> > 		BOOL bRet = CWindowImpl<CThemedSliderCtrl, CTrackBarCtrl>::SubclassWindow(hWnd);
> > 
> > 		if (bRet) m_wndThemeParent = GetTopLevelParent();
> > 		return bRet;
> > 	}
> > 
> > 	LRESULT OnBackground(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& /*bHandled*/)
> > 	{
> > 		CDCHandle dc((HDC)wParam);
> > 		CRect rcPaint;
> > 		GetClientRect(&rcPaint);
> > 
> > 		// Paint background from parent
> > 		if( m_brBack.IsNull() ) m_brBack = AtlGetBackgroundBrush(m_hWnd, m_wndThemeParent);
> > 		HBRUSH hOldBrush = dc.SelectBrush(m_brBack);
> > 		dc.PatBlt(rcPaint.left, rcPaint.top, rcPaint.Width(), rcPaint.Height(), PATCOPY);
> > 		dc.SelectBrush(hOldBrush);
> > 		return true;
> > 	}
> > };
> > 
> > 
> > 
> > 
> > --- In [email protected], Timo Kunze <TKunze71216@> wrote:
> > >
> > > Handle WM_CTLCOLORSTATIC. I doubt you can use this message to make the
> > > control really transparent, but you could use it to create a pattern
> > > brush out of the things behind the track bar and return this brush as
> > > the background brush.
> > > 
> > > Timo
> > > -- 
> > > www.TimoSoft-Software.de - Unicode controls for VB6
> > > "Those who sacrifice freedom for safety deserve neither."
> > > "Demokratie ist per Definition unsicher. Ihr Schutz entsteht aus der
> > > &#65533;berzeugung, dass die demokratischen Kräfte überwiegen und sich &#65533;&#65533; auf
> > > demokratischem Wege &#65533;&#65533; durchsetzen."
> > >
> >
>




------------------------------------

Yahoo! Groups Links

<*> To visit your group on the web, go to:
    http://groups.yahoo.com/group/wtl/

<*> Your email settings:
    Individual Email | Traditional

<*> To change settings online go to:
    http://groups.yahoo.com/group/wtl/join
    (Yahoo! ID required)

<*> To change settings via email:
    mailto:[email protected] 
    mailto:[email protected]

<*> To unsubscribe from this group, send an email to:
    [email protected]

<*> Your use of Yahoo! Groups is subject to:
    http://docs.yahoo.com/info/terms/
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.