Click here to Skip to main content
15,867,453 members
Articles / Desktop Programming / MFC
Article

MFC List Control with Tooltips, Editing, Colors, Columns hiding, Sorting and lot more abilities.

Rate me:
Please Sign up or sign in to vote.
5.00/5 (27 votes)
6 Apr 2020MIT5 min read 52.2K   2.5K   46   13
Owner-draw list control with tooltips, editing, colors, sorting, hyperlinks, columns hiding and lot more.
In this project, we will take a look at MFC list control for MFC applications. After seeing a list of the main features of the IListEx, we will learn about installation and creation, tooltips, menu and sorting. We will also learn about the public methods, structures and notification messages. Finally, we will see an example.

List control for MFC applications

Image 1

Table of Contents

Introduction

IListEx is an owner-draw extension of the MFC CMFCListCtrl class with many features:

Installation

The usage of the control is simple:

  1. Add ListEx.h and CListEx into your project
  2. Declare IListExPtr variable: IListExPtr myList { CreateListEx() };

IListExPtr is a pointer to the IListEx class, wrapped in std::unique_ptr. This wrapper is used mainly for convenience, so you don't have to bother about object lifetime, it will be destroyed automatically. That's why there is a call to the factory function CreateListEx(), to properly initialize a pointer.

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

C++
LISTEX::

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

C++
using namespace LISTEX;

Create

Manually

Create is the main method to create list control. It takes LISTEXCREATE structure as argument.

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

C++
IListExPtr myList { CreateListEx() };
.
.
LISTEXCREATE lcs;
lcs.pParent = this;
lcs.uID = ID_MY_LIST;
lcs.rect = CRect(0, 0, 500, 300);

myList->Create(lcs);

In Dialog

To create the control in a Dialog you can manually do it with the Create method.

But most of the times you prefer to place a standard List Control onto the Dialog's template, by 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. Give it appropriate ID (IDC_LISTEX) and make it desirable size.
  2. Declare IListExPtr member varable within your dialog class: IListExPtr m_myList { CreateListEx() };
  3. In your OnInitDialog method call m_myList->CreateDialogCtrl(IDC_LISTEX, this); function.

Tooltips

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

C++
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.

Sorting

To enable sorting set the LISTEXCREATE::fSortable flag to true. In this case, when you click on the header, list will be sorted according to the clicked column. By default IListEx performs lexicographical sorting.

To set your own sorting routine use SetSortable method.

Editing Cells

By default list control works in the read-only mode. To enable cells editing call the SetColumnEditable method with the column ID which cells you wish to become editable.

Data Alignment

Classical MFC list control allows setting alignment only for header and column text simultaneously.
ListEx allows setting alignment separately for header and for data respectively. The iDataAlign argument in the InsertColumn() method is responsible exactly for that.

Public Methods

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

HideColumn

C++
void HideColumn(int iIndex, bool fHide);

Hide or show column by iIndex.

SetCellColor

C++
SetCellColor(int iItem, int iSubitem, COLORREF clrBk, COLORREF clrText = -1);

Sets background and text color for a given cell.

SetCellData

C++
void SetCellData(int iItem, int iSubitem, ULONGLONG ullData);

Sets arbitrary application-defined data associated with given cell.

SetCellTooltip

C++
void SetCellTooltip(int iItem, int iSubitem, std::wstring_view wstrTooltip, std::wstring_view wstrCaption = L"");

Sets tooltip and caption for a given cell.

SetColumnEditable

C++
void SetColumnEditable(int iColumn, bool fEditable);

Enables or disables edit mode for a given column.

SetHdrColumnIcon

C++
void SetHdrColumnIcon(int iColumn, int iIconIndex, bool fClick = false);

Sets the icon index in the header's image list for a given iColumn. To remove icon from column set the iIconIndex to -1.
Flag fClick means that icon is clickable. See LISTEX_MSG_HDRICONCLICK message for more info.

SetHdrHeight

C++
void SetHdrHeight(DWORD dwHeight);

SetSortable

C++
void SetSortable(bool fSortable, PFNLVCOMPARE pfnCompare = nullptr, EListExSortMode enSortMode = EListExSortMode::SORT_LEX)

Parameters:

bool fSortable
Enables or disables sorting

PFNLVCOMPARE pfnCompare
Callback function pointer with type int (CALLBACK *PFNLVCOMPARE)(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort) that is used to set your own comparison function. If it's nullptr IListEx performs default sorting.
The comparison function must be either a static member of a class or a stand-alone function that is not a member of any class. For more information see official MSDN documentation.

