Click here to Skip to main content
15,895,142 members
Articles / Desktop Programming / MFC

VCOMBOBX in MFC - A Virtual Combo Box Control

Rate me:
Please Sign up or sign in to vote.
4.91/5 (33 votes)
4 Jan 2011CPOL8 min read 72.1K   11.3K   108  
A MFC based virtual combo box
/* 
 * Kenny Liu
 * http://www.codeproject.com/Members/yonken
 *
 * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED
 * OR IMPLIED.  ANY USE IS AT YOUR OWN RISK.
 *
 * Permission is hereby granted to use or copy this program
 * for any purpose,  provided the above notices are retained on all copies.
 * Permission to modify the code and to distribute modified code is granted,
 * provided the above notices are retained, and a notice that the code was
 * modified is included with the above copyright notice.
 */

#ifndef __CUSTOMDRAWUTILS_H__
#define __CUSTOMDRAWUTILS_H__

/*----------------------------------------------------------------------------*/
/* Global control
/*----------------------------------------------------------------------------*/
#define CUSTOMDRAW_THEME_BACKGROUND

// Color for item
#define RGB_ITEM_SELECTED_FILL_COLOR1			RGB(235, 244, 254)
#define RGB_ITEM_SELECTED_FILL_COLOR2			RGB(207, 228, 254)

#define RGB_ITEM_SELFOCUS_FILL_COLOR1			RGB(220, 235, 252)
#define RGB_ITEM_SELFOCUS_FILL_COLOR2			RGB(193, 219, 252)
#define RGB_ITEM_SELFOCUS_OUTER_BORDER_COLOR	RGB(125, 162, 206)
#define RGB_ITEM_SELFOCUS_INNER_BORDER_COLOR	RGB(235, 244, 253)

#define RGB_ITEM_SELNOFOCUS_FILL_COLOR1			RGB(248, 248, 248)
#define RGB_ITEM_SELNOFOCUS_FILL_COLOR2			RGB(229, 229, 229)
#define RGB_ITEM_SELNOFOCUS_OUTER_BORDER_COLOR	RGB(217, 217, 217)
#define RGB_ITEM_SELNOFOCUS_INNER_BORDER_COLOR	RGB(240, 240, 240)

#define RGB_ITEM_HOT_FILL_COLOR1				RGB(250, 251, 253)
#define RGB_ITEM_HOT_FILL_COLOR2				RGB(235, 243, 253)
#define RGB_ITEM_HOT_OUTER_BORDER_COLOR			RGB(184, 214, 251)
#define RGB_ITEM_HOT_INNER_BORDER_COLOR			RGB(252, 243, 254)

// Color for tooltip
#define RGB_TOOLTIP_FILL_COLOR1					RGB(255, 255, 255)
#define RGB_TOOLTIP_FILL_COLOR2					RGB(201, 217, 239)
#define RGB_TOOLTIP_TEXT_COLOR					RGB(0x4C,0x4C,0x4C)		// ::GetSysColor(COLOR_INFOTEXT)
#define RGB_TOOLTIP_BORDER_COLOR				RGB(107, 107, 107)		// ::GetSysColor(COLOR_INFOTEXT)

// Color for button
#define RGB_BUTTON_NORMAL_FILL_COLOR1			RGB(242,242,242)
#define RGB_BUTTON_NORMAL_FILL_COLOR2			RGB(235,235,235)
#define RGB_BUTTON_NORMAL_FILL_COLOR3			RGB(221,221,221)
#define RGB_BUTTON_NORMAL_FILL_COLOR4			RGB(207,207,207)

#define RGB_BUTTON_HOT_FILL_COLOR1				RGB(234,246,253)
#define RGB_BUTTON_HOT_FILL_COLOR2				RGB(217,240,252)
#define RGB_BUTTON_HOT_FILL_COLOR3				RGB(190,230,253)
#define RGB_BUTTON_HOT_FILL_COLOR4				RGB(167,217,245)

#define RGB_BUTTON_PUSHED_FILL_COLOR1			RGB(229,244,252)
#define RGB_BUTTON_PUSHED_FILL_COLOR2			RGB(196,229,249)
#define RGB_BUTTON_PUSHED_FILL_COLOR3			RGB(152,209,239)
#define RGB_BUTTON_PUSHED_FILL_COLOR4			RGB(109,182,221)

#define BUTTON_FIRST_HALF_PERCENTAGE			47

