Click here to Skip to main content
15,901,035 members
Articles / Desktop Programming / MFC

Generic Picker Dropdown Control

Rate me:
Please Sign up or sign in to vote.
5.00/5 (13 votes)
12 Dec 2008CPOL3 min read 52.6K   3.1K   46   14
A flexible easily overridable combo-box-alike control for choosing from a 2D array of options
Image 1

Image 2


This article was inspired by Chris Maunder's Office 97 style Colour Picker control. A couple of years ago, I modified it greatly to pick from a palette of colours in my software. Rather than creating my own palette, I used the definitions that Autocad does in DXF files. 0 = black, 1 = white, 2 red, etc. More recently, I needed to add some drawing facilities. Once I added a couple of patterns, I wanted to add the rest. Which makes for a bust 60odd drop down list. Similarly, I wanted drop downs for line width and line styles. 

So I went back to Chris' article, stole/re-used the parts dealing with the popup window, and made the rest. As I knew, I would be making a variety of related controls, I made some member functions pure virtual, and implemented the specific functionality in several inheriting classes.

Lastly, the example project, and most of the pre-built pickers use GDI+. That's not at all related to the core idea - but it was the raison d'etre (sultana of summer, from the French) of the project.

The Pre-built Pickers 

Colour Picker

Image 3

This is the original one I implemented, and most closely modelled on Chris' implementation.

Line Style

Image 4

Line Width

Image 5

Hatch Style

Image 6

How to Use a Generic Picker Control in Your Own Code.

This is hopefully pretty simple! I'll be using the Line Style Picker as my example. First, add a button to your dialog, and give it an ID number. The text is irrelevant, but nice for editing the dialog later.

Image 7

Next, include the header file and member variable to your dialog box:

#include "DrawingPickers.h"
class CGenericPickerDemoDlg : public CDialog
	CPickerLineStyle	m_LineStyle;

Next, subclass the control in your OnInitDialog dialog member function. I also like to initialise the current item. Feel free to use DDX_Control in the DoDataExchange if you wish - but I rarely do.

BOOL CGenericPickerDemoDlg::OnInitDialog()
	m_LineStyle.SubclassDlgItem (IDC_LINESTYLE, this);
	m_LineStyle.SetStyle (Gdiplus::DashStyleSolid);
	return TRUE;  // return TRUE  unless you set the focus to a control

To handle changes in selection, you should handle the CBN_SELCHANGE notification. Chris' control used a custom message, but I wanted to be lazy and be able to use ON_CBN_SELCHANGE macro for my code.

BEGIN_MESSAGE_MAP(CGenericPickerDemoDlg, CDialog)

Lastly, you need to do something with the selection. In my demo program, I draw a circle with the various styles. As this is not a GDI+ tutorial, I'll skip over that part!

Making Your Own Picker

There are a few pure functions in CGenericPicker you need to override. Here they are:

virtual int GetColumns () const = 0;
virtual int GetRows () const = 0;

virtual void MeasureSubItem(CSize &mis) = 0;

virtual void DrawSubItem(const DrawItemSubStruct &dis) = 0;

virtual BOOL ShowDefaultItem () const = 0;

virtual BOOL IsCellValid (int nCol, int nRow) const { return TRUE; }

I'll show the CPickerLineWidth's implementation as an example.

GetColumns & GetRows

int	CPickerLineWidth::GetColumns () const		{ return 2;	}
int	CPickerLineWidth::GetRows () const		{ return 10;	}

Hopefully these two overridables are fairly self-explanatory! How many boxes wide and high is the 2D drop down...


