I provide a simple class that enables the user to customize list view control columns.
Introduction
To customize column of a Win32 listview
control, I provide a simple class that can do show/hide/reorder/save and restore the columns. This class is implemented as a template class so it can easily be used with Win32/ATL/WTL/MFC platforms (may need minor changes).
Using the Code
This class is a template, to use it, you need to just drive it from your base list-view control class. The base class MUST at-least have a member m_hWnd
that contains the handle of the list-view control. All columns are identified with an id named subitem id
(zero based).
Also, you have to insert items as LPSTR_TEXTCALLBACK
and handle LVN_GETDISPINFO
notification. For more information, see LVN_GETDISPINFO notification code.
The class declaration and members are like below:
template<class T>
class CListCtrlCustomizeImpl
{
public:
int IndexToSubItem(int nIndex);
int SubItemToIndex(int nSubItem);
void DeleteAllColumns();
BOOL IsColumnVisible(int nSubItem);
void ShowColumn(int nSubItem);
void HideColumn(int nSubItem);
void ToggleColumn(int nSubItem);
BOOL SaveState(LPCTSTR lpszSubKey, LPCTSTR lpszValueName);
BOOL LoadState(LPCTSTR lpszSubKey, LPCTSTR lpszValueName);
protected:
virtual void OnNeedColumnInfo
(int nSubItem, CString& strHeading, int& nFormat, int& nWidth) = 0;
};
The members are simple as their name. You can see that there exists one virtual
pure member OnNeedColumnInfo
- you must override this in the derived class to provide information about the columns you want to insert/show. In fact, this member is the heart of this class. nSubItem
parameter specified the column id that information about it must be set in other referenced type parameters.
An implementation is like below:
#define SUBITEM_ID 0
#define SUBITEM_NAME 1
#define SUBITEM_AGE 2
#define SUBITEM_EMAIL 3
#define SUBITEM_ADDRESS 4
#define SUBITEM_JOB 5
class CDemoListCtrl : public ATL::CWindow, public CListCtrlCustomizeImpl<CDemoListCtrl>
{
public:
void OnNeedColumnInfo(int nSubItem, CString& strHeading, int& nFormat, int& nWidth)
{
switch (nSubItem)
{
case SUBITEM_ID:
strHeading = _T("ID");
nFormat = LVCFMT_LEFT;
nWidth = 100;
break;
case SUBITEM_NAME:
strHeading = _T("Name");
nFormat = LVCFMT_LEFT;
nWidth = 200;
break;
case SUBITEM_AGE:
strHeading = _T("Age");
nFormat = LVCFMT_LEFT;
nWidth = 70;
break;
case SUBITEM_EMAIL:
strHeading = _T("e-mail");
nFormat = LVCFMT_LEFT;
nWidth = 150;
break;
case SUBITEM_ADDRESS:
strHeading = _T("Address");
nFormat = LVCFMT_LEFT;
nWidth = 200;
break;
case SUBITEM_JOB:
strHeading = _T("Job");
nFormat = LVCFMT_LEFT;
nWidth = 90;
break;
}
}
};
To insert/show a column, call ShowColumn
method with column subitem
id
. Then, this method called OnNeedColumnInfo
to get information about that column.
To hide/remove a column, call HideColumn
method with column subitem id
.
Also, in handling LVN_GETDISPINFO
, you must use IndexToSubItem
member to convert column index to subitem id
. Like below:
LRESULT OnGetDispInfo(int idCtrl, LPNMHDR pnmh, BOOL& bHandled)
{
NMLVDISPINFO* pDispInfo = (NMLVDISPINFO*)pnmh;
if (pDispInfo->item.mask & LVIF_TEXT)
{
switch (m_wndList.IndexToSubItem(pDispInfo->item.iSubItem))
...
SaveState
and LoadState
do save and restore of columns. Data are saved in system registry under current user key.
To insert initial columns, you must first call LoadState
and if this method returns FALSE
, then insert default columns like below:
if (m_wndList.LoadState("path to reg key", "reg value name") == FALSE)
{
m_wndList.ShowColumn(SUBITEM_ID);
m_wndList.ShowColumn(SUBITEM_NAME);
...
}
Note: To enabling headers re-ordering use LVS_EX_HEADERDRAGDROP on list view control extended style.
See the demo code to get full information.
History
- 19th September, 2023: First version of this class