// Color for header
#define RGB_HEADER_NORMAL_FILL_COLOR1			RGB(255,255,255)
#define RGB_HEADER_NORMAL_FILL_COLOR2			RGB(255,255,255)
#define RGB_HEADER_NORMAL_FILL_COLOR3			RGB(247,248,250)
#define RGB_HEADER_NORMAL_FILL_COLOR4			RGB(241,242,244)

#define RGB_HEADER_HOT_FILL_COLOR1				RGB(227,247,255)
#define RGB_HEADER_HOT_FILL_COLOR2				RGB(227,247,255)
#define RGB_HEADER_HOT_FILL_COLOR3				RGB(189,237,255)
#define RGB_HEADER_HOT_FILL_COLOR4				RGB(183,231,251)

#define RGB_HEADER_PUSHED_FILL_COLOR1			RGB(188,228,249)
#define RGB_HEADER_PUSHED_FILL_COLOR2			RGB(227,247,255)
#define RGB_HEADER_PUSHED_FILL_COLOR3			RGB(141,214,247)
#define RGB_HEADER_PUSHED_FILL_COLOR4			RGB(138,209,245)

#define RGB_HEADER_SORT_FILL_COLOR1				RGB(242,249,252)
#define RGB_HEADER_SORT_FILL_COLOR2				RGB(242,249,252)
#define RGB_HEADER_SORT_FILL_COLOR3				RGB(225,241,249)
#define RGB_HEADER_SORT_FILL_COLOR4				RGB(216,236,246)

#define HEADER_FIRST_HALF_PERCENTAGE			41

#define RGB_COMBO_BORDER_NORMAL					RGB(190, 190, 190)
#define RGB_COMBO_BORDER_HILIGHT				RGB(60, 127, 177)

#define RGB_TRANSPARENT							RGB(255,0,255)

#define RGB_SELECTION_BORDER					::GetSysColor(COLOR_HIGHLIGHT)

#define ALPHA_SELECTION_FILL					70

//#define IMPLEMENT_BITMAP_TRANSITION	// TODO


#ifdef CUSTOMDRAW_GRADIENT
BOOL FillGradient(HDC hDC, PRECT pRect, COLORREF c1, COLORREF c2, int nGradientType = GFT_TOP_BOTTOM, BYTE byAlpha = 255);

typedef struct tagCOLORS4
{
	COLORREF c1;
	COLORREF c2;
	COLORREF c3;
	COLORREF c4;
} COLORS4, *PCOLORS4;

typedef const COLORS4	*PCCOLORS4;

void Fill4ColorsGradient(HDC hDC, CRect rect, 
						 COLORREF colorStart1, COLORREF colorFinish1, 
						 COLORREF colorStart2, COLORREF colorFinish2,
						 BOOL bHorz = TRUE,
						 int nPercentage = 50,
						 BYTE byAlpha = 255);

void Fill4ColorsGradient(HDC hDC, CRect rect, 
						 PCCOLORS4 pColors,
						 BOOL bHorz = TRUE,
						 int nPercentage = 50,
						 BYTE byAlpha = 255);

BOOL SimpleGradientRoundRect(HDC hDC, PRECT pRect, COLORREF c1, COLORREF c2, COLORREF clrBorder = CLR_INVALID, int nGradientType = GFT_TOP_BOTTOM, BYTE byAlpha = 255);

void DrawRoundRectItem(CDC* pDC, CRect rect, COLORREF clrFillColor1, COLORREF clrFillColor2, COLORREF clrInnerBorder, COLORREF clrOuterBorder, BYTE byAlpha = 255);

enum CDTBT
{
	CDTBT_PUSHBUTTON,
	CDTBT_HEADER,
	CDTBT_COMBODOWNBUTTON,
	CDTBT_COMBOBUTTON,
};

enum CDSTATEID
{
	CDSTATEID_DISABLED,
	CDSTATEID_HOT,
	CDSTATEID_NORMAL,
	CDSTATEID_PRESSED,
	CDSTATEID_MAX
};

void DrawDropDownArrow(HDC hDC, const RECT* pRect);
HRESULT CustomDrawThemeBackground(HWND hwnd, HDC hDC, CDTBT type, CDSTATEID state, const RECT* pRect, const RECT* pClipRect = NULL);

#endif // CUSTOMDRAW_GRADIENT

void DrawSortArrow(CDC* pDC, CRect rectArrow, BOOL bIsUp);
void DrawSimpleRoundRectBorder( CDC* pDC, RECT rect, COLORREF clrLine, bool bForClipping = false );


void DrawAlphaBlendedSelection( CDC& dc, CRect rcPaint, CRect rcSelBox, COLORREF clrSelFill, COLORREF clrSelBorder );