void CPickerLineWidth::MeasureSubItem(CSize &mis)
{ /= 2; = 12;

MeasureSubItem is called once. The CSize structure is initialised to the same size as the button you added to your dialog. This makes it easy to match the width of the drop down to the width of the parent control. As I have two columns, and neither need to be a specific width, I just split the width between them. For other controls (like colour, or hatch style, this is made to be a fixed size.


void CPickerLineWidth::DrawSubItem(const DrawItemSubStruct &dis)
	CRect rc (dis.rcItem);
	Graphics graphics (*dis.pDC);
	Color colour;
	colour.SetFromCOLORREF(dis.bSelected ? GetSysColor (COLOR_BTNTEXT) : 
					GetSysColor (COLOR_BTNSHADOW));
	float fWidth = dis.nRow + float(10 * dis.nColumn);
	fWidth /= 2.0f;
	Pen pen (colour, fWidth);
	graphics.DrawLine (&pen, rc.left, + 6, rc.right, + 6);

DrawSubItem does the heavy lifting of the control. It is used for drawing the button, and for drawing each cell. The DrawItemSubStruct structure has the CDC, drawing rectangle, current column and row, and whether it should be drawn as selected.

The selected option is used to choose between drawing in dark grey or black in my controls. Feel free to be fancier.


BOOL CPickerLineWidth::ShowDefaultItem () const { return FALSE; }

This isn't really implemented by me - Chris' control uses it. This lets the inheriting controls tell the CGenericPicker class know whether to leave room for a default item. This will be drawn with a column and row of -1.



  • 1.0 Initial release 


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

Written By
Software Developer (Senior)
Sweden Sweden
I have now moved to Sweden for love, and recently married a lovely Swede.

I started programming on BBC micros (6502) when I was six and never quite stopped even while I was supposed to be studying physics and uni.

I've been working for ~13 years writing software for machine control and data analysis. I now work on financial transaction transformation software, for a Software company in Gamlastan, Stockholm.
Look at my articles to see my excellent coding skills. I'm modest too!

Comments and Discussions

GeneralMy vote of 5 Pin
Manoj Kumar Choubey17-Feb-12 23:58
professionalManoj Kumar Choubey17-Feb-12 23:58 
QuestionProgrammatically select item? Pin
bolivar12316-Feb-10 10:37
bolivar12316-Feb-10 10:37 
AnswerRe: Programmatically select item? Pin
Iain Clarke, Warrior Programmer7-Jul-10 21:15
Iain Clarke, Warrior Programmer7-Jul-10 21:15 
I owe you an apology for my glacial reply - your question must have got swallowed by my spam folder...

I presume you've already solved your problem, but...

There's a protected member function of the base class:

void CGenericPicker::SetCurSel (POINTS ptCurSel)

You don't use it direcly though. In each of my derived classes, there's a SetSelection method.

void CPickerDefaultColour::SetColour (int nColour)
	pt.x = nColour % 16;
	pt.y = nColour / 16;
	SetCurSel (pt);

I hope that helps.

I am one of "those foreigners coming over here and stealing our jobs". Yay me!

GeneralExcellent Pin
bolivar12312-Feb-10 8:34
bolivar12312-Feb-10 8:34 
GeneralRe: Excellent Pin
Iain Clarke, Warrior Programmer12-Feb-10 11:29
Iain Clarke, Warrior Programmer12-Feb-10 11:29 
GeneralGood stuff, Iain Pin
Rajesh R Subramanian11-Oct-09 5:39
professionalRajesh R Subramanian11-Oct-09 5:39 
GeneralRe: Good stuff, Iain Pin
Iain Clarke, Warrior Programmer11-Oct-09 5:52
Iain Clarke, Warrior Programmer11-Oct-09 5:52 
QuestionAnyone knows how to do this in C#? Pin
soft2buy16-Aug-09 7:30
soft2buy16-Aug-09 7:30 
AnswerRe: Anyone knows how to do this in C#? Pin
Iain Clarke, Warrior Programmer16-Aug-09 20:50
Iain Clarke, Warrior Programmer16-Aug-09 20:50 
GeneralRe: Anyone knows how to do this in C#? Pin
soft2buy17-Aug-09 4:36
soft2buy17-Aug-09 4:36 
GeneralThanks for the article. Pin
Randor 17-Dec-08 2:06
professional Randor 17-Dec-08 2:06 
GeneralRe: Thanks for the article. Pin
Iain Clarke, Warrior Programmer7-Jan-09 23:51
Iain Clarke, Warrior Programmer7-Jan-09 23:51 
GeneralMy vote of 2 Pin
PatLeCat15-Dec-08 0:10
PatLeCat15-Dec-08 0:10 
GeneralRe: My vote of 2 Pin
Iain Clarke, Warrior Programmer15-Dec-08 1:02
Iain Clarke, Warrior Programmer15-Dec-08 1:02 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.