Click here to Skip to main content
15,917,859 members
Articles / Desktop Programming / ATL

Customize List View Column Headers

Rate me:
Please Sign up or sign in to vote.
4.82/5 (4 votes)
20 Sep 2023CPOL2 min read 8.6K   102   8   4
Show or hide, reorder, save and restore list view column headers
I provide a simple class that enables the user to customize list view control columns.


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
    // 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);

    // 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>
    void OnNeedColumnInfo(int nSubItem, CString& strHeading, int& nFormat, int& nWidth)
        switch (nSubItem)
        case SUBITEM_ID:
            strHeading = _T("ID");
            nFormat = LVCFMT_LEFT;
            nWidth = 100;

        case SUBITEM_NAME:
            strHeading = _T("Name"); 
            nFormat = LVCFMT_LEFT;
            nWidth = 200;

        case SUBITEM_AGE:
            strHeading = _T("Age");
            nFormat = LVCFMT_LEFT;
            nWidth = 70;

        case SUBITEM_EMAIL:
            strHeading = _T("e-mail");
            nFormat = LVCFMT_LEFT;
            nWidth = 150;

        case SUBITEM_ADDRESS:
            strHeading = _T("Address");
            nFormat = LVCFMT_LEFT;
            nWidth = 200;

        case SUBITEM_JOB:
            strHeading = _T("Job");
            nFormat = LVCFMT_LEFT;
            nWidth = 90;

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

Note:  To enabling headers re-ordering use LVS_EX_HEADERDRAGDROP on list view control extended style.

See the demo code to get full information.


  • 19th September, 2023: First version of this class


This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Written By
Iran (Islamic Republic of) Iran (Islamic Republic of)
Visual C++ Enjoyer.

Comments and Discussions

GeneralMy vote of 5 Pin
Ștefan-Mihai MOGA10-Oct-23 20:32
professionalȘtefan-Mihai MOGA10-Oct-23 20:32 
GeneralRe: My vote of 5 Pin
Javad Taheri (drjackool)10-Oct-23 22:55
Javad Taheri (drjackool)10-Oct-23 22:55 
Questioncommenting Pin
Joy Spring Mental Health20-Sep-23 19:39
Joy Spring Mental Health20-Sep-23 19:39 
What if we have to add a row?
AnswerRe: commenting Pin
Javad Taheri (drjackool)21-Sep-23 0:24
Javad Taheri (drjackool)21-Sep-23 0:24 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.