NM_CUSTOMDRAW on Listview
Hi all,
After being a silent observer for a while I've decided to ask for your help. It seems to me like I've search all the forums in the world for answers and couldn't find...
I consider myself a newbie in win32, so the fact I couldn't find the answer doesn't say anything about the problem being complex :).
My task is pretty simple - I'm writing a dll to be used with nsis (Nullsoft Scriptable Install System). I want to change the color of specific subitems in a listview.
I suspect that I'm not getting notifications for the items at all, but only for the titles: let's say I have 4 colums and 10 rows (that is 10 items with 4 subitems each). In the handler I get CDDS_PREPAINT and return CDRF_NOTIFYITEMDRAW then I get 4 CDDS_ITEMPREPAINTs and for each one of them I return CDRF_NOTIFYSUBITEMDRAW, and that's it.
I also noticed that if instead of returning CDRF_NOTIFYSUBITEMDRAW I change the font and return CDRF_NEWFONT, also nothing is happening (and according to MSDN this should affect all subitems of the item).
here is the code: http://www.mediafire.com/?pd6tf3p1r269d84
it's a pack that contains also an nsis file that uses the dll, you need nsis compiler to use it. It also contains an example2.exe which is compiled with a dll in debug mode, so you can attach to it and see the notifications, and hopefully see where is the bug in my code.
lines 279-344 of URLCtrl:
LRESULT ProcessCustomDraw (LPARAM lParam)
{
LPNMLVCUSTOMDRAW lplvcd = (LPNMLVCUSTOMDRAW)lParam;
switch(lplvcd->nmcd.dwDrawStage)
{
case CDDS_PREPAINT : //Before the paint cycle begins
//request notifications for individual listview items
return CDRF_NOTIFYITEMDRAW;
break;
case CDDS_ITEMPREPAINT: //Before an item is drawn
return CDRF_NOTIFYSUBITEMDRAW;
break;
//Before a subitem is drawn
case (CDDS_ITEMPREPAINT | CDDS_SUBITEM):
lplvcd->clrText = RGB(255,0,0);
lplvcd->clrTextBk = RGB(255,255,255);
return CDRF_NEWFONT;
break;
}
return CDRF_DODEFAULT;
}
LRESULT CALLBACK urlctrl_lv_proc(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam)
{ URLCTRLS *urls=(URLCTRLS*)GetWindowLong(hwnd,GWL_USERDATA);
WNDPROC oldproc=urls->oldproc;
LRESULT myResult = CDRF_DODEFAULT;
switch(uMsg)
{ case WM_NOTIFY:
{ switch (((LPNMHDR)lParam)->code)
{ case NM_CUSTOMDRAW:
{ myResult = ProcessCustomDraw(lParam);
return myResult;
break;
}
}
break;
}
....
....
}
return CallWindowProc(oldproc,hwnd,uMsg,wParam,lParam);
}
Thanks!
J
Re: NM_CUSTOMDRAW on Listview
Is your question about custom draw of the list control? Or about integration of your dll with nsis?
I, for example, can’t get to your code. Not that I would want to compile the installer…
Could you please isolate your problem? Create a simple app with one list control that does your custom drawing.
If it doesn’t work – post your app here.
Good luck!
1 Attachment(s)
Re: NM_CUSTOMDRAW on Listview
Hi Vladimir,
First of all - Thanks for the reply. I did manage to isolate the problem and I'm sending a very small piece of code that demonstrate it - I don't get NM_CUSTOMDRAW from the list view.
I try to add to the listview a url, in this code the subitem 1 of item 1 is a url and opens google.com when I press on it. I want to make its color blue.
Thanks!
Re: NM_CUSTOMDRAW on Listview
Notifications like NM_CUSTOMDRAW are sent via WM_NOTIFY to the parent of the control and NOT to the control itself.
What you are receiving in listview control procedure are notifications from its child which is a header control.
So, in your program you have to catch NM_CUSTOMDRAW in WndProc and not in urlctrl_lv_proc.
Code:
LRESULT CALLBACK WndProc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
{
//...
switch (iMsg)
{
case WM_NOTIFY:
{
LPNMHDR lpNHMDR = (LPNMHDR)lParam;
switch(lpNHMDR->code)
{
case NM_CUSTOMDRAW:
{
LPNMHDR lpNHMDR = (LPNMHDR)lParam;
HWND hWndList = lpNHMDR->hwndFrom;
URLCTRLS *urls=(URLCTRLS*)GetWindowLongPtr(hWndList,GWLP_USERDATA);
if(lpNHMDR->idFrom == (UINT)urls->idFrom)
{
// ... got it here!
// ...
//...
}
Two additional remarks/advices:
- Try to write stucture declarations (as well as any other declaration) in header files in order to be accesibile in any sources where is necessary;
- Try to avoid writing code after '{' brackets; it took me time to re-format your code in order to put brakpoints and see what's going on.
And finally:
NMHDR is a general structure which gests very few information.
In case of handling NM_CUSTOMDRAW for a listview control it's better to cast lParam to LPNMLVCUSTOMDRAW.
It was not hecessary here for the monent, but never know...
Re: NM_CUSTOMDRAW on Listview
One more remark.
It is not necessary to keep the control ID in that structure (URLCTRLS) which has its address stored in control's user data.
You know in your program the ID which has been passed to CreateWindowEx.
All above being said, we can write a little bit better:
Code:
LRESULT CALLBACK WndProc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
{
//...
switch (iMsg)
{
case WM_NOTIFY :
{
LPNMHDR lpNHMDR = (LPNMHDR)lParam;
switch(lpNHMDR->code)
{
case NM_CUSTOMDRAW:
{
if(ID_LISTVIEW == lpNHMDR->idFrom)
{
HWND hWndList = lpNHMDR->hwndFrom ;
URLCTRL *url=(URLCTRL*)GetWindowLongPtr(hWndList,GWLP_USERDATA);
LPNMLVCUSTOMDRAW lpNMCustomDraw = (LPNMLVCUSTOMDRAW)lpNHMDR;;
// ... Enjoy!
// ...
//...
}
Re: NM_CUSTOMDRAW on Listview
hi ovidiucucu,
Thanks! funny thing is that just 20 minutes before your post I got to the solution myself. I've made another handler for the parent and it worked :).
I really appreciate your tips and your explanation, and I'll try to apply them.
Thanks again!
J
Re: NM_CUSTOMDRAW on Listview
Quote:
Originally Posted by
ovidiucucu
One more remark.
It is not necessary to keep the control ID in that structure (URLCTRLS) which has its address stored in control's user data.
You know in your program the ID which has been passed to CreateWindowEx.
This is true for the small example but I'm actually writing a dll which adds the ability to change subitems to urls in an already exist listview which I get as hwnd from the caller, so I assumed that I wouldn't know the id of the listview in that case