This project is about ListEx, which is an advanced MFC List Control with a lot of features.
Introduction
ListEx
is an advanced owner-drawn List Control, an extended version of the CMFCListCtrl
class.
Table of Contents
Features
Installation
The usage of the control is simple:
- Add ListEx.h and CListEx.cpp into your project
- 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:
LISTEX::
or to define namespace in the source file's beginning:
using namespace LISTEX;
Create
Manually
Create
is the main method to create ListEx
control, it takes LISTEXCREATE
structure as an argument.
Below is a simple example of the ListEx
creation:
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 ListEx
in a Dialog, you can manually do it with the Create method.
But most of the time, you prefer to place a standard List Control onto a Dialog
's template, by dragging it from the Toolbox within Visual Studio.
To use the latter approach, follow these steps:
- Put a standard List Control from the toolbox onto your dialog.
- Declare
IListExPtr
member variable within your dialog class: IListExPtr m_myList { CreateListEx() };
- In your
OnInitDialog
method call the m_myList->CreateDialogCtrl(ID_OF_THE_LIST, this);
method.
Tooltips
To set a tooltip for a given cell:
myList->SetCellTooltip(0, 1, L"Tooltip text", L"Tooltip caption:");
This will set a tooltip for a Cell (0, 1)
with the text: Tooltip text, and the caption Tooltip caption.
Sorting
To enable a 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 ListEx
performs lexicographical sorting.
To set your own sorting routine, use the SetSortable
method.
Editing Cells
By default, ListEx
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 List Control allows setting an alignment only for a header and column simultaneously.
ListEx
allows setting alignment separately for the header and the data. The iDataAlign
argument in the InsertColumn()
method is responsible exactly for that.
Public Methods
IListEx
class also has a set of additional methods to help customize your control in many different aspects.
HideColumn
void HideColumn(int iIndex, bool fHide);
Hide or show column by iIndex
.
SetCellColor
SetCellColor(int iItem, int iSubitem, COLORREF clrBk, COLORREF clrText = -1);
Sets background and text color for a given cell.
SetCellData
void SetCellData(int iItem, int iSubitem, ULONGLONG ullData);
Sets arbitrary application-defined data associated with given cell.
SetCellTooltip
void SetCellTooltip(int iItem, int iSubitem,
std::wstring_view wstrTooltip, std::wstring_view wstrCaption = L"");
Sets tooltip and caption for a given cell.
SetColumnEditable
void SetColumnEditable(int iColumn, bool fEditable);
Enables or disables edit mode for a given column.
SetHdrColumnIcon
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
void SetHdrHeight(DWORD dwHeight);
SetSortable
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 the type int (CALLBACK *PFNLVCOMPARE)(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
that is used to set your own comparison function. If it's nullptr
ListEx
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
struct LISTEXCREATE {
LISTEXCOLORS stColor { }; CRect rect; CWnd* pParent { }; LOGFONTW* pListLogFont { }; LOGFONTW* pHdrLogFont { }; UINT uID { }; DWORD dwStyle { }; DWORD dwListGridWidth { 1 }; DWORD dwHdrHeight { }; bool fDialogCtrl { false }; bool fSortable { false }; bool fLinkUnderline { true }; bool fTooltipBaloon { true }; bool fLinkTooltip { true }; bool fHighLatency { false }; };
LISTEXCOLORS
struct LISTEXCOLORS
{
COLORREF clrListText { GetSysColor(COLOR_WINDOWTEXT) }; COLORREF clrListTextLink { RGB(0, 0, 200) }; COLORREF clrListTextSel { GetSysColor(COLOR_HIGHLIGHTTEXT) }; COLORREF clrListTextLinkSel { RGB(250, 250, 250) }; COLORREF clrListTextCellTt { GetSysColor(COLOR_WINDOWTEXT) }; COLORREF clrListBkOdd { GetSysColor(COLOR_WINDOW) }; COLORREF clrListBkEven { GetSysColor(COLOR_WINDOW) }; COLORREF clrListBkSel { GetSysColor(COLOR_HIGHLIGHT) }; COLORREF clrListBkCellTt { RGB(170, 170, 230) }; COLORREF clrListGrid { RGB(220, 220, 220) }; COLORREF clrTooltipText { GetSysColor(COLOR_INFOTEXT) }; COLORREF clrTooltipBk { GetSysColor(COLOR_INFOBK) }; COLORREF clrHdrText { GetSysColor(COLOR_WINDOWTEXT) }; COLORREF clrHdrBk { GetSysColor(COLOR_WINDOW) }; COLORREF clrHdrHglInact { GetSysColor(COLOR_GRADIENTINACTIVECAPTION) }; COLORREF clrHdrHglAct { GetSysColor(COLOR_GRADIENTACTIVECAPTION) }; COLORREF clrNWABk { GetSysColor(COLOR_WINDOW) }; };
This struct
is also used in SetColor
method.
LISTEXCOLOR
struct LISTEXCOLOR
{
COLORREF clrBk { };
COLORREF clrText { };
};
using PLISTEXCOLOR = LISTEXCOLOR*;
LISTEXHDRICON
struct LISTEXHDRICON
{
POINT pt { }; int iIconIndex { }; bool fClickable { true }; };
EListExSortMode
Enum
showing sorting type for list columns.
enum class EListExSortMode : short
{
SORT_LEX, SORT_NUMERIC
};
Notification Messages
These messages are sent to the parent window in form of WM_NOTIFY
Windows messages.
The lParam
will contain a pointer to the 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 a pointer to the LISTEXCOLOR
struct
in response, or nothing to use defaults.
void CListDlg::OnListExGetColor(NMHDR* pNMHDR, LRESULT* )
{
const auto pNMI = reintepret_cast<NMITEMACTIVATE*>(pNMHDR);
if (pNMI->iSubItem == 1)
{
static LISTEXCOLOR clr { RGB(0, 220, 220), RGB(0, 0, 0) };
pNMI->lParam = reinterpret_cast<LPARAM>(&clr);
}
}
LISTEX_MSG_GETICON
void CListDlg::OnListExGetIcon(NMHDR* pNMHDR, LRESULT* )
{
const auto pNMI = reinterpret_cast<NMITEMACTIVATE*>(pNMHDR);
...
pNMI->lParam = SomeIndex; }
This message is used in Virtual Mode to obtain an icon index in the List
's 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.
The link
and the title
's text must be quoted ""
.
LISTEX_MSG_HDRICONCLICK
Header icon that previously was set by the SetHdrColumnIcon
method has been clicked.
Example code for handling this message:
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);
}
...
}
LISTEX_MSG_EDITBEGIN
Sent when the 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.
BOOL CMyDlg::OnNotify(WPARAM wParam, LPARAM lParam, LRESULT* pResult)
{
const auto pNMI = reinterpret_cast<LPNMITEMACTIVATE>(lParam);
pNMI->lParam = 0; ...
}
LISTEX_MSG_DATACHANGED
Sent in the Virtual Mode when cell's text has been changed.
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 ListEx
control with the non standard header height, and yellow background color. Nothing is simpler, see the code below:
LISTEXCREATE lcs;
lcs.rect = CRect(0, 0, 500, 300)
lcs.pParent = this;
lcs.dwHdrHeight = 50;
lcs.stColor.clrListBkOdd = RGB(255, 255, 0);
lcs.stColor.clrListBkEven = RGB(255, 255, 0);
IListExPtr myList { CreateListEx() };
myList->Create(lcs);
myList->InsertColumn(...);
myList->InsertItem(...);
Here, we set both even and odd rows to the same yellow color.
Appearance
With the Ctrl+MouseWheel, you can dynamically change the ListEx
's font size.