In runtime, I'm trying to create a single-column custom CListCtrl
(or CMFCListCtrl
, but not CheckListBox
- I want to be able to add multiple columns in the future) using MFC. Using LVS_EX_CHECKBOXES
style forces all items to have the checkbox. The desired control should look like this (item1 and item3 have checkboxes, item2 doesn't):
From the user's point of view, the desired list control should be created like this:
int main() {
MyCListCtrl list_control;
list_control.AddItem("item1", true) // true indicates checkbox presence
list_control.AddItem("item2", false) // false - item without checkbox
list_control.AddItem("item3", true) // true indicates checkbox presence
}
So far I was able to create a control like this, but adding LVS_OWNERDRAWFIXED
triggers a failed assertion, when calling base class CListCtrl::DrawItem
method:
// MyCListCtrl.h
class MyCListCtrl : public CListCtrl {
public:
virtual void DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct) override {
// if the item should be without a checkbox, here I want to move it a few pixels
// to the left so that the checkbox is hidden
...
CListCtrl::DrawItem(lpDrawItemStruct); // call base's DrawItem - without this
// there's no exception but the listbox appears empty
}
};
BOOL MyCDialogEx::OnInitDialog() {
CDialogEx::OnInitDialog();
...
// list being defined somewhere in the header file
list->Create(WS_CHILD | WS_VISIBLE | WS_BORDER | LVS_REPORT | LVS_NOCOLUMNHEADER |
LVS_OWNERDRAWFIXED, // for DrawItem invocation
rect, this, SOME_ID);
list->SetExtendedStyle(list->GetExtendedStyle() | LVS_EX_CHECKBOXES);
// add 1 mandatory column because of LVS_REPORT style
LVCOLUMN lvColumn;
lvColumn.mask = LVCF_FMT | LVCF_TEXT | LVCF_WIDTH;
lvColumn.fmt = LVCFMT_LEFT;
lvColumn.cx = rect.Width() - 20; // also, how to make this column fit the width exactly?
lvColumn.pszText = nullptr;
list->InsertColumn(0, &lvColumn);
// for now only add 1 testing item and make his checkbox disappear by moving the
// whole item to the left in DrawItem method (called by the system), so that the text
// is aligned to the left list border
list->InsertItem(0, "item1");
...
}
This is how my (not working) solution looks like, if you know how to solve this, maybe even in an easier way, please let me know. Thanks.
EDIT
With @Landstalker 's help, I'm now able to erase the checkbox with the custom drawing, but I still need to move the text to the left (so it takes the place of a non-existing checkbox, like on the picture above). Current solution results in this result:
This is achieved by handling the NM_CUSTOMDRAW message like this:
void MyCListCtrl::OnCustomDraw(NMHDR* pNMHDR, LRESULT* pResult)
{
*pResult = CDRF_DODEFAULT; // default windows painting
LPNMLVCUSTOMDRAW lpn = (LPNMLVCUSTOMDRAW)pNMHDR;
if (CDDS_PREPAINT == lpn->nmcd.dwDrawStage)
{
*pResult = CDRF_NOTIFYITEMDRAW; // notify on every item
}
else if (CDDS_ITEMPREPAINT == lpn->nmcd.dwDrawStage)
{
int row = lpn->nmcd.dwItemSpec;
if (row == 1) {
lpn->nmcd.rc.left -= 16; // not working
lpn->rcText.left -= 16; // not working
SetItemState(row, INDEXTOSTATEIMAGEMASK(0),
LVIS_STATEIMAGEMASK); // erase checkbox
}
}
}
After long investigations ... I found a solution for you: use
SetItemState ()
magic function :Remarque : Having multiple columns is not a problem
MyCListCtrl.h
MyCListCtrl.cpp
MainDlg.cpp