Advanced C++ Listcontrol

Introduction

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?

Summary

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:

- CCellEdit

- CCellCheckBox. This derives from CCellButton, which handles behaviour of a button, since a checkbox is a type of button.

- CCellComboBox

- CCellDateCtrl

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

Further Extensions

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.

Notes

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



Downloads

Comments

  • RE: CCellEdit focus

    Posted by CCamacho on 11/25/2011 06:42am

    Actually, thank you very much for your comment.
    I think the feature in which you select a row if first character of first row is same as  typed character needs to be disabled.
    This is not only an issue for CCellEdit, but also CCellDateCtrl and CCellTimeCtrl (which I cam currently working on as a user requested this).  I feel if so many controls have a problem, it only gets confusing keeping the feature.
    I am just commenting the code as in:
    
    void CConfigListCtrl::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags)
    {
    	BOOL bCall = TRUE;
    	if (m_ActiveCell.m_lItem != -1 && m_ActiveCell.m_lSubItem != -1)
    	{
    		bCall = m_ActiveCell.m_pCellCtrl->OnChar(nChar, nRepCnt, nFlags);
    		InvalidateActiveCellRect();
    		SetItem(m_ActiveCell.m_lItem, m_ActiveCell.m_lSubItem, m_ActiveCell.m_pCellCtrl->GetSelectedTextValue());
    	}
    	// Never call CListCtrl::OnChar: reason:
    	// If do so, will automatically highlight a row in which first character of first column is same as typed character.
    	// No notification received, and m_ActiveCell remains the active cell, so display is inconsistent.   
    	//if (bCall)
    	//	CListCtrl::OnChar(nChar, nRepCnt, nFlags);
    }
    
    Latest code will soon be out on: 
    http://www.codeproject.com/KB/list/MFCCListCtrlDerClass.aspx

    Reply
  • RE: CCellEdit focus

    Posted by CCamacho on 11/23/2011 04:25am

    Hi Maurizio, Thank you for your feedback. That is indeed a bug which I found myself and corrected in my CodeProject article: http://www.codeproject.com/KB/list/MFCCListCtrlDerClass.aspx?fid=1643538 (This newer version has lots of bug fixes and enhancements, including full theme support b I am only able to support VS 2010 though) My comment about your fix: You have added bCall=FALSE; In fact, it is CCellEdit::OnChar that should return FALSE. This way, you keep standard behaviour, except when a CCellEdit is ActiveCell. Kind Regards, Christopher Camacho

    Reply
  • CCellEdit focus

    Posted by MaurizioFontana on 11/23/2011 04:00am

    Dear Christopher I downloaded the configurableCtrl VS 2010, but I have a problem when I type a character in an CCellEdit, the focus goes to the line that begin with character typed. I have changed the CConfigListCtrl::OnChar, see below and all look working well, some suggestion? void CConfigListCtrl::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags) { BOOL bCall = TRUE; if (m_ActiveCell.m_lItem != -1 && m_ActiveCell.m_lSubItem != -1) { bCall = m_ActiveCell.m_pActiveCtrl->OnChar(nChar, nRepCnt, nFlags); InvalidateActiveCellRect(); SetItem(m_ActiveCell.m_lItem, m_ActiveCell.m_lSubItem, m_ActiveCell.m_pActiveCtrl->GetSelectedTextValue()); bCall=FALSE; // Fontana } if (bCall) CListCtrl::OnChar(nChar, nRepCnt, nFlags); }

    Reply
  • Re: outdated look & feel

    Posted by CCamacho on 03/02/2011 11:16am

    I am not updating the style for frames and buttons.

    After second thoughts, I realise I do not have the resources for doing this.

    Essentially, I would need a computer with each version of Windows: 2000, ME, XP, Vista, 7 and be able to run and test application on each machine.

    Worse: for this to be really viable, I would need to support future versions of Windows b something I am not in a position to guarantee.

    I feel this is a job for a Software vendor or the MFC team.

    If you application needs an up do date look and feel, then this control is not suitable as it is.

    Reply
  • Re: outdated look & feel

    Posted by CCamacho on 02/11/2011 10:24am

    Thank you!

    I see what you mean about the scroll bar in drop downs. I will see what I can do to fix that. I donbt quite follow you about the other outdated looks, but I will investigate.

    Otherwise, I have fixed issues with Esc, Enter and F4 in my latest code changes b not yet available to you, as not even submitted. You can also navigate using up and down arrows in latest code changes I am making for both combo box and date picker (I have not done color picker yet).

    I donbt like the way I displayed dates in a strict dd/MM/yyyy format, so I am currently making changes so date displayed is in the short date format of computer (so will display as MM/dd/yyyy).

    Another change I need to make is tabbing within the control: you should be able to tab from one control into another within the list control. I will have a style so on tabbing out of a control with this style set, tab out of the list control altogether.

    Reply
  • outdated look & feel

    Posted by pates on 02/11/2011 09:39am

    Thanks for the responses! Another concern is that the controls appear outdated. Check out this sample - the dropdown arrow and calendar navigation arrows are blue instead of gray: http://www.codeproject.com/KB/list/Extended_List_Control.aspx. But I like how your dropdowns are persistent and don't require a double-click first. In fact, in your dialog Select Row dropdown has a modern scrollbar, whereas all the other controls (dropdowns and scrollbars within the list, OK button, etc.) use the outdated look.

    Reply
  • Re: ESC behavior

    Posted by CCamacho on 02/05/2011 03:26pm

    ...actually, on pressing 'Enter', you want to select the currently selected item. My solution above doesn't do that.
    Instead of having:
    
    void CCellDropDown::CloseDropDown
    
    have 2 functions:
    
    virtual void CCellDropDown::OnEnter()
    void CCellDropDown::OnEsc()
    
    Call these in 
    CConfigListCtrl::OnEnter
    CConfigListCtrl::OnEsc
    
    which replace:
    CConfigListCtrl::ClosePopUp
    
    CCellDropDown::OnEnter() needs to be implemented for drop down, date controls and color pickers. I'll do that later - sorry I am too busy right now.

    Reply
  • Re: Re: ESC behavior: ATTENTION

    Posted by CCamacho on 02/04/2011 03:11pm

    The code got formated. ClosePopUp should read:
    
    void CConfigListCtrl::ClosePopUp(DWORD dwLocation, CCellCtrl *pCellCtrl)
    {
    	CCellDropDown *pDropDown = dynamic_cast<CCellDropDown *>(pCellCtrl);
    
    	if (pDropDown)
    		pDropDown->CloseDropDown();
    }
    
    It is the lines in bold that need to be added:
    
    BOOL CConfigurableCtrlDlg::PreTranslateMessage(MSG* pMsg)
    {
    	if(pMsg->wParam == VK_ESCAPE || pMsg->wParam == VK_RETURN)
    		return m_ListCtrl.OnEnterEsc(pMsg->wParam);
    
    	return CDialogEx::PreTranslateMessage(pMsg);
    }
    
    (sorry - didn't realise formating took place in comments too)

    Reply
  • Re: ESC behavior

    Posted by CCamacho on 02/04/2011 03:00pm

    Thank you for pointing this issue out.
    An MFC dialog box closes when user presses Esc and Enter (which is even worse!)
    To prevent this from occuring, you need to make following modifications:
    
    Add a public function in CellDropDown.h/.cpp as follow:
    
    void CCellDropDown::CloseDropDown()
    {
    	if (m_bPopUpOpen)
    	{
    		m_bPopUpOpen = FALSE;
    		m_pPopUpWnd->ShowWindow(SW_HIDE);
    		GetWindowFromHandle()->InvalidateRect(&m_rcBounding, FALSE);
    	}
    }
    
    Add the following 2 functions in ConfigListCtrl.cpp
    
    void CConfigListCtrl::ClosePopUp(DWORD dwLocation, CCellCtrl *pCellCtrl)
    {
    	CCellDropDown *pDropDown = dynamic_cast(pCellCtrl);
    
    	if (pDropDown)
    		pDropDown->CloseDropDown();
    }
    
    BOOL CConfigListCtrl::OnEnterEsc(WPARAM param)
    {
    	if(param == VK_ESCAPE || param == VK_RETURN)
    		ForEachCellCtrl(m_CtrlMap, &CConfigListCtrl::ClosePopUp);
    
    	return TRUE;
    }
    
    where:
    
    void ClosePopUp(DWORD dwLocation, CCellCtrl *pCellCtrl);
    
    is private and
    
    BOOL OnEnterEsc(WPARAM param)
    
    is public.
    Now, all that remains to be done is to add a PreTranslateMessage function to CConfigurableCtrlDlg (the main dialog box) using the wizard, and add the two lines shown below: 
    
    BOOL CConfigurableCtrlDlg::PreTranslateMessage(MSG* pMsg)
    {
    	if(pMsg->wParam == VK_ESCAPE || pMsg->wParam == VK_RETURN)
    		return m_ListCtrl.OnEnterEsc(pMsg->wParam);
    
    	return CDialogEx::PreTranslateMessage(pMsg);
    }
    
    This is what you get with Visual Studio 2010. Visual Studio 2006 might be slightly different.
    I shall update the sources.
    Also, I have just noticed that F4 does not open and close the drop downs.
    I shall fix that too.

    Reply
  • ESC behavior

    Posted by pates on 02/03/2011 10:40am

    Thank you so much for this example - it's been very helpful! I have a small usability nitpick, and that's regarding the behavior of the ESC key. When a dropdown menu or the time/date popup is open, I believe the user expectation is that ESC would close the dropdown/popup and not the entire dialog.

    Reply
  • Loading, Please Wait ...

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • In the competitive marketplace that surrounds us today, customers shouldn't have to settle for legacy desktop or application delivery simply because they've relied on a certain vendor in the past. This white paper reviews how three customers decided to partner with VMware, and how they benefited from the latest VDI and app trends to improve the end-user experience, increase productivity, reliability and stability to deliver better SLAs - with lower cost and less time needed to manage end users.

  • Not all enterprise applications are created equal. Sophisticated applications need developer support but other more basic apps do not. With the right tools, everyone is a potential app developer with ideas and a perspective to share. Trends such as low-code development and model driven development are fundamentally changing how and who creates applications. Is your organization ready? Read this report and learn: The seven personas of enterprise app delivery How application ownership is spreading to the …

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds