0

INTRODUCTION AND RELEVANT INFORMATION:

I want to create a static control with gradient background.

I want to do it the following way:

Create the gradient at the main window’s background, and then place transparent static control on top of that background.

To do that, I have created RECT variable in WM_PAINT handler that positions the gradient at the position where static control should be.

Also, I try to use double buffering to avoid flickering ( I have also handled WM_ERASEBKGND, removed flags CS_VREDRAW and CS_HREDRAW from window class ).

I have also handled WM_SIZE message to invalidate the window, and to reposition static control properly.

In my WM_CTLCOLORSTATIC handler, I have returned NULL_BRUSH.

I have made a demo application to illustrate this, via application wizard in Visual Studio.

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

Bellow I will submit the modified code for WM_PAINT, and snippets for other handlers listed above:

    case WM_CREATE:
         {
             // get rectangle dimensions of the main window

             RECT rec;
             GetClientRect( hWnd, &rec );

             /******* main window's static control ******/

             HWND StaticControl = CreateWindowEx( 0, L"Static", L"", 
                                     WS_VISIBLE | WS_CHILD | SS_NOTIFY,
                                     ( 3 * ( rec.right - rec.left ) / 4 - 340 ) / 3,
                                     120 + ( rec.bottom - rec.top - 450 ) / 3, 
                                     150, 150, hWnd, (HMENU)4000, hInst, 0);
         }
         return (LRESULT)0;

    case WM_SIZE:
        {
             RECT rec; // main window's client rectangle

             GetClientRect( hWnd, &rec ); 

             SetWindowPos( GetDlgItem( hWnd, 4000 ),
                           NULL,
                           ( 3  * ( rec.right - rec.left ) / 4 - 340 ) / 3, 
                           120 + ( rec.bottom - rec.top - 450 ) / 3, 150, 150,
                           SWP_NOZORDER );

            InvalidateRect( hWnd, NULL, FALSE); 
        }
        return (LRESULT)0;

    case WM_ERASEBKGND:
        return (LRESULT)1;

    case WM_CTLCOLORSTATIC:
        return (LRESULT)( (HBRUSH)GetStockObject(NULL_BRUSH) );

    case WM_PAINT:
        {
            hdc = BeginPaint(hWnd, &ps);
            // TODO: Add any drawing code here...

            RECT r; // rectangle for main window's client area

            GetClientRect( hWnd, &r);

            HDC MemDC = CreateCompatibleDC(hdc); // back buffer

            // compatible bitmap for MemDC

            HBITMAP bmp = CreateCompatibleBitmap( hdc,
                                                  r.right - r.left, 
                                                  r.bottom - r.top ),
                    oldBmp = (HBITMAP)SelectObject( MemDC, bmp ); // needed for cleanup

            /***** draw a reference header at the top of the window *******/

            // position it properly at the top

            RECT rect;

            rect.left = r.left;
            rect.top = r.top;
            rect.right = r.right;
            rect.bottom = 120;

            FillRect( MemDC, &rect, (HBRUSH)GetStockObject(LTGRAY_BRUSH) );

            /**** main window's gradient background *******/

            //============ down triangle =========//

            TRIVERTEX vertex[3];

            vertex[0].x     = r.right;
            vertex[0].y     = r.bottom - r.top;
            vertex[0].Red   = 0xDB00;
            vertex[0].Green = 0xE500;
            vertex[0].Blue  = 0xF100;
            vertex[0].Alpha = 0x0000;

            vertex[1].x     = r.left;
            vertex[1].y     = r.bottom - r.top;
            vertex[1].Red   = 0x9500;
            vertex[1].Green = 0xB300;
            vertex[1].Blue  = 0xD700;
            vertex[1].Alpha = 0x0000;

            vertex[2].x     = r.left;
            vertex[2].y     = r.top + 120; 
            vertex[2].Red   = 0xDB00;
            vertex[2].Green = 0xE500;
            vertex[2].Blue  = 0xF100;
            vertex[2].Alpha = 0x0000;

            // Create a GRADIENT_TRIANGLE structure that
            // references the TRIVERTEX vertices.

            GRADIENT_TRIANGLE gTriangle;

            gTriangle.Vertex1 = 0;
            gTriangle.Vertex2 = 1;
            gTriangle.Vertex3 = 2;

            // Draw a shaded triangle.

            GradientFill( MemDC, vertex, 3, &gTriangle, 1, GRADIENT_FILL_TRIANGLE);

            //=============== upper triangle =================//

            TRIVERTEX vertex1[3];

            vertex1[0].x     = r.right;
            vertex1[0].y     = r.bottom - r.top;
            vertex1[0].Red   = 0xDB00;
            vertex1[0].Green = 0xE500;
            vertex1[0].Blue  = 0xF100;
            vertex1[0].Alpha = 0x0000;

            vertex1[1].x     = r.right;
            vertex1[1].y     = r.top + 120;
            vertex1[1].Red   = 0x9500;
            vertex1[1].Green = 0xB300;
            vertex1[1].Blue  = 0xD700;
            vertex1[1].Alpha = 0x0000;

            vertex1[2].x     = r.left;
            vertex1[2].y     = r.top + 120; 
            vertex1[2].Red   = 0xDB00;
            vertex1[2].Green = 0xE500;
            vertex1[2].Blue  = 0xF100;
            vertex1[2].Alpha = 0x0000;

            // Draw a shaded triangle.

            GradientFill( MemDC, vertex1, 3, &gTriangle, 1, GRADIENT_FILL_TRIANGLE);

            //**** draw background of the static control ****//

            //position it properly 

            rect.left = ( 3  * ( r.right - r.left ) / 4 - 340 ) / 3;
            rect.top = 120 + ( r.bottom - r.top - 450 ) / 3;
            rect.right = 150 + ( 3  * ( r.right - r.left ) / 4 - 340 ) / 3;
            rect.bottom = 270 + ( r.bottom - r.top - 450 ) / 3; // this one fails!!!

            //FillRect( MemDC, &rect, (HBRUSH)GetStockObject(WHITE_BRUSH) );

            // vertexes for static's gradient color

            //================= top rectangle =====================//

            TRIVERTEX vertexS[2], vertex1S[2] ;

            vertexS[0].x     = rect.left;
            vertexS[0].y     = rect.top;
            vertexS[0].Red   = 0x9500;
            vertexS[0].Green = 0xB300;
            vertexS[0].Blue  = 0xD700;
            vertexS[0].Alpha = 0x0000;

            vertexS[1].x     = rect.right;
            vertexS[1].y     = ( rect.bottom - rect.top ) / 2; 
            vertexS[1].Red   = 0x4F00;
            vertexS[1].Green = 0x8B00;
            vertexS[1].Blue  = 0xBD00;
            vertexS[1].Alpha = 0x0000;

            //================== bottom rectangle ====================//

            vertex1S[0].x     = rect.left;
            vertex1S[0].y     = ( rect.bottom - rect.top ) / 2; 
            vertex1S[0].Red   = 0x4F00;
            vertex1S[0].Green = 0x8B00;
            vertex1S[0].Blue  = 0xBD00;
            vertex1S[0].Alpha = 0x0000;

            vertex1S[1].x     = rect.right;
            vertex1S[1].y     = rect.bottom;
            vertex1S[1].Red   = 0x9500;
            vertex1S[1].Green = 0xB300;
            vertex1S[1].Blue  = 0xD700;
            vertex1S[1].Alpha = 0x0000;

            // Create a GRADIENT_RECT structure that 
            // references the TRIVERTEX vertices.

            GRADIENT_RECT gRect;

            gRect.UpperLeft  = 0;
            gRect.LowerRight = 1;

            // Draw a shaded rectangle. 

            GradientFill( MemDC, vertexS, 2, &gRect, 1, GRADIENT_FILL_RECT_V );
            GradientFill( MemDC, vertex1S, 2, &gRect, 1, GRADIENT_FILL_RECT_V );

            /****** draw back buffer on the screen DC *****************/

            BitBlt( hdc, 0, 0, r.right - r.left, r.bottom - r.top,
                    MemDC, 0, 0, SRCCOPY );

            /************** cleanup *******************/

            SelectObject( MemDC, oldBmp );

            DeleteObject(bmp); // compatible bitmap for MemDC

            DeleteDC(MemDC);

            EndPaint(hWnd, &ps);
        }
        return (LRESULT)0;

