Click here to Skip to main content
13,902,979 members
Click here to Skip to main content
Add your own
alternative version


8 bookmarked
Posted 13 Dec 2018
Licenced CPOL

MFC CListEx - List Control with Tooltip Ability

, 28 Feb 2019
Rate this:
Please Sign up or sign in to vote.
List control with cells tooltips, menu ability, and even more


When I first faced a necessity in List Control with the ability to show tooltips on individual cells, I was discouraged with the fact that such obvious feature simply doesn’t exist in standard MFC CListCtrl.

Well, apparently it’s time to implement it.

Long Story Short

CListEx is a CMFCListCtrl derived class with cell’s tooltip ability. And even more.
It works with LVS_OWNERDRAWFIXED and LVS_REPORT styles. The tooltiping is achieved with public method:

void SetCellTooltip(int iItem, int iSubitem, const std::wstring& strTooltip, 
                    const std::wstring& strCaption = { });

With this method, you can set a tooltip for any cell (item/subitem), that will show up on mouse hovering. Tooltips are implemented as Windows balloons.

To remove tooltip from cell, just set it again with empty string (L"").

Control was built and tested in Visual Studio 2017.


  1. Add two files in your project: ListEx.h and ListEx.cpp.
  2. Add #include "ListEx.h"
  3. Control uses its own namespace, so either type using namespase LISTEX, or use LISTEX:: prefix when declaring CListEx variable: LISTEX::CListEx myList;


To set a tooltip for a given cell, just write:

myList.SetCellTooltip(0, 1, L"Tooltip", L"Tooltip caption:");

This will set a tooltip for cell (0, 1) with text: "Tooltip", and caption "Tooltip caption".


CListEx class possesses innate ability to set popup menu for individual cells, as well as for the whole list.

CListEx::SetListMenu(CMenu* pMenu);
CListEx::SetCellMenu(int iItem, int iSubitem, CMenu* pMenu);

Menu Example

With the code below, we are going to set two separate menus:

  1. For the whole list - CMenu m_menuList
  2. For the individual cell - CMenu m_menuCell
#include "ListEx.h"

using namespace LISTEX;

constexpr auto IDC_LIST_ID = 0xff;
constexpr auto IDC_LIST_MENU_CELL_FIRST = 0x1;
constexpr auto IDC_LIST_MENU_CELL_SECOND = 0x2;
constexpr auto IDC_LIST_MENU_GLOBAL_FIRST = 0x3;
constexpr auto IDC_LIST_MENU_GLOBAL_SECOND = 0x4;

class CMyDlg : public CDialogEx
  CMenu m_menuCell;
  CMenu m_menuList;
  CListEx list;

  list.Create(WS_VISIBLE, CRect(0, 0, 500, 500), this, IDC_LIST_ID);
  list.InsertColumn(0, L"First column", 0, 100);

  m_menuCell.AppendMenuW(MF_STRING, IDC_LIST_MENU_CELL_FIRST, L"Cell first submenumenu...");
  m_menuCell.AppendMenuW(MF_STRING, IDC_LIST_MENU_CELL_SECOND, L"Cell second submenu...");

  m_menuList.AppendMenuW(MF_STRING, IDC_LIST_MENU_GLOBAL_FIRST, L"List global first submenu...");
  m_menuList.AppendMenuW(MF_STRING, IDC_LIST_MENU_GLOBAL_SECOND, L"List global second submenu...");

  list.SetCellMenu(1, 2, &m_menuCell);

Handle Menu Clicks

When user clicks menu, CListEx sends WM_NOTIFY message, with NMITEMACTIVATE struct pointer as lParam, to its parent window. So in order to properly handle clicks, you have to handle this message in your list's parent window:

BOOL CMyDialog::OnNotify(WPARAM wParam, LPARAM lParam, LRESULT* pResult)
    const LPNMITEMACTIVATE pNMI = reinterpret_cast<LPNMITEMACTIVATE>(lParam);
    if (pNMI->hdr.idFrom == IDC_LIST_ID)
        if (pNMI->hdr.code == LISTEX_MSG_MENUSELECTED)
            switch (pNMI->lParam)
            case IDC_LIST_MENU_CELL_FIRST:
            case IDC_LIST_MENU_CELL_SECOND:
            case IDC_LIST_MENU_GLOBAL_FIRST:
    return CDialogEx::OnNotify(wParam, lParam, pResult);

CListEx fills NMITEMACTIVATE struct with NMITEMACTIVATE::hdr.code equals LISTEX_MSG_MENUSELECTED. And menuId is stored as NMITEMACTIVATE::lParam.

NMITEMACTIVATE::iItem and NMITEMACTIVATE::iSubItem both point to a cell the menu was clicked on.

But It’s Not Yet It

Additionally, in CListEx::Create method, there is one additional parameter - pointer to LISTEXINFO struct. This parameter goes last so it doesn’t break compatibility with the existing code, you can simply change the class of your existing CListCtrl members to CListEx.

What's Inside

The LISTEXINFO is a helper structure whose fields are described below:

