I'm trying to change the row text-color of a list-view I made from a resource. For that, I handle NM_CUSTOMDRAW inside my dialog process. It's a modal dialog box, if that matters. According to the documentation,
dwDrawStage should equal CDDS_ITEMPREPAINT after returning CDRF_NOTIFYITEMDRAW. But this is not the case. I'm receiving CDDS_PREPAINT for every item.
What did I do wrong?
This is how I respond to the message:
case WM_NOTIFY:
if (((LPNMHDR)lParam)->hwndFrom == GetDlgItem(hwnd, IDC_List2) && ((LPNMHDR)lParam)->code == NM_CUSTOMDRAW)
{
int result = CDRF_DODEFAULT;
LPNMLVCUSTOMDRAW lplvcd = (LPNMLVCUSTOMDRAW)lParam;
switch (lplvcd->nmcd.dwDrawStage)
{
case CDDS_PREPAINT:
result = CDRF_NOTIFYITEMDRAW;
break;
case CDDS_ITEMPREPAINT: //never gets executed
lplvcd->clrText = RGB(255, 0, 0);
result = CDRF_NEWFONT;
break;
}
//SetWindowLongPtr(hEdit, DWLP_MSGRESULT, result);
//return TRUE;
return result;
}
break;
these are the properties of the ListView:
IDC_List2,"SysListView32",LVS_REPORT | LVS_SINGLESEL | LVS_NOLABELWRAP | LVS_ALIGNLEFT | LVS_NOSORTHEADER | WS_BORDER | WS_TABSTOP,205,18,363,197,WS_EX_CLIENTEDGE
I figured out the problem:
WM_NOTIFY is handled inside a dialog so of course I have to return the values accordingly. The issue was, that I used the wrong variable for the window (hEdit in this case).
This is the corrected version if anyone cares:
case WM_NOTIFY:
if (((LPNMHDR)lParam)->hwndFrom == GetDlgItem(hwnd, IDC_List2) && ((LPNMHDR)lParam)->code == NM_CUSTOMDRAW)
{
int result = CDRF_DODEFAULT;
LPNMLVCUSTOMDRAW lplvcd = (LPNMLVCUSTOMDRAW)lParam;
switch (lplvcd->nmcd.dwDrawStage)
{
case CDDS_PREPAINT:
result = CDRF_NOTIFYITEMDRAW;
break;
case CDDS_ITEMPREPAINT:
lplvcd->clrText = RGB(255, 0, 0);
result = CDRF_NEWFONT;
break;
}
SetWindowLongPtr(hDialog, DWLP_MSGRESULT, result);
return TRUE;
}
break;
Related
In this Microsoft example on how to process the WM_NOTIFY message for a SysLink control they have this code,g_hLinkbeing the handle of the SysLink control:
// g_hLink is the handle of the SysLink control.
case WM_NOTIFY:
switch (((LPNMHDR)lParam)->code)
{
case NM_CLICK: // Fall through to the next case.
case NM_RETURN:
{
PNMLINK pNMLink = (PNMLINK)lParam;
LITEM item = pNMLink->item;
if ((((LPNMHDR)lParam)->hwndFrom == g_hLink) && (item.iLink == 0))
{
ShellExecute(NULL, L"open", item.szUrl, NULL, NULL, SW_SHOW);
}
else if (wcscmp(item.szID, L"idInfo") == 0)
{
MessageBox(hDlg, L"This isn't much help.", L"Example", MB_OK);
}
break;
}
}
break;
I don't uderstand why the (((LPNMHDR)lParam)->hwndFrom == g_hLink) condition is not needed for the else clause?
Or is this simply an error in the example?
I have to make a custom controller "MyCEdit" inherited from CEdit. I just want to push my custom controller in a dialogbox but the window crash on create. I saw the documention MSDN and made some adjustement :
On InitDialog I have:
if (message == WM_INITDIALOG)
{
CWnd* pWnd = CWnd::FromHandle(hwndDlg);
MyCEdit* pEdit = new MyCEdit();
pEdit->Create(WS_CHILD | WS_VISIBLE | WS_TABSTOP | WS_BORDER,
CRect(10, 10, 100, 100), pWnd, IDC_CUSTOM_EDIT1); /* Failure here */
return TRUE;
}
I Have check the pointer pWnd, he's not NULL and point to the good window. And pEdit is not NULL.
My custom controller looks like (only override the PreTranslateMessage method):
class MyCEdit : public CEdit
{
public:
BOOL PreTranslateMessage(MSG* pMsg) override
{
if (pMsg->message == WM_KEYDOWN && ::GetKeyState(VK_CONTROL) != 0)
{
switch (pMsg->wParam)
{
case 'X': Cut(); return TRUE;
case 'C': Copy(); return TRUE;
case 'V': Paste(); return TRUE;
case 'Z': Undo(); return TRUE;
case 'A': SetSel(0, -1); return TRUE;
}
}
return CEdit::PreTranslateMessage(pMsg);
}
};
There is a step I miss or I make a wrong operation ?
I am trying to change the color of listview item based on what I type on text box. When user type some text based on some logic I use I have below code
ListView_SetItemState(hList, wordid, LVIS_FOCUSED | LVIS_SELECTED, LVIS_FOCUSED | LVIS_SELECTED);
Then on my WM_NOTIFY block I have this;
case WM_NOTIFY:
{
NMHDR *pNMHDR= (NMHDR*)lParam;
switch(pNMHDR->code){
case LVN_GETDISPINFO:
OnGetdispinfo(pNMHDR);
break;
case NM_CUSTOMDRAW:
wmnot= OnDraw(pNMHDR);
return wmnot;
break;
}
return 0;
}
OnGetdispinfo function fills my virtual listview by using Sqlite. In my ondraw function I have this
int OnDraw (NMHDR* pNMHDR){
int nIndex,state;
int result;
NMLVCUSTOMDRAW* pLVCD = reinterpret_cast<NMLVCUSTOMDRAW*>( pNMHDR );
if (pLVCD->nmcd.hdr.hwndFrom==hList)
{
switch (pLVCD->nmcd.dwDrawStage)
{
case CDDS_PREPAINT:
result= CDRF_NOTIFYITEMDRAW;
break;
case CDDS_ITEMPREPAINT:
result=CDRF_NOTIFYSUBITEMDRAW;
break;
case CDDS_SUBITEM|CDDS_ITEMPREPAINT:
nIndex=pLVCD->nmcd.dwItemSpec;
state=ListView_GetItemState(hList,nIndex,LVIF_TEXT |LVIF_PARAM);
if(state&LVIS_SELECTED==LVIS_SELECTED)
{
pLVCD->clrTextBk=RGB(124,34,78);
return CDRF_NEWFONT;
}
result= CDRF_DODEFAULT;
break;
default:
result=CDRF_DODEFAULT;
break;
}
}
return result; // CDRF_DODEFAULT
}
I get CDDS_PREPAINT message but I don't get CDDS_ITEMPREPAINT message at all.
My Listview has this styles according to Spy++
Windows Styles (5021580D)
WS_CHILDWINDOW
WS_VISIBLE
WS_VSCROLL
WS_TABSTOP
LVS_REPORT
LVS_SINGLESEL
LVS_SHOWSELALWAYS
Extended Styles (00000204)
WS_EX_LEFT
WS_EX_LTRREADING
WS_EX_RIGHTSCROLLBAR
WS_EX_NOPARENTNOTIFY
WS_EX_CLIENTEDGE
If your control is in a dialog, you have to return your result code using:
SetWindowLongPtr(hWnd, DWLP_MSGRESULT, result);
and then return TRUE from the DlgProc itself.
I finally solved the problem. Here is the fixed code.
First of all I added onchange notifier function so that I can detect changes for selection.
void OnItemChange (LPARAM lParam)
{
LPNMLISTVIEW pNMListView = reinterpret_cast<LPNMLISTVIEW>(lParam);
if ((pNMListView->uChanged & LVIF_STATE)
&& (pNMListView->uNewState & LVNI_SELECTED))
{
ListView_RedrawItems(hList,Query.WordID,pNMListView->iItem);
ListView_RedrawItems(hList,pNMListView->iItem,Query.WordID);
Query.WordID=pNMListView->iItem;
//DO STUFF;
}
}
Then I modified my custom draw function as well.
LRESULT OnDraw (LPARAM lParam)
{
LPNMLVCUSTOMDRAW lplvcd = (LPNMLVCUSTOMDRAW)lParam;
int nIndex;
int state;
if (lplvcd->nmcd.hdr.hwndFrom!=hList)
return CDRF_DODEFAULT;
switch(lplvcd->nmcd.dwDrawStage)
{
case CDDS_PREPAINT : //Before the paint cycle begins
return CDRF_NOTIFYITEMDRAW;
case CDDS_ITEMPREPAINT:
return CDRF_NOTIFYSUBITEMDRAW;
case CDDS_POSTPAINT:
nIndex=lplvcd->nmcd.dwItemSpec;
state=lplvcd->nmcd.uItemState;
return CDRF_DODEFAULT;
case CDDS_SUBITEM | CDDS_ITEMPREPAINT:
nIndex=lplvcd->nmcd.dwItemSpec;
state=lplvcd->nmcd.uItemState;
if ( nIndex==Query.WordID) {
lplvcd->clrText = GetSysColor(COLOR_HIGHLIGHTTEXT);
lplvcd->clrTextBk = GetSysColor(COLOR_HIGHLIGHT);
return CDRF_NEWFONT;
}
else{
lplvcd->clrText = GetSysColor(COLOR_WINDOWTEXT);
lplvcd->clrTextBk = GetSysColor(COLOR_WINDOW);
return CDRF_NEWFONT;
}
default:
return CDRF_DODEFAULT;
}
return CDRF_DODEFAULT;
}
So basically, I am not selecting items by ListView Messages. Instead i set Query.WordID to the item I want to select and if the item is selected(either by mouse or programmatically) I manually change the color of the item.
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)
I have create list a listview
list[0]=CreateWindow(WC_LISTVIEW,L"",WS_DLGFRAME|WS_CHILD|WS_VISIBLE|LVS_REPORT|LVS_SINGLESEL,-,-,-,-,hwnd,NULL,hInstance,NULL);
LVCOLUMN lvcolumn;
lvcolumn.mask = LVCF_FMT|LVCF_TEXT|LVCF_WIDTH;
lvcolumn.fmt = LVCFMT_CENTER;
lvcolumn.pszText = L"a";
lvcolumn.cx = 100;
ListView_InsertColumn(list[0],0,&lvcolumn);
lvcolumn.pszText = L"b";
ListView_InsertColumn(list[0],1,&lvcolumn);
lvcolumn.pszText = L"c";
ListView_InsertColumn(list[0],2,&lvcolumn);
lvcolumn.pszText = L"d";
ListView_InsertColumn(list[0],3,&lvcolumn);
And I use LPNMLVCUSTOMDRAW to insert color item
case WM_NOTIFY:
if( ((LPNMHDR)lParam)->code == NM_CUSTOMDRAW){
switch( ((LPNMLVCUSTOMDRAW)lParam)->nmcd.dwDrawStage){
case CDDS_PREPAINT:
return CDRF_NOTIFYITEMDRAW;
break;
case CDDS_ITEMPREPAINT:
{
LPNMLVCUSTOMDRAW customDraw = (LPNMLVCUSTOMDRAW)lParam;
customDraw->clrText = getcolor();
return CDRF_NEWFONT;
break;
}
default: return CDRF_DODEFAULT;
}
}
break;
getcolor is function response colorref. Each item have it own text color. But when the parent window is hide and show again the item color is change. So how to keep it not change or we have another way to set text color
One more I create an new listview item by this way (in case someone need it)
ListView_InsertItem(hwndlist,&lvitem);
ListView_SetItemText(hwndlist,i,k,(LPWSTR)strarr[k].c_str());