c++winapigdi+gdiwm-paint

Improve code for WM_PAINT and WM_CTLCOLORSTATIC handlers


INTRODUCTION AND RELEVANT INFORMATION:

I have implemented complex painting of the main window’s background and its child static controls.

The picture below shows how it looks.

enter image description here

Static controls have SS_NOTIFY style, which is important to mention, as certain things happen when user clicks on them.

At this point, actions activated when clicking on them, are not relevant.

Both main window, and static controls, have gradient backgrounds, which were made through usage of GradientFill(...) API.

Top banner of the main window is created with gray brush, and the grid lines were created with LineTo(...) and MoveTo(...) API.

Map on the orange static control, and the top left logo are EMF files, top right logo is PNG file, and other pictures are bitmaps.

Orange static control has 4 child static controls which are owner drawn and also have SS_NOTIFY style.

It was the only way I could have thought of, which enabled me to draw the control the way it was asked of me ( if I can improve on this, please suggest it, I will accept any reasonable suggestion ).

In order to paint the orange static control, I have decided to paint its background in WM_CTLCOLORSTATIC handler, and to owner draw child static controls in a subclass procedure.

Notifications received from child static controls are also handled in the orange static controls subclass procedure, since I didn’t know how to forward them to the parent window, but are omitted since they are also irrelevant at this moment.

I have decided to provide link to the demo project, instead of making this post quite lengthy with code snippets.

I have tried to submit demo application as small and simple as it is possible.

I did not skimp on the commentaries, so I believe everything is well covered and explained in source code.

If there are still questions please leave a comment and I will reply as soon as possible ( usually immediately, or in the same day, at least ).

Here is the link to the demo project:http://www.filedropper.com/geotermistgrafika_1

Important update:

/==========================================================/

Text bellow in square brackets was the original part of the question, but is now omitted since the project had memory leaks.The above link links to an improved version.

[ Updated in response to member xMRi's comment: This link should be fine: http://www.filedropper.com/geotermistgrafika ]

/==========================================================/

I work on Windows XP, using MS Visual Studio C++ and pure Win32 API.

One note: since Express edition of VS has no resource editor, resource file and resource header were created using ResEdit from here: http://www.resedit.net/.

PROBLEM:

When I resize my window, static controls slightly flicker.

MY EFFORTS TO SOLVE PROBLEM:

I believe that my code has no memory leaks-therefore I doubt this is the problem, but being inexperienced, I would highly appreciate if my assumption can be somehow confirmed.

I think that I have properly handled WM_ERASEBKGND, and I have excluded styles CS_VREDRAW and CS_HREDRAW from my window class-therefore flickering should not be caused because of this.

I have forgot to mention, that my window has WS_CLIPCHILDREN style, so I am mentioning that now, in response to the comment bellow made by member Roger Rowland.

I have implemented double buffering for both handlers, in order to avoid flickering.

QUESTIONS:

  1. How can I modify code in demo project to get rid of flickering?

  2. I need advice on how to optimize both WM_PAINT and WM_CTLCOLORSTATIC handlers, so my painting code gets more efficient and faster.

A small note for second question:

I was thinking to improve my code by drawing the entire picture on the main window’s background, and to put transparent static controls on top of the part of the picture that corresponds that static controls background.

That way, I would only return NULL_BRUSH in my WM_CTLCOLORSTATIC handler, and do all the work in the WM_PAINT.

Am I on the right track with this idea? Could this work ?

Thank you. Regards.


Solution

  • Firstly, your App is leaky as hell. Haven't looked for leaks, but most of them should be in WM_CTLCOLORSTATIC as you forget to delete HBITMAP's(use this neat freeware http://www.nirsoft.net/utils/gdi_handles.html to look for gdi leaks).

    Secondly, your code is way to big. I noticed that you didn't use functions, maybe because you don't know what they are capable of. For example I would use:

    void DrawBackground(HDC &hDC, SOMEINFOSTRUCT GradientInfo, LPCTSTR Text);
    

    to simplify your code a lot.

    Anyway enough of lecturing, let's go back to your problem. In WM_CTLCOLORSTATIC you must return brush, you want to paint background with. What you're doing now is painting background manually using Bitblt(), then return NULL brush and program paints it on your already painted background. Instead of painting it yourself, let the brush do the job. Simply instead of the last Bitblt() use CreatePatternBrush(), but then you need to take care of this Brush and here is what you should do:

    HBRUSH TempBrush = NULL; //Create global brush
    
    //Some Code....
    
    case WM_CTLCOLORSTATIC:
        {
            if (TempBrush != NULL)
            {
                DeleteObject(TempBrush);
                TempBrush = NULL;
            }
    
            //Let's skip to the end....
            GradientFill( MemDC, vertex3, 3, &gTriangle, 1, 
            GRADIENT_FILL_TRIANGLE );
    
            TempBrush  = CreatePatternBrush(bmp);// these 3 line should be at the 
                                                //end of every if
            DeleteDC(MemDC);      // or put them once outside if's
            DeleteObject(bmp);  // also if you delete HDC first, you don't need to
                                //unselect hbitmap
            }
            return (LRESULT)TempBrush;
        }
        break;
    
    case WM_CLOSE:
            {
                if (TempBrush != NULL)
                {
                    DeleteObject(TempBrush);
                    TempBrush = NULL;
                }
    //.......