typedef struct LISTEXINFO {
    COLORREF clrListText;          //List text color.
    COLORREF clrListBkRow1;        //List Bk color for odd rows.
    COLORREF clrListBkRow2;        //List Bk color for even rows.
    COLORREF clrListGrid;          //List grid color.
    DWORD dwListGridWidth;         //Width of the list grid.
    COLORREF clrListTextSelected;  //Selected item text color.
    COLORREF clrListBkSelected;    //Selected item bk color.
    COLORREF clrTooltipText;       //Tooltip window text color.
    COLORREF clrTooltipBk;         //Tooltip window bk color.
    COLORREF clrListTextCellTt;    //Text color of cell that has tooltip.
    COLORREF clrListBkCellTt;      //Bk color of cell that has tooltip.
    const LOGFONT* pListLogFont;   //List font.
    COLORREF clrHeaderText;        //List header text color.
    COLORREF clrHeaderBk;          //List header bk color.
    DWORD dwHeaderHeight;          //List header height.
    const LOGFONT* pHeaderLogFont; //List header font.

Every member of this struct has its default value, so you don’t have to worry about filling them all, only the fields you are about to customize.

With this structure alone and with additional methods, you can tune a plethora of list’s aspects:

  • Color of the list text and bk (background). Bk is set separately for odd and even rows
  • Color of the list header
  • Height of the list header
  • Font of the list header, and font of list itself
  • Color of individual header's columns
  • Color of tooltip's window text and bk
  • Color of the text and bk of a cell that has tooltip
  • Color of list grid, and even its width
  • Color of row when it's selected

Public Methods

CListEx class also has a set of additional public methods to help customize your control in many different aspects.

void SetFont(const LOGFONT* pLogFontNew);
        void SetFontSize(UINT uiSize);
        UINT GetFontSize();
        void SetCellTooltip(int iItem, int iSubitem, const std::wstring& wstrTooltip, 
                            const std::wstring& wstrCaption = { });
        void SetCellMenu(int iItem, int iSubitem, CMenu* pMenu);
        void SetListMenu(CMenu* pMenu);
        void SetCellData(int iItem, int iSubitem, DWORD_PTR dwData); //Arbitrary data for cell.
        DWORD_PTR GetCellData(int iItem, int iSubitem);
        void SetTooltipColor(COLORREF clrTooltipText, COLORREF clrTooltipBk,
            COLORREF clrTextCellTt,  //Text color of a cell that has tooltip.
            COLORREF clrBkCellTt);   //Bk color of a cell that has tooltip.
        void SetHeaderColor(COLORREF clrHdrText, COLORREF clrHdrBk);
        void SetHeaderHeight(DWORD dwHeight);
        void SetHeaderFont(const LOGFONT* pLogFontNew);
        void SetHeaderColumnColor(DWORD nColumn, COLORREF clr);


Let’s imagine that you need a list control with a non standard header height, and yellow background color.
Nothing is simpler, just write:

listInfo.dwHeaderHeight = 50;
listInfo.clrListBkRow1 = RGB(255, 255, 0);
listInfo.clrListBkRow2 = RGB(255, 255, 0);
CListEx myList;
myList.Create(WS_CHILD | WS_VISIBLE, CRect(0, 0, 800, 400), this, 0x1, &listInfo);

Here, we set both - even and odd rows (clrListBkRow1 and clrListBkRow2) to the same yellow color.


With the Ctrl+MouseWheel combination, you can dynamically change list's font size.


If you have any useful feature to suggest, or if you found a bug — please let me know in the comments section below.

Licencing and Contribution

You can use this code for free with the only condition — to provide the link back to the author, in the form of:


  • 22nd December, 2018 - v1.2 Added SetHeaderColumnColor method
  • 5th January, 2019 - v1.3 Small fixes
  • 13th January, 2019 - v1.4 Many improvements and fixes, dynamic font sizing added
  • 23rd January, 2019 - v1.5 Set menu ability added, other improvements
  • 29th January, 2019 - v1.6 Resource memory leak has been fixed
  • 1st March, 2019 - v1.7 Some overall improvements


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


About the Author

Russian Federation Russian Federation
No Biography provided

You may also be interested in...

Comments and Discussions

QuestionCan not work on a Dialog Pin
suhm7723-Dec-18 20:49
membersuhm7723-Dec-18 20:49 
AnswerRe: Can not work on a Dialog Pin
Jovibor23-Dec-18 23:47
memberJovibor23-Dec-18 23:47 
GeneralRe: Can not work on a Dialog Pin
suhm7724-Dec-18 3:41
membersuhm7724-Dec-18 3:41 
GeneralRe: Can not work on a Dialog Pin
Jovibor24-Dec-18 12:00
memberJovibor24-Dec-18 12:00 
GeneralRe: Can not work on a Dialog Pin
suhm7724-Dec-18 21:00
membersuhm7724-Dec-18 21:00 
QuestionSome Enhancements Pin
Rick York19-Dec-18 6:55
mveRick York19-Dec-18 6:55 
AnswerRe: Some Enhancements Pin
Jovibor19-Dec-18 11:17
memberJovibor19-Dec-18 11:17 
GeneralRe: Some Enhancements Pin
Rick York19-Dec-18 12:15
mveRick York19-Dec-18 12:15 
GeneralRe: Some Enhancements Pin
Jovibor19-Dec-18 13:41
memberJovibor19-Dec-18 13:41 
SuggestionFeature Suggestion Pin
Rick York15-Dec-18 8:02
mveRick York15-Dec-18 8:02 
GeneralRe: Feature Suggestion Pin
Jovibor15-Dec-18 11:46
memberJovibor15-Dec-18 11:46 
GeneralRe: Feature Suggestion Pin
Jovibor16-Dec-18 12:56
memberJovibor16-Dec-18 12:56 
GeneralRe: Feature Suggestion Pin
Rick York16-Dec-18 17:03
mveRick York16-Dec-18 17:03 

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.

Permalink | Advertise | Privacy | Cookies | Terms of Use | Mobile
Web03 | 2.8.190306.1 | Last Updated 28 Feb 2019
Article Copyright 2018 by Jovibor
Everything else Copyright © CodeProject, 1999-2019
Layout: fixed | fluid