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

An Extended MFC CListCtrl to edit individual cells, sort headers, and more

0.00/5 (No votes)
23 Jul 2008 1  
The MFC CListCtrl does not allow editing labels for all columns. This extended class implements ways to specify column editors, row, cell, and column colors etc.

Introduction

This article explains a very flexible way to specify a row, column, or cell editor, color or sorting features.

untitled1.JPG

Background

I was looking for a list control that allows me to edit each column label separately and specify the editor of my choice like a DateTimePicker, or ComboBox, or TextBox, or even a dialog box. So, I extended and created this list control.

Using the code

Using CListCtrlEx is very similar to using CListCtrl. A few extra methods have been added to ease label editing and sorting. Following are the additional functions added to the list control:

void SetColumnEditor(int nColumn, PFNEDITORCALLBACK pfnInitEditor, 
     PFNEDITORCALLBACK m_pfnEndEditor = NULL,  CWnd* pWnd = NULL);
void SetColumnEditor(int nColumn, CWnd* pWnd);
void SetCellEditor(int nRow, int nColumn, PFNEDITORCALLBACK pfnInitEditor, 
     PFNEDITORCALLBACK m_pfnEndEditor = NULL,  CWnd* pWnd = NULL);
void SetCellEditor(int nRow, int nColumn, CWnd* pWnd);
void SetRowEditor(int nRow, PFNEDITORCALLBACK pfnInitEditor, 
     PFNEDITORCALLBACK m_pfnEndEditor = NULL,  CWnd* pWnd = NULL);
void SetRowEditor(int nRow, CWnd* pWnd);
void SetDefaultEditor(PFNEDITORCALLBACK pfnInitEditor, 
     PFNEDITORCALLBACK m_pfnEndEditor = NULL,  CWnd* pWnd = NULL);
void SetDefaultEditor(CWnd* pWnd); 
         
void SetColumnReadOnly(int nColumn, bool bReadOnly = true);
void SetCellReadOnly(int nRow, int nColumn, bool bReadOnly = true);
void SetRowReadOnly(int nRow, bool bReadOnly = true);

void SetRowColors(int nItem, COLORREF clrBk, COLORREF clrText);
void SetColumnColors(int nColumn, COLORREF clrBack, COLORREF clrText);
void SetCellColors(int nRow, int nColumn, COLORREF clrBack, COLORREF clrText);
 
BOOL DisplayEditor(int nItem, int nSubItem);
void HideEditor(BOOL bUpdate = TRUE);

void DeleteSelectedItems(void);
//Delete selected items when delete key is pressed
void HandleDeleteKey(BOOL bHandle = TRUE);
void SelectItem(int nItem, BOOL bSelect);
BOOL DeleteAllColumns(void);
BOOL Reset(void);

void SetColumnSorting(int nColumn, Sort eSort, Comparer eSortType = String);
void SetColumnSorting(int nColumn, Sort eSort, PFNLVCOMPARE fnCallBack);
BOOL SortOnColumn(int nColumn, BOOL bChangeOrder = FALSE);
 
BOOL IsColumnReadOnly(int nColumn);
BOOL IsRowReadOnly(int nRow);
BOOL IsCellReadOnly(int nRow, int nColumn);

The following code explains how to use the extended list control in your dialog:

BOOL CDemoDlg::OnInitDialog()
{
    CDialog::OnInitDialog();

    ...
    //Set image list to increase row height
    m_imgList.Create(1, 20, ILC_COLOR, 0, 1);
    m_lstDemo.SetImageList(&m_imgList, LVSIL_SMALL);
    FillListBox();
    m_lstDemo.SetExtendedStyle(LVS_EX_FULLROWSELECT | LVS_EX_GRIDLINES);
    //set full row select and grid lines

    return TRUE;
    // return TRUE  unless you set the focus to a control
}

void CDemoDlg::AddColumns(void)
{
    m_lstDemo.InsertColumn(0, "Default Editor", LVCFMT_LEFT, 100);
    m_lstDemo.InsertColumn(1, "Date Time Editor", LVCFMT_LEFT, 100);
    m_lstDemo.InsertColumn(2, "Combobox Editor", LVCFMT_LEFT, 100);
    m_lstDemo.InsertColumn(3, "Color Select", LVCFMT_LEFT, 150);
    m_lstDemo.InsertColumn(4, "Read Only Column", LVCFMT_LEFT, 100);
    m_lstDemo.InsertColumn(5, "Just a Column", LVCFMT_LEFT, 100);

    m_lstDemo.SetColumnEditor(1, &CDemoDlg::InitEditor, 
                                 &CDemoDlg::EndEditor, &m_wndDT);
    m_lstDemo.SetColumnEditor(2, &CDemoDlg::InitEditor, 
                                 &CDemoDlg::EndEditor, &m_wndCB);
    m_lstDemo.SetColumnEditor(3, &CDemoDlg::InitEditor, 
                                 &CDemoDlg::EndEditor, &m_dlgColor);
    m_lstDemo.SetColumnReadOnly(4);
    m_lstDemo.SetDefaultEditor(NULL, NULL, &m_wndEdit);
    m_lstDemo.SetColumnColors(4, RGB(200, 200, 200), RGB(128, 128, 128));
    m_lstDemo.SetColumnSorting(0, CListCtrlEx::Auto, CListCtrlEx::StringNoCase);
    m_lstDemo.SetColumnSorting(1, CListCtrlEx::Auto, CListCtrlEx::Date);
    m_lstDemo.SetColumnSorting(2, CListCtrlEx::Auto, CListCtrlEx::String);
    m_lstDemo.SetColumnSorting(3, CListCtrlEx::Auto, CListCtrlEx::StringNoCase);
    m_lstDemo.SetColumnSorting(4, CListCtrlEx::Auto, CListCtrlEx::StringNoCase);
}