EListExSortMode enSortMode
Default sorting mode for the list.

Structures

LISTEXCREATE

C++
struct LISTEXCREATE {
    LISTEXCOLORS stColor { };             //All control's colors.
    CRect        rect;                    //Initial rect.
    CWnd*        pParent { };             //Parent window.
    LOGFONTW*    pListLogFont { };        //List font.
    LOGFONTW*    pHdrLogFont { };         //Header font.
    UINT         uID { };                 //List control ID.
    DWORD        dwStyle { };             //Control's styles. Zero for default.
    DWORD        dwListGridWidth { 1 };   //Width of the list grid.
    DWORD        dwHdrHeight { };         //Header height.
    bool         fDialogCtrl { false };   //If it's a list within dialog.
    bool         fSortable { false };     //Is list sortable, by clicking on the header column?
    bool         fLinkUnderline { true }; //Links are displayed underlined or not.
    bool         fTooltipBaloon { true }; //Baloon type tooltip for cells.
    bool         fLinkTooltip { true };   //Show links' toolips.
    bool         fHighLatency { false };  //Do not redraw window until scrolling completes.
};

LISTEXCOLORS

C++
struct LISTEXCOLORS
{
    COLORREF clrListText { GetSysColor(COLOR_WINDOWTEXT) };       //List text color.
    COLORREF clrListTextLink { RGB(0, 0, 200) };                  //List hyperlink text color.
    COLORREF clrListTextSel { GetSysColor(COLOR_HIGHLIGHTTEXT) }; //Selected item text color.
    COLORREF clrListTextLinkSel { RGB(250, 250, 250) };           //List hyperlink text color in selected cell.
    COLORREF clrListTextCellTt { GetSysColor(COLOR_WINDOWTEXT) }; //Text color of a cell that has tooltip.
    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 clrListBkSel { GetSysColor(COLOR_HIGHLIGHT) };       //Selected item bk color.
    COLORREF clrListBkCellTt { RGB(170, 170, 230) };              //Bk color of a cell that has tooltip.
    COLORREF clrListGrid { RGB(220, 220, 220) };                  //List grid color.
    COLORREF clrTooltipText { GetSysColor(COLOR_INFOTEXT) };      //Tooltip window text color.
    COLORREF clrTooltipBk { GetSysColor(COLOR_INFOBK) };          //Tooltip window bk color.
    COLORREF clrHdrText { GetSysColor(COLOR_WINDOWTEXT) };        //List header text color.
    COLORREF clrHdrBk { GetSysColor(COLOR_WINDOW) };              //List header bk color.
    COLORREF clrHdrHglInact { GetSysColor(COLOR_GRADIENTINACTIVECAPTION) };//Header highlight inactive.
    COLORREF clrHdrHglAct { GetSysColor(COLOR_GRADIENTACTIVECAPTION) };    //Header highlight active.
    COLORREF clrNWABk { GetSysColor(COLOR_WINDOW) };              //Bk of Non Working Area.
};

This struct is also used in SetColor method.

LISTEXCOLOR

C++
struct LISTEXCOLOR
{
    COLORREF clrBk { };
    COLORREF clrText { };
};
using PLISTEXCOLOR = LISTEXCOLOR*;

LISTEXHDRICON

C++
struct LISTEXHDRICON
{
    POINT pt { };              //Point of the top-left corner.
    int   iIconIndex { };      //Icon index in the header's image list.
    bool  fClickable { true }; //Is icon sending LISTEX_MSG_HDRICONCLICK message when clicked.
};

EListExSortMode

Enum showing sorting type for list columns.

C++
enum class EListExSortMode : short
{
    SORT_LEX, SORT_NUMERIC
};

Notification Messages

These messages are sent to the parent window in form of WM_NOTIFY windows message.
The lParam will contain pointer to NMHDR standard windows struct. NMHDR::code can be one of the LISTEX_MSG_... messages described below.

LISTEX_MSG_GETCOLOR

When in virtual mode, sent to the parent window to retrieve cell's color. Expects pointer to the LISTEXCOLOR struct in response, or nothing to use defaults.

C++
void CListDlg::OnListExGetColor(NMHDR* pNMHDR, LRESULT* /*pResult*/)
{
    const auto pNMI = reintepret_cast<NMITEMACTIVATE*>(pNMHDR);

    //For column number 1 (all rows) set color to RGB(0, 220, 220).
    if (pNMI->iSubItem == 1)
    {
        static LISTEXCOLOR clr { RGB(0, 220, 220), RGB(0, 0, 0) };
        pNMI->lParam = reinterpret_cast<LPARAM>(&clr);
    }
}

