Click here to Skip to main content
14,299,664 members

MFC List Control with Tooltip and Menu Ability

Rate this:
5.00 (14 votes)
Please Sign up or sign in to vote.
5.00 (14 votes)
15 May 2019CPOL
List control with cells tooltips, menu ability, and even more

List Control for MFC Applications

Table of Contents


When I faced a necessity in List control with the ability to display tooltips on arbitrary cells, I was discouraged with the fact that such obvious feature simply doesn’t exist in standard MFC CListCtrl class. That was the main reason behind the creation of this control.

Image 1

Long Story Short

CListEx is a CMFCListCtrl derived class with cells tooltip ability, and even more.
It works with LVS_OWNERDRAWFIXED and LVS_REPORT styles.
The tooltiping is achieved with the 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) in the list, and it will show up on mouse hovering.
Tooltips are implemented as Windows balloons.

To remove tooltip from a cell, just set it again with empty string L"".
Control was built and tested in Visual Studio 2019.


The usage of the control is pretty easy:

  1. Copy ListEx folder into your project's folder.
  2. Add all files from ListEx folder into your project.
  3. Add #include "ListEx/ListEx.h" where you are supposed to use the control.

Control uses its own namespace - LISTEX. So it's up to you, whether to use namespace prefix before declarations:


or to define namespace in the source file's beginning:

using namespace LISTEX;



Create is the main method to create list control. It takes one argument - reference to the LISTEXCREATESTRUCT structure. The LISTEXCREATESTRUCT is a helper structure which fields are described below. This struct is also used in SetColor method.

    PLISTEXCOLORSTRUCT	pstColor { };   //All control's colors. If nullptr defaults are used.
    DWORD				dwStyle;        //Control's styles. Zero for default.
    const				CRect rc;       //Initial rect.
    CWnd*				pwndParent { };	//Parent window.
    UINT				nID { };        //Control Id.
    const				LOGFONT* pListLogFont { };   //List font.
    const				LOGFONT* pHeaderLogFont { }; //List header font.
    DWORD				dwListGridWidth { 1 };       //Width of the list grid.
    DWORD				dwHeaderHeight { 20 };       //List header height.
    bool				fDialogCtrl { false };       //If it's a list within dialog.

pstColor is a pointer to the LISTEXCOLORSTRUCT structure which fields are described below:

	COLORREF		clrListText { GetSysColor(COLOR_WINDOWTEXT) };	//List text color
	COLORREF		clrListBkRow1 { GetSysColor(COLOR_WINDOW) };	//List Bk color of 
                                                                    //the odd rows
	COLORREF		clrListBkRow2 { GetSysColor(COLOR_WINDOW) };	//List Bk color of 
                                                                    //the even rows
	COLORREF		clrListGrid { RGB(220, 220, 220) };				//List grid color
	COLORREF		clrListTextSelected { GetSysColor(COLOR_HIGHLIGHTTEXT) };//Selected 
                                                                             //item text color
	COLORREF		clrListBkSelected { GetSysColor(COLOR_HIGHLIGHT) };		 //Selected item 
                                                                             //bk color
	COLORREF		clrTooltipText { GetSysColor(COLOR_INFOTEXT) };	//Tooltip window text color
	COLORREF		clrTooltipBk { GetSysColor(COLOR_INFOBK) };		//Tooltip window bk color
	COLORREF		clrListTextCellTt { GetSysColor(COLOR_WINDOWTEXT) };//Text color of a cell 
                                                                        //that has tooltip
	COLORREF		clrListBkCellTt { RGB(170, 170, 230) };			//Bk color of a cell 
                                                                    //that has tooltip
	COLORREF		clrHeaderText { GetSysColor(COLOR_WINDOWTEXT) };//List header text color
	COLORREF		clrHeaderBk { GetSysColor(COLOR_WINDOW) };		//List header bk color
	COLORREF		clrBkNWA { GetSysColor(COLOR_WINDOW) };			//Bk of non working area

Below is a simple example of the control's creation:

CListEx myList;
lcs.pwndParent = this;
lcs.nID = ID_MY_LIST;
lcs.rect = CRect(0, 0, 500, 300);


With LISTEXCREATESTRUCT structure, you can set 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

In Dialog

To create list control in Dialog, you can manually create it with the Create method.
But most of the times, you prefer to place a list control onto the Dialog template dragging it from the Toolbox within Visual studio.
To use the latter approach, follow these steps:

  1. Put standard list control from the toolbox onto your dialog template and make it a desirable size.
  2. Right click on the control and choose Add variable from the drop-down menu.
  3. In the Variable type: field put CListEx
  4. In OnInitDialog method, make a call to myList.CreateDialogCtrl();


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

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

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

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

Image 2

This is achieved with the help of two public methods:

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

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

  • For the whole list - CMenu m_menuList;
  • For the individual cell - CMenu m_menuCell;
#include "ListEx/ListEx.h"

using namespace LISTEX;

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 m_myList;

  m_myList.InsertColumn(0, L"First column", 0, 100);

  m_menuCell.AppendMenuW(MF_STRING, IDC_LIST_MENU_CELL_FIRST, L"Cell's first menu...");
  m_menuCell.AppendMenuW(MF_STRING, IDC_LIST_MENU_CELL_SECOND, L"Cell's second menu...");

  m_menuList.AppendMenuW(MF_STRING, IDC_LIST_MENU_GLOBAL_FIRST, L"List's first menu...");
  m_menuList.AppendMenuW(MF_STRING, IDC_LIST_MENU_GLOBAL_SECOND, L"List's second menu...");

  m_myList.SetCellMenu(1, 0, &m_menuCell); //Set menu for row:1 column:0.

Handle Menu Clicks

When user clicks a 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 process 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_LISTEX)
		if (pNMI->hdr.code == LISTEX_MSG_MENUSELECTED)
			CString ss;
			switch (pNMI->lParam)
				ss.Format(L"Cell's first menu clicked. Row: %i, Column: %i", 
                          pNMI->iItem, pNMI->iSubItem);
				ss.Format(L"Cell's second menu clicked. Row: %i, Column: %i", 
                          pNMI->iItem, pNMI->iSubItem);
				ss.Format(L"List's first menu clicked. Row: %i, Column: %i", 
                          pNMI->iItem, pNMI->iSubItem);
				ss.Format(L"List's second menu clicked. Row: %i, Column: %i", 
                          pNMI->iItem, pNMI->iSubItem);

	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.

Public Methods

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

bool IsCreated();
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 SetColor(const LISTEXCOLORSTRUCT& lcs);
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, see code below:

lcs.rect = CRect(0, 0, 500, 300)
lcs.pwndParent = this;
lcs.dwHeaderHeight = 50;
lcs.clrListBkRow1 = RGB(255, 255, 0);
lcs.clrListBkRow2 = RGB(255, 255, 0);
CListEx myList;


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.


  • 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
  • 20th April, 2019 - v1.9 Visual Studio 2019 support. Sample project added. Internal reworks.
  • 16th May, 2019 - v1.9.1 Fixed regression in tooltips.


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

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 
If you mean the ability to set the color for individual cells, well, i thought about this at control's implementation stage, and frankly saying was not sure about if it was really needed.

It's rather rare used feature in my opinion.

But if i'm wrong... well, it can be implemented if there is a real demand on it.
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.

Posted 13 Dec 2018


19 bookmarked