views:

300

answers:

3

I have implemented my custom button inheriting from CButton and drawing it by using uxtheme.dll (DrawThemeBackground with BP_PUSHBUTTON).

Everything works fine but I have two statuses (Normal and Pressed) which Hot status is the same. It means when the user places the cursor over the button it is drawn alike regardless the button status (Pressed or not).

This is a bit confusing to the user and I would like to change the way the button is drawn in Pressed & Hot status. Does anybody know a way?

I have also thought about custumizing the whole drawing but the buttons use gradients, borders, shadows, etc. So it is not easy to achive the same look&feel drawing everything by myself. Is there a way to find the source code of the dll or know how to do it?

Thanks in advance.

Javier

Note: I think I could be able to achive what I want to do by using CMFCButton and overriding the OnDraw method. Let the control draw the button on OnDrawBorder and then drawing the inside button myself. But I need to know how the control draws the inside button when pressed. It is a gradient and I can't guess how it's done. Does anybody have a clue?

+1  A: 

In answer to your second question, if you derive from CMFCButton instead of CButton you can override OnDraw() or OnDrawText() instead of the usual DrawItem(). That way the default button background will be drawn, and then your drawing code is executed.

djeidot
Please, have a look to my note in the original question. And thank you for your help.
Javier De Pedro
+1  A: 

The only way I know of to really tackle this is to use 'custom draw', rather than 'owner draw'. Custom draw came in with Windows 2000, but is only used by button controls with comctrl32 6.0 (so Windows XP onwards), isn't very clearly documented, and isn't something MFC goes out of its way to support.

Anyway, the good thing about custom draw is that it lets you hook in at various points in the drawing process, unlike owner draw, which makes you deal with the whole thing. Have a look in MSDN at the NM_CUSTOMDRAW notification message.

For the other part of your problem, detecting the 'hot' state, the easiest way to do this is to use WM_MOUSEMOVE messages and the TrackMouseEvent() function to track whether the mouse is over your button.

Unfortunately this is a bit of a vague answer: the amount of code you need to demonstrate a button that uses custom draw is a bit too much to type into these answer boxes! I do have a project that demonstrates such techniques, using a custom draw button (falling back to owner draw on older Windows versions) that adds a little arrow to the button. You can have a look at the source code by getting

Windows_UI_source.zip

Open it and have a look at the "DropArrowButton" class. The important bit is the OnCustomDraw() handler and its helper function DrawControl(): these get called at the various button drawing phases, and use UxTheme to draw the control appropriately.

DavidK
I had a look at your code but I don't see how that solves my problem. You end up calling DrawThemeBackground with PBS_HOT state and that draws the normal (unpressed) button with the golden border (hot state). I need a way to draw the pressed (PBS_PRESSED) state with the golden border (hot state).Thank you very much anyway.
Javier De Pedro
My mistake: I misunderstood your question. I don't think there's any way to quite make DrawThemeBackground() do what you want: all I can suggest would be to draw the background pressed, and alpha-blend in the "hot" state rectangle yourself. If you did the "hot" rectangle like that always, you should probably able to get a passable effect.
DavidK
This is something similar to what I did. In fact the effect is quite good without using AlphaBlend (I'll give it a try if someone complains!). Unfortunatelly I had already accepted my answer when I saw your comment and I cannot unmake my acceptation. I would have accepted your answer otherwise. Thank you.
Javier De Pedro
+1  A: 

I finally figured out how to achive what I want to do. It's pretty easy indeed.

I use two calls to DrawThemeBackground. The first one with PBS_PRESSED and the second one with state PBS_HOT. Then I make a ExcludeClipRect to avoid from drawing over the center of the button.

Something like this:

        DrawThemeBackground(    hTheme,
                                pCustomDraw->hdc, 
                                BP_PUSHBUTTON,
                                PBS_PRESSED,
                                &pCustomDraw->rc, 
                                NULL);

        CDC *pDC = CDC::FromHandle(pCustomDraw->hdc);

        CRect rectClient;
        GetClientRect(rectClient);
        CRect rectInternal = rectClient;

        rectInternal.DeflateRect(4,4);
        pDC->SelectClipRgn(NULL);
        pDC->ExcludeClipRect(&rectInternal);

        DrawThemeBackground(    hTheme,
                                pCustomDraw->hdc, 
                                BP_PUSHBUTTON,
                                PBS_HOT,
                                &pCustomDraw->rc, 
                                NULL);

        pDC->SelectClipRgn(NULL);

Of course this is not the whole code but I think is enough to make my point.

Thanks.

Javier De Pedro