Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Generic Picker Dropdown Control

0.00/5 (No votes)
12 Dec 2008 2  
A flexible easily overridable combo-box-alike control for choosing from a 2D array of options

Introduction

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

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

Line Style

Line Width

Hatch Style

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.

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()
{
	CDialog::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)
...
	ON_CBN_SELCHANGE(IDC_LINESTYLE, OnPickerChanged)
...
END_MESSAGE_MAP()

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...

MeasureSubItem

void CPickerLineWidth::MeasureSubItem(CSize &mis)
{
	mis.cx /= 2;
	mis.cy = 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.

DrawSubItem

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, rc.top + 6, rc.right, rc.top + 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.

ShowDefaultItem

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.

Acknowledgements

History

  • 1.0 Initial release 

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here