void CDemoDlg::FillListBox(void)
{
    m_lstDemo.Reset();
    AddColumns();
    CString strDate = COleDateTime(CTime::GetCurrentTime().GetTime()).Format();
    for(int i = 0; i < 20; i++)
    {
        CString str;
        str.Format("Some %d Text %d", rand(), rand());
        m_lstDemo.InsertItem(i, str);
        m_lstDemo.SetItemText(i, 1, strDate);
        m_lstDemo.SetItemText(i, 2, "text1");
        m_lstDemo.SetItemText(i, 3, "Some Text");
        m_lstDemo.SetItemText(i, 4, "Read Only");
        m_lstDemo.SetItemText(i, 5, "Some Text");
        if(i%9 == 3)
        {
            m_lstDemo.SetRowColors(i, -1, RGB(255, 0, 0));
            m_lstDemo.SetRowEditor(i, NULL, NULL, &m_wndEdit);
        }
        if(i % 7 == 0)
        {
            m_lstDemo.SetCellColors(i, 5, RGB(0, 255, 0), RGB(255, 255, 255));
            m_lstDemo.SetCellEditor(i, 5, &CDemoDlg::InitEditor, 
                                    &CDemoDlg::EndEditor, &m_wndDT);
        }
        if(i % 8 == 0) m_lstDemo.SetCellColors(i, 5, RGB(0, 255, 0), -1);
    }
    
}
//Call back function to initialize the cell editor.
BOOL
CDemoDlg::InitEditor(CWnd** pWnd, int nRow, int nColumn, CString&
strSubItemText, DWORD_PTR dwItemData, void* pThis, BOOL bUpdate)
{
    ASSERT(*pWnd);
    switch(nColumn)
    {
    case 1:
    case 5:
        {
            CDateTimeCtrl *pDTC = dynamic_cast<CDateTimeCtrl *>(*pWnd);
            COleDateTime dt;
            if(dt.ParseDateTime(strSubItemText)) pDTC->SetTime(dt);
        }
        break;
    case 2:
        {
            CComboBox *pCmb = dynamic_cast<CComboBox *>(*pWnd);
            pCmb->SelectString(0, strSubItemText);
        }
        break;
    case 3:
        {
            CDlgColor *pDlg = dynamic_cast<CDlgColor *>(*pWnd);        
            pDlg->m_nColor = strSubItemText.CompareNoCase("green")? 
               (strSubItemText.CompareNoCase("blue")?0:2):1;
            pDlg->Create(CDlgColor::IDD, (CWnd*)pThis);            
            pDlg->UpdateData(FALSE);
        }
        break;
    }
    return TRUE;
}
 
//Call back function to end and destroy the cell editor.
//Spacial feature return -1 to sort list control items 
//based on the current editing item.
BOOL CDemoDlg::EndEditor(CWnd** pWnd, int nRow, int nColumn, 
                         CString& strSubItemText, 
                         DWORD_PTR dwItemData, 
                         void* pThis, BOOL bUpdate)
{
    ASSERT(pWnd);
    switch(nColumn)
    {
    case 1:
    case 5:
        {
            CDateTimeCtrl *pDTC = dynamic_cast<CDateTimeCtrl *>(*pWnd);
            COleDateTime dt;
            pDTC->GetTime(dt);
            strSubItemText = dt.Format();
        }
        break;
    case 2:
        {
            CComboBox *pCmb = dynamic_cast<CComboBox *>(*pWnd);
            int index = pCmb->GetCurSel();
            if(index >= 0) pCmb->GetLBText(index, strSubItemText);
        }
        break;
    case 3:
        {
            CDlgColor *pDlg = dynamic_cast<CDlgColor *>(*pWnd);
            CListCtrlEx *pList = reinterpret_cast<CListCtrlEx *>(pThis);
            pDlg->UpdateData(TRUE);
            switch(pDlg->m_nColor)
            {
            case 1:
                strSubItemText = "Green";
                pList->SetCellColors(nRow, nColumn, RGB(0, 255, 0), -1);
                break;
            case 2:
                strSubItemText = "Blue";
                pList->SetCellColors(nRow, nColumn, RGB(0, 0,255 ), -1);
                break;
            default:
                strSubItemText = "Red";
                pList->SetCellColors(nRow, nColumn, RGB(255, 0, 0), -1);
                break;
            }                
            pDlg->DestroyWindow();
        }
        break;
    }
    return TRUE;
}

I hope the code is self explanatory; if not, just post a question. Also have a look at the demo project.

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