#ifndef PW_CLIENTONLY
	#define PW_CLIENTONLY           0x00000001
#endif // PW_CLIENTONLY

BOOL OPrintWindow(HWND hwnd, HDC hdcBlt, UINT nFlags = 0);

#ifndef AW_BLEND
	#define AW_HOR_POSITIVE             0x00000001
	#define AW_HOR_NEGATIVE             0x00000002
	#define AW_VER_POSITIVE             0x00000004
	#define AW_VER_NEGATIVE             0x00000008
	#define AW_CENTER                   0x00000010
	#define AW_HIDE                     0x00010000
	#define AW_ACTIVATE                 0x00020000
	#define AW_SLIDE                    0x00040000
	#define AW_BLEND                    0x00080000
#endif // AW_BLEND

#if _MSC_VER < 1400	// MS VC++ 8.0
	BOOL OAnimateWindow(HWND hwnd, DWORD dwTime, DWORD dwFlags);
#else
	#define OAnimateWindow	AnimateWindow
#endif	//  _MSC_VER < 1400

BOOL ORandomAnimateWindow(HWND hwnd, DWORD dwTIme);

// References:
// http://www.codeproject.com/KB/list/CGridListCtrlEx.aspx
// http://msdn.microsoft.com/en-us/library/dd373487(VS.85).aspx

bool CheckOSVersion(WORD requestOS);
bool IsCommonControlsEnabled();
bool IsThemeEnabled();

#ifndef VSCLASS_BUTTON
	#define VSCLASS_BUTTON			L"BUTTON"
#endif // VSCLASS_BUTTON

#ifndef VSCLASS_COMBOBOX
	#define VSCLASS_COMBOBOX		L"COMBOBOX"
#endif // VSCLASS_COMBOBOX

#ifndef VSCLASS_HEADER
	#define VSCLASS_HEADER			L"HEADER"
#endif // VSCLASS_HEADER


#ifdef MSVC_NEW_VER
	#include <VSStyle.h>
#else
	enum BUTTONPARTS {
		BP_PUSHBUTTON = 1,
		BP_RADIOBUTTON = 2,
		BP_CHECKBOX = 3,
		BP_GROUPBOX = 4,
		BP_USERBUTTON = 5,
		BP_COMMANDLINK = 6,
		BP_COMMANDLINKGLYPH = 7,
	};

	enum PUSHBUTTONSTATES {
		PBS_NORMAL = 1,
		PBS_HOT = 2,
		PBS_PRESSED = 3,
		PBS_DISABLED = 4,
		PBS_DEFAULTED = 5,
		PBS_DEFAULTED_ANIMATING = 6,
	};

	enum CHECKBOXSTATES {
		CBS_UNCHECKEDNORMAL = 1,
		CBS_UNCHECKEDHOT = 2,
		CBS_UNCHECKEDPRESSED = 3,
		CBS_UNCHECKEDDISABLED = 4,
		CBS_CHECKEDNORMAL = 5,
		CBS_CHECKEDHOT = 6,
		CBS_CHECKEDPRESSED = 7,
		CBS_CHECKEDDISABLED = 8,
		CBS_MIXEDNORMAL = 9,
		CBS_MIXEDHOT = 10,
		CBS_MIXEDPRESSED = 11,
		CBS_MIXEDDISABLED = 12,
		CBS_IMPLICITNORMAL = 13,
		CBS_IMPLICITHOT = 14,
		CBS_IMPLICITPRESSED = 15,
		CBS_IMPLICITDISABLED = 16,
		CBS_EXCLUDEDNORMAL = 17,
		CBS_EXCLUDEDHOT = 18,
		CBS_EXCLUDEDPRESSED = 19,
		CBS_EXCLUDEDDISABLED = 20,
	};

	enum COMBOBOXPARTS {
		CP_DROPDOWNBUTTON = 1,
		CP_BACKGROUND = 2,
		CP_TRANSPARENTBACKGROUND = 3,
		CP_BORDER = 4,
		CP_READONLY = 5,
		CP_DROPDOWNBUTTONRIGHT = 6,
		CP_DROPDOWNBUTTONLEFT = 7,
		CP_CUEBANNER = 8,
	};

	enum COMBOBOXSTYLESTATES {
		CBXS_NORMAL = 1,
		CBXS_HOT = 2,
		CBXS_PRESSED = 3,
		CBXS_DISABLED = 4,
	};
	
	enum HEADERPARTS {
		HP_HEADERITEM = 1,
		HP_HEADERITEMLEFT = 2,
		HP_HEADERITEMRIGHT = 3,
		HP_HEADERSORTARROW = 4,
		HP_HEADERDROPDOWN = 5,
		HP_HEADERDROPDOWNFILTER = 6,
		HP_HEADEROVERFLOW = 7,
	};

	enum HEADERITEMSTATES {
		HIS_NORMAL = 1,
		HIS_HOT = 2,
		HIS_PRESSED = 3,
		HIS_SORTEDNORMAL = 4,
		HIS_SORTEDHOT = 5,
		HIS_SORTEDPRESSED = 6,
		HIS_ICONNORMAL = 7,
		HIS_ICONHOT = 8,
		HIS_ICONPRESSED = 9,
		HIS_ICONSORTEDNORMAL = 10,
		HIS_ICONSORTEDHOT = 11,
		HIS_ICONSORTEDPRESSED = 12,
	};

	typedef HANDLE HTHEME;          // handle to a section of theme data for class