LISTEX_MSG_GETICON

C++
void CListDlg::OnListExGetIcon(NMHDR* pNMHDR, LRESULT* /*pResult*/)
{
    //Virtual data icons.
    const auto pNMI = reinterpret_cast<NMITEMACTIVATE*>(pNMHDR);
    ...
	
    pNMI->lParam = SomeIndex; //Icon index in list's image list.
}

This message is used in Virtual List mode to obtain icon index in list image list.

LISTEX_MSG_LINKCLICK

List embedded hyperlink has been clicked. WM_NOTIFY lParam will point to the NMITEMACTIVATE struct.
NMITEMACTIVATE::lParam will contain wchar_t* pointer to the link text of the clicked hyperlink. The iItem and iSubItem members will contain indexes of the list item/subitem the link was clicked at.

Hyperlink syntax is: L"Text with the <link="any_text_here" title="Optional tool-tip text">embedded link</link>"
If no optional title tag is provided then the link text itself will be used as hyperlink's tool-tip.
Link and title text must be quoted "".

LISTEX_MSG_HDRICONCLICK

Header icon that previously was set by SetHdrColumnIcon call has been clicked.
Example code for handling this message:

C++
BOOL CMyDlg::OnNotify(WPARAM wParam, LPARAM lParam, LRESULT* pResult)
{
    const auto pNMI = reinterpret_cast<LPNMITEMACTIVATE>(lParam);

    if (pNMI->hdr.code == LISTEX_MSG_HDRICONCLICK && pNMI->hdr.idFrom == IDC_MYLIST)
    {
    	const auto pNMI = reinterpret_cast<NMHEADERW*>(lParam);
    	//pNMI->iItem holds clicked column index.
    }
    ...

LISTEX_MSG_EDITBEGIN

Sent when edit box for data editing is about to show up. If you don't want it to show up, you can set lParam to 0 in response.

C++
BOOL CMyDlg::OnNotify(WPARAM wParam, LPARAM lParam, LRESULT* pResult)
{
    const auto pNMI = reinterpret_cast<LPNMITEMACTIVATE>(lParam);
    pNMI->lParam = 0; //Edit-box won't show up.
    ...

LISTEX_MSG_DATACHANGED

Sent in Virtual mode when cell's text has changed.

C++
BOOL CMyDlg::OnNotify(WPARAM wParam, LPARAM lParam, LRESULT* pResult)
{
    const auto pNMI = reinterpret_cast<LPNMITEMACTIVATE>(lParam);
    const auto iItem = pNMI->iItem;
    const auto iSubItem = pNMI->iSubItem;
    const pwszNewText = reinterpret_cast<LPCWSTR>(pNMI->lParam);
    ...

Example

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

C++
LISTEXCREATE lcs;
lcs.rect = CRect(0, 0, 500, 300)
lcs.pParent = this;
lcs.dwHdrHeight = 50;
lcs.stColor.clrListBkRow1 = RGB(255, 255, 0);
lcs.stColor.clrListBkRow2 = RGB(255, 255, 0);

myList->Create(lcs);

myList->InsertColumn(...);
myList->InsertItem(...);

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

Appearance

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

This article was originally posted at https://github.com/jovibor/ListEx

License

This article, along with any associated source code and files, is licensed under The MIT License


Written By
Zaire Zaire
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionCan not work on a Dialog Pin
suhm7723-Dec-18 20:49
suhm7723-Dec-18 20:49 
AnswerRe: Can not work on a Dialog Pin
Jovibor23-Dec-18 23:47
Jovibor23-Dec-18 23:47 
GeneralRe: Can not work on a Dialog Pin
suhm7724-Dec-18 3:41
suhm7724-Dec-18 3:41 
GeneralRe: Can not work on a Dialog Pin
Jovibor24-Dec-18 12:00
Jovibor24-Dec-18 12:00 
Dude, you declare CListEx member variable (CListEx myList;) in ClistTestDlg::OnInitDialog() function. It destroys as soon as function returns.
No wonder it doesn't work;
Move its declaration in your class header file - listTestDlg.h.
GeneralRe: Can not work on a Dialog Pin
suhm7724-Dec-18 21:00
suhm7724-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
Jovibor19-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
Jovibor19-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
Jovibor15-Dec-18 11:46 
GeneralRe: Feature Suggestion Pin
Jovibor16-Dec-18 12:56
Jovibor16-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.