News:

Masm32 SDK description, downloads and other helpful links
Message to All Guests

Main Menu

Custom draw controls in dialog: can't make it work

Started by NoCforMe, March 31, 2017, 09:33:44 AM

Previous topic - Next topic

NoCforMe

(Somewhat long post here: you might want to skip it if your attention span is short ...)

I'm trying to add custom-draw functionality to a dialog. Specifically, I want to make a toolbar in the dialog a different color. Nothing fancy, just want to change the color scheme.

I turned to the custom draw documentation at MSDN. Couple problems here: the docs aren't exactly clear on how this all works, on top of which some of it is just plain wrong (or just plain doesn't work in Win32).  So I'm turning to folks here who hopefully know a lot more about this stuff.

Here's my understanding of how this should work:

I've created a dialog using an in-memory template, which works fine. Using DialogBoxIndirectParam() to open the dialog. First control in the dialog is a toolbar.

What I'm doing in the dialog proc, in pseudocode, is this:
case WM_NOTIFY:
   IF lParam->NMHDR.code = NM_CUSTOMDRAW
   AND lParam->NMHDR.idFrom = [my toolbar's control ID]
   AND lParam->NMCUSTOMDRAW.dwDrawStage = CDDS_PREPAINT
         SetWindowLong (hWin, DWL_MSGRESULT, CDRF_NOTIFYITEMDRAW)    ;Request further notifications
         return TRUE

   IF  lParam->NMHDR.code = NM_CUSTOMDRAW
   AND lParam->NMHDR.idFrom = [my toolbar's control ID]
   AND lParam->NMCUSTOMDRAW.dwDrawStage = CDDS_ITEMPREPAINT
          (try various stuff to change toolbar color)
         SetWindowLong (hWin, DWL_MSGRESULT, CDRF_DODEFAULT)    ;Let Windows draw the control
         return TRUE

I say "try various stuff" because that's what I did. Nothing seemed to work.

So again, my understanding of how this works is that the first time the notification comes in with the drawing stage equal to CDDS_PREPAINT, I return CDRF_NOTIFYITEMDRAW to let Windows know that I want to get further notifications of when items are painted or erased. Right? Then I look for those notifications (CDDS_ITEMPREPAINT) to actually do the custom drawing.

So here's what I see is available to me to try to change my toolbar's color:
In the NMCUSTOMDRAW structure, there's a handle to the control's device context. I tried using the DC:

SetBkColor (lParam->NMCUSTOMDRAW.hdc, $colorRed)


which didn't work. (Also tried creating a colored brush and selecting it into the DC; no luck.) In the NMTBCUSTOMDRAW structure (which by the way isn't in the MASM package--I had to add it by hand), there are a bunch of color specifiers (of type COLORREF, RGB values). I tried using these, replacing them with another color; no joy.

There are a couple of things about this whole process that are unclear to me:

First, there's the whole thing of sequencing, based on what "draw stage" you're in. Here are the stages:










CDDS_POSTERASEAfter the erasing cycle is complete.
CDDS_POSTPAINTAfter the painting cycle is complete.
CDDS_PREERASEBefore the erasing cycle begins.
CDDS_PREPAINTBefore the painting cycle begins.
CDDS_ITEMPOSTERASEAfter an item has been erased.
CDDS_ITEMPOSTPAINTAfter an item has been drawn.
CDDS_ITEMPREERASEBefore an item is erased.
CDDS_ITEMPREPAINTBefore an item is drawn.

My question is, when am I supposed to try to change the color here? Before or after erasing? before or after painting? What's not clear to me is what Windows is doing before or after any of these actions. In other words, which things that I can change (background color, text color, font, etc.) will survive whatever operations the API does after I change it?

The second thing I don't really understand is what I'm supposed to return (from my dialog procedure) after making changes. I understand that I need to do this through SetWindowLong():

    INVOKE   SetWindowLong, hWin, DWL_MSGRESULT, [return code]
    MOV        EAX, TRUE
    RET


Thing is, what do I return? Here are the return values I tried, from MSDN:




CDRF_DODEFAULTThe control will draw itself. It will not send additional NM_CUSTOMDRAW notification codes for this paint cycle. This flag cannot be used with any other flag.
CDRF_NEWFONTYour application specified a new font for the item; the control will use the new font. For more information on changing fonts, see Changing fonts and colors. This occurs when dwDrawStage equals CDDS_ITEMPREPAINT.

(There are other choices, which you can see here. These are the only two that looked like they would do me any good.)

I did notice that when I returned CDRF_NEWFONT, it did change the font (in a bad way), but not the color. I tried this return value because I saw it used in a couple of code examples I found through MSDN, which supposedly worked.

If anyone can shed any light on this, I'd be grateful. It's not a huge deal--it's basically "chrome", not basic functionality, but it's bugging me that I can't make anything work here.

Oh, and I'm running XP, and I'm using "visual styles" (have a manifest). I haven't tried disabling this, but will do. Also am calling InitCommonControlsEx(). Oh, and the toolbar contains only text, no bitmaps.

Thanks in advance!
Assembly language programming should be fun. That's why I do it.

fearless

Some references that might be useful: https://www.codeproject.com/Articles/646482/Custom-Controls-in-Win-API-Control-Customization

Dont think there is a builtin TB_SETBKCOLOR message or a way of directly controlling that background color.

Couple options that i can think of are:

- Might be possible to place a child static control and host a bitmap or image so as to 'paint' the background of the toolbar or rebar control as long as they are transparent for this to work - unsure if it is feasible tbh.

- Use clrBack of rebar info structure and host the toolbars in the rebar - again not sure how feasible this is or if it would work.

- Other option is much like the links at the end of that article - wine using subclassing of rebar and other controls to get round these limitations.

- Create a toolbar control manually and duplicate the features of a toolbar and allow a way of custom painting the background of the toolbar.



dedndave

background color for buttons is a hard one to do
it's almost easier to write your own WndProc for a button class of your own

NoCforMe

Quote from: fearless on March 31, 2017, 11:34:34 AM
Some references that might be useful: https://www.codeproject.com/Articles/646482/Custom-Controls-in-Win-API-Control-Customization

That is useful! The guy actually explains drawing stages, etc., in a very clear way. (Unfortunately, it still doesn't answer my original questions, but I'm still re-reading and absorbing it.) Thanks!

Another of his articles was immediately useful to me to eliminate flickering in another app (by suppressing WM_ERASEBKGND). Good stuff.
Assembly language programming should be fun. That's why I do it.

NoCforMe

Quote from: dedndave on March 31, 2017, 12:51:11 PM
background color for buttons is a hard one to do
it's almost easier to write your own WndProc for a button class of your own

Ackshooly, in this case it'd prolly be easier to lose the toolbar and just put some buttons in the dialog. I guess I could "fake" a toolbar by drawing a thin rectangle behind a row of buttons (or just use the toolbar as a parent for some button child windows--been there, done that, just can't use my dialog editor to place the buttons, they have to be put in "by hand" ...).
Assembly language programming should be fun. That's why I do it.