#endif // MSVC_NEW_VER

// MSDN suggests that: Do not link to UxTheme.lib.
// see http://msdn.microsoft.com/en-us/library/bb773175(VS.85).aspx#whistler_windows	
	
#ifdef LINK_TO_UXTHEME
	#include <Uxtheme.h>
	#pragma comment(lib, "UxTheme.lib")

	#define OOpenThemeData								OpenThemeData
	#define OCloseThemeData								CloseThemeData
	#define OSetWindowTheme								SetWindowTheme
	#define ODrawThemeBackground						DrawThemeBackground
	#define OIsThemeBackgroundPartiallyTransparent		IsThemeBackgroundPartiallyTransparent
	#define ODrawThemeParentBackground					DrawThemeParentBackground
#else
	HTHEME	OOpenThemeData(HWND hwnd, LPCWSTR pszClassList);
	HRESULT	OCloseThemeData(HTHEME hTheme);
	HRESULT	OSetWindowTheme(HWND hwnd, LPCWSTR pszSubAppName, LPCWSTR pszSubIdList);
	HRESULT	ODrawThemeBackground(HTHEME hTheme, HDC hdc, int iPartId, int iStateId, const RECT* pRect, const RECT* pClipRect);
	BOOL	OIsThemeBackgroundPartiallyTransparent(HTHEME hTheme, int iPartId, int iStateId);
	HRESULT	ODrawThemeParentBackground(HWND hwnd, HDC hdc, const RECT* prc);
#endif // LINK_TO_UXTHEME

LRESULT EnableWindowTheme(HWND hwnd, LPCWSTR classList, LPCWSTR subApp, LPCWSTR idlist);

SIZE GetBmpSize(HBITMAP hBmp);
SIZE GetIconSize(HICON hIcon);

/*----------------------------------------------------------------------------*/
/* CCustomDrawHelper
/*----------------------------------------------------------------------------*/
class CCustomDrawHelper
{
public:
	CCustomDrawHelper();
	virtual ~CCustomDrawHelper();
public:
	void DrawItemBackground(CDC* pDC, CRect rect, BYTE byAlpha = 255);
	void DrawFocus(CDC* pDC, CRect rect, BYTE byAlpha = 255);

#ifdef CUSTOMDRAW_GRADIENT
	void DrawThemeBackground(CDC* pDC, CRect rect, CDTBT type);
#endif // CUSTOMDRAW_GRADIENT

protected:
	inline bool ShouldFillBackground()
	{
#ifdef CUSTOMDRAW_GRADIENT
		bool bShouldFill = m_bSelected || m_bIsHotItem;
#else
		bool bShouldFill = m_bSelected;
#endif // CUSTOMDRAW_GRADIENT
		return bShouldFill;
	}
public:
	bool m_bWndHasFocus;
	bool m_bFocusItem;
	bool m_bSelected;
	bool m_bIsHotItem;
	bool m_bDrawBorderWhenFill;
	bool m_bIsPressed;
	bool m_bWndEnabled;

	CRect	m_rcBounds;
	HWND	m_hWnd;
};

/*----------------------------------------------------------------------------*/
/* CMemBitmap
/*----------------------------------------------------------------------------*/
class CMemBitmap
{
public:
	CMemBitmap(CDC* pDC = NULL, CBitmap* pBmp = NULL);
	virtual ~CMemBitmap();
public:
	BOOL			BeginDraw(int cx, int cy, BOOL bAutoCreate = TRUE);
	void			EndDraw();

	inline BOOL		BeginDraw(SIZE size)		{ return BeginDraw(size.cx, size.cy); }

