A common need is an owner draw CListCtrl that allows a particular cell to contain another control such as an edit box, combo box, color picker etc…
Such a control would be very useful in many applications, as would provide a way of easily configuring application specific properties, such as background colour etc…
I have tried directly inserting a CCombobox or CEdit control in a cell, but this seemed to have nasty side effects – I managed to get some difficult to reproduce strange behaviour with the CEdit control, which does much more than it appears to do.
Also the frames of different controls are not always consistent with each other. For a good appearance, these would have to be the same.
…I found there was quite a bit of flicking too.
…and you have to handle which of the controls is going to receive user input messages, which is not as straighforward as it appears: is it the parent owner draw CListCtrl control or the child CCombobox who should receive user input at a particular moment for instance?
In this article, I describe an owner draw CConfigListCtrl that derives from CListCtrl. This control does not have another CWnd-derived control inserted in a particular cell. Instead, all user input messages are always handled by CConfigListCtrl. When want to insert another ‘control’ in a cell, you insert a CCellCtrl-derived control.
When draw a particular cell, a call is made to CCellCtrl::DrawCtrl from the parent. All message handling is passed from CConfigListCtrl to the CCellCtrl-derived controls and CConfigListCtrl has no knowledge of the implementation details of the CCellCtrl-derived controls.
The drawback with this approach is the CCellCtrl-derived controls need to stick to the look and behaviour of controls from later versions of MFC.
The frames are painted differently for instance, but this is not a big issue as can easily be done without much effort.
Description of controls
As mentioned, all pseudo-controls inserted into CConfigListCtrl cells derive from CCellControl. This class has a number of virtual functions. The drawing of the control is done by DrawCtrl, as already mentioned. Otherwise, all messages from CConfigListCtrl are passed to CCellControl virtual functions, so the pseudo-control can derive from these and handle the correct message. It is possible that new handling may be required.
CCellControl also has some utility functions such as CCellCtrl::DrawFrameCtrl.
If want to change the look of the frame, most of changes would be done here, but changes may be required elsewhere too: combo box button may need adjusting.
The full list of usable controls is as follows:
– CCellCheckBox. This derives from CCellButton, which handles behaviour of a button, since a checkbox is a type of button.
– CCellColorCtrl (VS2010 only, but not a big effort to transfer to VS6.0: copy relevant files and change the color picker dialog box for ‘other’ button, so it is the old version)
CCellComboBox, CCellDateCtrl and CCellColorCtrl all derive from CCellDropDown, which itself derives from CCellButton and has facilities and common features for all drop down controls. On clicking the drop down button, a new CWnd-derived pop up dialog appears. This is one of CPopUpScrollList, CPopupCalendar or CPopupColorBar. These derive from ClistCtrlCellWnd, which itself derives from CWnd.
The insertion of a control is done as (for example):
m_ListCtrl.SetItem(0, 1, new CCellEdit, _T("(Not interesting)"));
The cleanup of allocated memory is done in CConfigListCtrl::~CConfigListCtrl(), CConfigListCtrl::ForEachCellCtrl and CConfigListCtrl::DeleteCellCtrl. (A class function pointer was used for this).
How to use
To use this control as such is fairly straightforward. You will have to recopy all .cpp/.h files, except ConfigurableCtrlDlg and StdAfx .h/.cpp files in your project. There is also the IDB_CHECKBOX bitmap you have to insure you have.
For examples of use, look in ConfigurableCtrlDlg.cpp. More examples below:
m_ListCtrl.SetItem(0, 1, new CCellEdit, _T("(Not interesting)")); m_ListCtrl.SetItem(1,1, new CCellCheckBox, _T("(0This is unchecked")); m_ListCtrl.SetItem(2,1, new CCellCheckBox, _T("(1This is checked")); m_ListCtrl.SetItem(3, 1, new CCellComboBox, _T("Lagonda\nArmstrong Siddeley\nBentley\nBugatti\nDe Dion Bouton\nHispano Suiza\nLagonda\nLorraine Dietrich\nMercedes\nNapier\nRolls Royce")); m_ListCtrl.SetItem(4, 1, new CCellDateCtrl, _T("20070930")); m_ListCtrl.SetItem(5, 1, new CCellColorCtrl, _T("0X000000FF")); m_ListCtrl.SetItem(6, 1, new CCellColorCtrl, _T("#000000FF"));
A checkbox is checked if first character of name is 1, unchecked if filled with 0.
The selected item of a combo box is the first
item in the list. For this to be visible, it must appear again subsequently in
the items used to populated the combo box.
For a color, the default is set as 0x00bbggrr or #00rrggbb, where rr, gg, bb are 2-digit hexadecimal numbers to represent red, green and blue.
New controls could easily be added, such as an editable drop down.
Almost all of the code for the color picker could be transfered from Visual Studio 2010 to Visual Studio 6.0. Frames of controls could be updated to reflect changes in MFC. Only needs to be done in one place. I would suggest having a style for that, so user of control can chose to keep using current look.
I am aware the CEdit control has changed in behaviour since VS6.0. Is it worth updating this behaviour? The main functionality, that is, entering text remains the same.
07/01/2011: just added source compilable with Visual Studio 2010. This include a color picker. See screenshot below:
07/01/2011: Replaced the old source (which compiles with Visual Studio 6.0). The new source has a few bug fixes, including those found by user in comments below. Bug fixes have been made for both Visual Studio 2010 and VS6.0 versions.
25/01/2011: Complete rewrite of text. I hope to be clearer!
23/02/2011: Support of escape, enter, F4, and tabs. DateTime control rewritten so it uses date format as set in short date on computer settings, with possibility of user configuring own format such as d – MMM – yyyy.
The date is put in cell and retrieved in format YYYYMMDD