PROBLEM:

When I use GradientFill API in WM_PAINT handler, I get a bigger rectangle on the screen than it should be.

Bellow picture illustrates this result:

enter image description here

If I try to fill the same rectangle with some solid brush, everything works fine.

Bellow picture illustrates this result:

enter image description here

MY EFFORTS TO SOLVE PROBLEM:

I have placed breakpoints at the places where rectangle’s coordinates are set and haven't seen any problems.

Also, GradientFill returns TRUE, so it doesn't fail.

QUESTION:

How to fix this?

Thank you in advance.

Regards.

AlwaysLearningNewStuff
  • 2,939
  • 3
  • 31
  • 84

1 Answers1

1

I think it's simply that your calculations for the vertices are wrong:

        vertexS[1].y     = ( rect.bottom - rect.top ) / 2; 
        /* .... */
        vertex1S[0].y     = ( rect.bottom - rect.top ) / 2;

Should they be rect.top + ( rect.bottom - rect.top ) / 2 or something similar? Or even just rect.bottom and rect.top respectively?

It depends what you're trying to achieve, but either way I think it's currently not what you want. You would need to use rect.bottom and rect.top to get the same shape that your commented-out FillRect draws.

Jonathan Potter
  • 36,172
  • 4
  • 64
  • 79
  • Mr.Potter, I am trying to create a gradient that has a lighter color at the top and bottom of the rectangle, and darker one in the middle. I thought to fill upper half of the rectangle with the gradient brush that is lighter at the top, and brighter at the bottom. Then, the bottom half of the rectangle would be filled with the brush that is lighter at the bottom, and darker at the top. By filling the rectangle with those 2 brushes, I would achieve the effect of a single gradient brush, which is light at the top and bottom, and dark in the middle. Your solution works, so +1 from me.Thank you. – AlwaysLearningNewStuff Nov 11 '13 at 04:53