	inline CDC&		GetDC()						{ return m_dcMemory; }
	inline const CDC&	GetDC() const			{ return m_dcMemory; }
	void			SetCompatibleDC(CDC* pDC);
	inline void		SetCompatibleDC(CDC& dc)	{ SetCompatibleDC(&dc); }

	inline CBitmap&	GetBitmap()					{ return m_pBmp ? *m_pBmp : m_bmp; }
	inline CBitmap*	GetBitmapPtr()				{ return m_pBmp ? m_pBmp : &m_bmp; }
	void			SetBitmap(CBitmap* pBmp);
	inline void		SetBitmap(CBitmap& bmp)		{ SetBitmap(&bmp); }

	inline const CSize& GetBitmapSize() const	{ return m_sizeBmp; }
	inline const int	GetBitmapWidth() const	{ return m_sizeBmp.cx; }
	inline const int	GetBitmapHeight() const	{ return m_sizeBmp.cy; }

	operator CDC&()								{ return GetDC(); }
	operator HDC()								{ return GetDC(); }
	CMemBitmap& operator =(CMemBitmap& src);
protected:
	CDC*		m_pDC;
	CBitmap*	m_pBmp;

	CDC			m_dcMemory;
	CBitmap		m_bmp;

	CBitmap*	m_pBmpOld;
	CSize		m_sizeBmp;
};

/*----------------------------------------------------------------------------*/
/* CSimpleMemDC
/*----------------------------------------------------------------------------*/
enum OUTPUTTYPE
{
	OUTPUTTYPE_NOP,
	OUTPUTTYPE_BITBLT,
	OUTPUTTYPE_TRANSPARENTBLT,
};

class CSimpleMemDC
{
public:
	CSimpleMemDC(CDC& dc, CWnd* pWnd, OUTPUTTYPE type = OUTPUTTYPE_BITBLT);
	CSimpleMemDC(CDC& dc, const CRect& rect, OUTPUTTYPE type = OUTPUTTYPE_BITBLT);
	
	virtual ~CSimpleMemDC();
public:
	inline CDC& GetDC()							{ return m_memBitmap.GetDC(); }
	inline CMemBitmap&	GetMemBitmap()			{ return m_memBitmap; }

	operator CDC&()								{ return GetDC(); }
	operator HDC()								{ return GetDC(); }
	CSimpleMemDC& operator =(CSimpleMemDC& src);
	CSimpleMemDC& operator =(CMemBitmap& src);

	inline void InitTransparentBackground(LPCRECT lpcRect = NULL)
	{
		GetDC().FillSolidRect(lpcRect ? lpcRect : m_rect, m_clrTransparent = RGB_TRANSPARENT);
	}
protected:
	inline BOOL PaintResult(const CRect& rectDraw, int nSrcX, int nSrcY)
	{
		switch (m_outputType)
		{
		case OUTPUTTYPE_BITBLT:
			return m_dc.BitBlt(rectDraw.left, rectDraw.top, rectDraw.Width(), rectDraw.Height(), &m_memBitmap.GetDC(), nSrcX, nSrcY, m_dwRop);
		case OUTPUTTYPE_TRANSPARENTBLT:
			return TransparentBlt(m_dc, rectDraw.left, rectDraw.top, rectDraw.Width(), rectDraw.Height(), 
				m_memBitmap.GetDC(), nSrcX, nSrcY, rectDraw.Width(), rectDraw.Height(), m_clrTransparent);
		case OUTPUTTYPE_NOP:
		default:
			return TRUE;
		}
		return FALSE;
	}
public:
	COLORREF		m_clrTransparent;	// note: this member won't be initialized, you must set it manually
	DWORD			m_dwRop;
protected:
	CDC&			m_dc;
	CRect			m_rect;
	CMemBitmap		m_memBitmap;
	CWnd*			m_pWnd;
	OUTPUTTYPE		m_outputType;

	CPoint			m_ptOldWndOrg;
};

/*----------------------------------------------------------------------------*/
/* ITextDrawer
/*----------------------------------------------------------------------------*/
class ITextDrawer
{
public:
	ITextDrawer(CWnd* pWnd = NULL)
		: m_pWnd(pWnd)
	{
		;
	}
	virtual ~ITextDrawer()
	{
		;
	}
public:
	virtual int Draw(CDC* pDC, const CString& strText, LPRECT lpRect, UINT nFormat = 0) = 0;
protected:
	CWnd*		m_pWnd;
};

#else
#error ### repeated include of CustomDrawUtils.h!!!
#endif // __CUSTOMDRAWUTILS_H__

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

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


Written By
Software Developer
China China
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions