Customize List View Column Headers






4.82/5 (4 votes)
Show or hide, reorder, save and restore list view column headers
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:
// Gets subitem from column index
int IndexToSubItem(int nIndex);
// Gets column index from subitem
int SubItemToIndex(int nSubItem);
// Deletes all list-view columns
void DeleteAllColumns();
// Returns TRUE if the specified columns already exist in the list-view
BOOL IsColumnVisible(int nSubItem);
// Call this function to show/insert a specified column
void ShowColumn(int nSubItem);
// Call this function to hide/remove a specified column
void HideColumn(int nSubItem);
// Show or hide a specified column
void ToggleColumn(int nSubItem);
// Saves the columns state to the system registry (HKEY_CURRENT_USER)
BOOL SaveState(LPCTSTR lpszSubKey, LPCTSTR lpszValueName);
// Loads the columns state from the system registry (HKEY_CURRENT_USER)
BOOL LoadState(LPCTSTR lpszSubKey, LPCTSTR lpszValueName);
protected:
// You must override this function, explained later
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:
// Columns subitem ids
#define SUBITEM_ID 0
#define SUBITEM_NAME 1
#define SUBITEM_AGE 2
#define SUBITEM_EMAIL 3
#define SUBITEM_ADDRESS 4
#define SUBITEM_JOB 5
//===============================================
// Demo list view class
//===============================================
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)
{
// Insert default columns
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