Introduction
This article explains a very flexible way to specify a row, column, or cell editor, color or sorting features.
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);
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();
...
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);
return TRUE;
}
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);
}
}
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;
}
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.