Change Background color of full column of CListCtrl in MFC - c++

Ive made a CListCtrl in Report View in MFC.
I want to color the first column (the full column, not only those cells where an Item is) with a grey background.
How can I do this?
Thank

The way you will implement this is color each cell of the first row individually. The code will look something like below, which basically a blue print but It should work (Note:I haven't test this for this post). You will have to use lplvcd->iSubItem and paint the first column of each row.
void MyList::OnNMCustomdraw(NMHDR *pNMHDR, LRESULT *pResult)
{
NMLVCUSTOMDRAW* cd = reinterpret_cast<NMLVCUSTOMDRAW*>(pNMHDR);
*pResult = CDRF_DODEFAULT;
switch( cd->nmcd.dwDrawStage)
{
case CDDS_PREPAINT:
*pResult = CDRF_NOTIFYITEMDRAW;
break;
case CDDS_ITEMPREPAINT:
{
int rowNumber = cd->nmcd.dwItemSpec;
bool highlightRow = (bool)GetItemData(rowNumber);
if (highlightRow)
{
COLORREF backgroundColor;
backgroundColor = RGB(255, 0, 0);
cd->clrTextBk = backgroundColor;
}
}
break;
case CDDS_SUBITEM | CDDS_ITEMPREPAINT:
{
// something like if(lplvcd->iSubItem == 0 ) to paint first column
lplvcd->clrText = RGB(0,0,255);
*pResult = CDRF_NEWFONT;
return;
}
default:
break;
}
}

Here two perfect articles that describe custom draw in detail.
Part I & Part II

Related

CMFCListCtrl force selected item to have red color

I have my own CMFCListCtrl derived class where I implemented the
virtual COLORREF OnGetCellTextColor(int nRow, int nColum)
{
CMyClass* pMyClass = (CMyClass*)GetItemData(nRow);
if (pMyClass && pMyClass->m_bDeleted)
return RGB(255, 0, 0);
return __super::OnGetCellTextColor(nRow, nColum);
}
function for marking red the deleted entries. This works except when a item is selected.
I went to the void CMFCListCtrl::OnCustomDraw(NMHDR* pNMHDR, LRESULT* pResult) function and placed a breakpoint with the condition iRow==selected item on the line
lplvcd->clrText = OnGetCellTextColor(iRow, iColumn);
executed it, then I created a new Data Breakpoint for &lplvcd->clrText.
The Data Breakpoint got hit on function
comctl32.dll!SHThemeComputeTextColors()
, as the callstack image shows:
, which is clearly overriding the variable value.
As I search for SHThemeComputeTextColors on Internet and nothing appears, can somebody help me on forcing the selected items text to be red?
Changing the colors and fonts of highlighted items is trickier than it seems, because highlighting selected items is a system-wide issue. In fact, it was something I had delayed for a long time, and just today I've finally tackled...
There are at least two ways to change the looks of a list control: owner draw (you have to do all the drawing yourself) and custom draw (the system tells you when it is about to do some steps of the drawing and lets you change the color, the font, etc.). This answer is about custom draw. This article covers the basics of using Custom Draw with CListCtrl.
How to change the highlight color in a CListCtrl (not a CMFCListCtrl, we'll get to that soon) is explained in this other article. These are the steps you have to do:
Intercept the listview draw routine just before it is about to draw a highlighted row (item).
Turn off the row highlight.
Set the row colors to whatever you want.
Let the listview draw the row.
Intercept the listview draw routine after it has drawn the row (post-draw item).
Turn this row's highlighting back on.
So, when the listview is about to draw a highlighted row, you have to telll the system the row is not highlighted so it doesn't use the system colors to paint it, tell what color to use, and set the item as highlighted again, so the selection works as usual.
Here is the code from that article:
COLORREF g_MyClrFgHi; // My foreground hilite color
COLORREF g_MyClrBgHi; // My background hilite color
HWND g_hListView; // Window handle of listview control
void EnableHighlighting(HWND hWnd, int row, bool bHighlight)
{
ListView_SetItemState(hWnd, row, bHighlight? 0xff: 0, LVIS_SELECTED);
}
bool IsRowSelected(HWND hWnd, int row)
{
return ListView_GetItemState(hWnd, row, LVIS_SELECTED) != 0;
}
bool IsRowHighlighted(HWND hWnd, int row)
{
// We check if row is selected.
// We also check if window has focus. This was because the original listview
// control I created did not have style LVS_SHOWSELALWAYS. So if the listview
// does not have focus, then there is no highlighting.
return IsRowSelected(hWnd, row) && (::GetFocus(hWnd) == hWnd);
}
BOOL OnNotify(WPARAM wParam, LPARAM lParam, LRESULT* pResult)
{
static bool bIsHighlighted = false;
*pResult = 0;
NMHDR *p = (NMHDR *)lParam;
switch (p->code)
{
...
case NM_CUSTOMDRAW:
NMLVCUSTOMDRAW *lvcd = (NMLVCUSTOMDRAW *)p;
NMCUSTOMDRAW &nmcd = lvcd->nmcd;
switch (nmcd.dwDrawStage)
{
case CDDS_PREPAINT:
// We want item prepaint notifications, so...
*pResult = CDRF_NOTIFYITEMDRAW;
break;
case CDDS_ITEMPREPAINT:
{
int iRow = (int)nmcd.dwItemSpec;
bHighlighted = IsRowHighlighted(g_hListView, iRow);
if (bHighlighted)
{
lvcd->clrText = g_MyClrFgHi; // Use my foreground hilite color
lvcd->clrTextBk = g_MyClrBgHi; // Use my background hilite color
// Turn off listview highlight otherwise it uses the system colors!
EnableHighlighting(g_hListView, iRow, false);
}
// We want item post-paint notifications, so...
*pResult = CDRF_DODEFAULT | CDRF_NOTIFYPOSTPAINT;
break;
}
case CDDS_ITEMPOSTPAINT:
{
if (bHighlighted)
{
int iRow = (int)nmcd.dwItemSpec;
// Turn listview control's highlighting back on now that we have
// drawn the row in the colors we want.
EnableHighlighting(g_hListView, iRow, true);
}
*pResult = CDRF_DODEFAULT;
break;
}
default:
*pResult = CDRF_DODEFAULT;
break;
}
break;
...
}
}
This works fine with a CListCtrl, but you are asking about a CMFCListCtrl. The problem is that CMFCListCtrl is already asking to be notified about NM_CUSTOMDRAW notifications (that's when it calls the OnGetCellTextColor function, as you've seen). If you create a handler for that in your own CMFCListCtrl-derived class, those notifications won't get to CMFCListCtrl and you lose that functionality (it may fine, depending on your needs).
So here is what I've done: I've created a NM_CUSTOMDRAW handler in my list control and, if I'm dealing with a highlighted row, I change the colors, otherwise, I call CMFCListCtrl::OnNMCustomDraw(). As you'll see, I just use the normal highlight colors; that's because I just wanted to see the selected items even when the control doesn't have the focus:
void CMyListCtrl::OnNMCustomdraw(NMHDR* pNMHDR, LRESULT* pResult)
{
bool callParent = true;
static bool bHighlighted = false;
LPNMLVCUSTOMDRAW lpLVCustomDraw = reinterpret_cast<LPNMLVCUSTOMDRAW>(pNMHDR);
NMCUSTOMDRAW nmcd = lpLVCustomDraw->nmcd;
*pResult = CDRF_DODEFAULT;
switch (lpLVCustomDraw->nmcd.dwDrawStage)
{
case CDDS_PREPAINT:
*pResult = CDRF_NOTIFYITEMDRAW;
break;
case CDDS_ITEMPREPAINT:
{
int row = nmcd.dwItemSpec;
bHighlighted = IsRowHighlighted(row);
if (bHighlighted)
{
lpLVCustomDraw->clrText = GetSysColor(COLOR_HIGHLIGHTTEXT);
lpLVCustomDraw->clrTextBk = GetSysColor(COLOR_HIGHLIGHT);
EnableHighlighting(row, false);
*pResult = CDRF_DODEFAULT | CDRF_NOTIFYPOSTPAINT;
callParent = false;
}
}
break;
case CDDS_ITEMPOSTPAINT:
if (bHighlighted)
{
int row = nmcd.dwItemSpec;
EnableHighlighting(row, true);
callParent = false;
}
*pResult = CDRF_DODEFAULT;
break;
default:
break;
}
if (callParent)
{
__super ::OnCustomDraw(pNMHDR, pResult);
}
}
bool CMyListCtrl::IsRowHighlighted(int row)
{
bool selected = GetItemState(row, LVIS_SELECTED) != 0;
return selected;
}
void CMyListCtrl::EnableHighlighting(int row, bool enable)
{
SetItemState(row, enable ? 0xff : 0, LVIS_SELECTED);
}
I haven't tested it thoroughly, but it seems to work.
UPDATE:
There is a little problem. When you call EnableHigilighting(), the dialog will get LVN_ITEMCHANGED notifications, which can trigger a redraw, which will trigger a LVN_ITEMCHANGED notification... So if you are listening to LVN_ITEMCHANGED notifications, you might need to do something like this:
void CMyListCtrl::EnableHighlighting(int row, bool enable)
{
m_internalStateChange = true;
SetItemState(row, enable ? 0xff : 0, LVIS_SELECTED);
m_internalStateChange = false;
}
void CWhateverDialog::OnLvnItemchangedListaEjesPane(NMHDR *pNMHDR, LRESULT *pResult)
{
LPNMLISTVIEW pNMLV = reinterpret_cast<LPNMLISTVIEW>(pNMHDR);
if (!c_List.InternalStateChange() && /* other conditions */)
{
// Respond to state changes
}
}

Change Text Color in MFC C++?

I want to change the color of the LVITEM?
m_szList is the CListCtrl.
LVITEM lvItem;
lvItem.mask = LVIF_TEXT;
lvItem.iItem = 0;
lvItem.iSubItem = 0;
lvItem.pszText = _T("Sandra");
m_szList.InsertItem(&lvItem);
m_szList.SetTextColor(RGB(255, 78, 12));
lvItem.mask = LVIF_TEXT;
lvItem.iItem = 1;
lvItem.iSubItem = 0;
lvItem.pszText = _T("Roger");
m_szList.InsertItem(&lvItem);
This code can change the both color of sandra and roger.
But i just want to change the color of sandra to red.
And roger to default(black).
You can use a Custom-draw list control for this job.
You make the control custom draw by responding to the NM_CUSTOMDRAW message. This is notification message that's sent from the control. Using MFC, your function header will look something like this:
void CCustomLvView::OnNMCustomdraw(NMHDR *pNMHDR, LRESULT *pResult)
To add this handler, you normally use the Properties list for the CListCtrl (or CListView), something like this:
That'll create a handler something like this:
void CCustomLV2View::OnNMCustomdraw(NMHDR *pNMHDR, LRESULT *pResult) {
LPNMCUSTOMDRAW pNMCD = reinterpret_cast<LPNMCUSTOMDRAW>(pNMHDR);
*pResult = CDRF_DODEFAULT;
}
[If memory serves, it also has a comment or two.]
You'll need to add a little code to that to change the text color, something on this order:
void CCustomLV2View::OnNMCustomdraw(NMHDR *pNMHDR, LRESULT *pResult) {
LPNMLVCUSTOMDRAW pNMCD = reinterpret_cast<LPNMLVCUSTOMDRAW>(pNMHDR);
*pResult = CDRF_DODEFAULT;
switch (pNMCD->nmcd.dwDrawStage) {
// this tells the control, before any painting begins, that we
// want to be notified just before any item in the control is drawn.
case CDDS_PREPAINT:
*pResult = CDRF_NOTIFYITEMDRAW;
break;
// This will be called before an item is drawn.
// We check what item is being drawn, and set the text color appropriately
case CDDS_ITEMPREPAINT:
if (pNMCD->nmcd.dwItemSpec == 0)
pNMCD->clrText = RGB(0, 0, 0);
else
pNMCD->clrText = RGB(255, 78, 12);
break;
}
}
As it is right now, this draws the text for the first item in black, and all subsequent items in your shade of red. The if (pNMCD->nmcd.dwItemSpec == 0) is what chooses the items, and (of course) the pNMCD->clrText = RGB... is what sets the text color.
Also note that I've made a fairly minor modification to the code it generates, so I have a LPNMLVCUSTOMDRAW instead of a LPNMCUSTOMDRAW. This gives access to the ListView-specific fields passed to the custom-draw handler. Without that, we don't get access to some of (any of?) the fields we're using.

MFC CListView custom drawing - colour row if text equals value

In my C++ MFC application I'm using a CListView in report style. I need a way to colour in a row if a value equals a specific value, i.e. I have a 'Validity' column, and if a value is out of range, colour the row red.
I understand that I need to use a CustomDraw handler, as custom drawing means I can make changes to the drawing context.
To add a custom draw handler, click on your List Control, go to Properties and click on Events. Add a 'NM_CUSTOMDRAW' control event handler.
This custom draw event handler colours in each row if the third column's row equals 'No':
void Test_ClientDlg::OnNMCustomdrawlistctrlvalues(NMHDR *pNMHDR, LRESULT *pResult)
{
LPNMLVCUSTOMDRAW lpLVCustomDraw = reinterpret_cast<LPNMLVCUSTOMDRAW>(pNMHDR);
int itemCnt = 0;
CString text;
RECT rc;
switch(lpLVCustomDraw->nmcd.dwDrawStage)
{
case CDDS_ITEMPREPAINT:
case CDDS_ITEMPREPAINT | CDDS_SUBITEM:
itemCnt = listAnalysisVals->GetItemCount();
for (int i = 0; i < itemCnt; i++)
{
//get each row text for 3rd column (position 2)
text = listAnalysisVals->GetItemText(i, 2);
if (text.Compare("No") == 0)
{
if (i == (lpLVCustomDraw->nmcd.dwItemSpec))
{
lpLVCustomDraw->clrTextBk = RGB(255,50,50);
listAnalysisVals->GetItemRect(i,&rc,LVIR_BOUNDS);
listAnalysisVals->InvalidateRect(&rc, 0);
}
}
}
break;
default: break;
}
*pResult = 0;
*pResult |= CDRF_NOTIFYPOSTPAINT;
*pResult |= CDRF_NOTIFYITEMDRAW;
*pResult |= CDRF_NOTIFYSUBITEMDRAW;
}
This results in:

No color in CListCtrl report view

I have done this before but to my surprise the CListCtrl will not show the text in color. I have the list view control on a dialog. I am using VS2010, is there something else needed that I am missing?
void CGameView::OnCustomdrawListBatsmen(NMHDR *pNMHDR, LRESULT *pResult)
{
//LPNMCUSTOMDRAW pNMCD = reinterpret_cast<LPNMCUSTOMDRAW>(pNMHDR);
LPNMLVCUSTOMDRAW pNMLVCD = reinterpret_cast<LPNMLVCUSTOMDRAW>(pNMHDR);
*pResult = CDRF_DODEFAULT;
switch( pNMLVCD->nmcd.dwDrawStage )
{
case CDDS_PREPAINT:
*pResult = CDRF_NOTIFYITEMDRAW;
break;
case CDDS_ITEMPREPAINT:
pNMLVCD->clrTextBk = RGB(255,0,0);
pNMLVCD->clrText = RGB(255, 0, 0 );
*pResult = CDRF_NOTIFYSUBITEMDRAW;
break;
case CDDS_ITEMPREPAINT | CDDS_SUBITEM:
// if( pNMLVCD->iSubItem == 1 )
pNMLVCD->clrTextBk = RGB(0,255,0);
pNMLVCD->clrText = RGB(255, 0, 0 );
break;
}
*pResult = 0;
}
When I debug it, the control never even get passed the first case! I don't know is that? Do I need to set List control properties in resource view?
I fixed the issue, it was the last line:
*pResult = 0;
This needed to be removed because it was resetting the whatever value was being set in the switch case.
maybe,
in CGameView, add:
1.
ON_NOTIFY(LVN_GETDISPINFO, IDC_LIST_Batsmen, GetDispInfo)
2.
void CGameView::GetDispInfo(NMHDR* pNMHDR, LRESULT* pResult)
in your CListCtrl class, add:
ON_NOTIFY_REFLECT(NM_CUSTOMDRAW, OnCustomdrawListBatsmen)

Color CListCtrl column Background without the CDDS_ITEMPREPAINT phase

I Want to paint the Background of the first column grey. The problem with CDDS_ITEMPREPAINT is, that it only colors the background if there is an item. Just like this:
At the moment, this is my code:
{
LPNMLVCUSTOMDRAW pNMLVCD = reinterpret_cast<LPNMLVCUSTOMDRAW>(pNMHDR);
*pResult = CDRF_DODEFAULT;
switch (pNMLVCD->nmcd.dwDrawStage)
{
case CDDS_PREPAINT:
*pResult = CDRF_NOTIFYITEMDRAW;
break;
case CDDS_ITEMPREPAINT:
*pResult = CDRF_NOTIFYSUBITEMDRAW;
break;
case CDDS_ITEMPREPAINT | CDDS_SUBITEM:
if (pNMLVCD->iSubItem == 0)
{
pNMLVCD->clrTextBk = RGB(97,97,97);
}
else
{
pNMLVCD->clrTextBk = RGB(255,255,255);
}
break;
}
}
Can anyone explain me how to solve this?
There is no way to accomplish this with custom draw.
Even with ownerdraw the routines are only called for exisitng items. To color the free area you may need to overwrite WM_ERASEBKGND and you Need to do your own stuff there.