Click here to Skip to main content
15,884,237 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
INTRODUCTION AND RELEVANT INFORMATION:

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

The link to this picture shows how it looks:http://pbrd.co/1cx8DHd[^]

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[^]

http://www.uploadmb.com/dw.php?id=1383366869[^]

If you have problem with the download, just click bellow on the click here to Download the file now and everything will be fine.

Added additional link since the original one becomes broken from time to time:
http://www.filedropper.com/geotermistgrafika[^]

[The above project had memory leaks, the new link links to the improved version]


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.

My window has WS_CLIPCHILDREN style.

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?

Thank you.

Regards.
Posted
Updated 4-Nov-13 2:49am
v9
Comments
Captain Price 3-Nov-13 1:30am    
I compiled and ran your program, and I couldn't observe any flickering. But if you resize the window to be very small (zero in client area size), and maximize it again, the 2 static child window controls at the bottom of the main window are not redrawn properly.
AlwaysLearningNewStuff 3-Nov-13 8:10am    
I have tried to replicate your case and have failed.

Can you be more specific by providing more detailed description of the problem?

Did you try it on Windows 7 or Windows XP ?

Can you provide screenshots of what happens when you perform actions described in your comment ?

Thank you.

Regards.
enhzflep 3-Nov-13 23:45pm    
Interesting. I'll be having a look a little later today. A few quick points I notice:
1. The images of the blue 'buttons' aren't cached into a bitmap - they're redrawn each time WM_PAINT is issued.
2. There's an awful lot of code in the main WndProc. There is also much code that is repeated over and over, with different variables (mainly position/colour info)
3. You can set the window text of the blue 'buttons' when you create them. You can then extract this text from the control while drawing it and its text. (this can aid in keeping the code short and clear)

I'll have a bit more of a look a little later.
I'll see if we can't improve the drawing of the orange box - i suspect that the background should be drawn at the same time as the icons/text/emf file - drawn to an off-screen bitmap before being blasted to screen in one quick operation. This incorporates the approach advocated by Paul Watt (that is, do nothing in the WM_ERASEBKG handler).
AlwaysLearningNewStuff 4-Nov-13 8:40am    
I have found memory leaks in my demo aplication.I will update my question with a link to the reworked project.Thanks again.Regards.
AlwaysLearningNewStuff 4-Nov-13 9:47am    
I have updated mu post with the link to the project that, in my opinion, has no memory leaks.

Thanks again.

Regards.

Okay then, well where to begin?

I looked at this problem from a bunch of angles, I must've pondered and tried 1/2 a dozen combinations of approaches, but I think I've found one that is workable.

Basically, the idea is to do all the drawing on the dialog/window itself and then use button controls that are invisble, to simplify the handling of mouse/keyboard messages. Actually, I've only just considered keyboard messages just now - if you want to be able to navigate through the various controls on the window using the keyboard, you really need to provide some kind of visual indication of state - whether it be focus, hover or active. Understand that my code hasn't taken this into consideration, though it could easily be adapted..

First, I tried drawing the orange square with the 3 buttons and the map into a memDC and blasting that to screen via a subclassed static's hdc. This worked okay - there was no longer any flicker or problem in the drawing of the map - the problem though was that there was a brief moment when the main window's background was visible where the orange square should be.

I also realized that the way that windows can allow you to attach so many pieces of data to the window itself is really convenient and it simplifies the code greatly, since each function can be written in a generic way before being re-used with differing input data. This caused me to wrap the blue buttons into a c++ class. This being an approach quite similar to mfc/win32++/wxWidgets/Qt - this made it very easy to cleanly attach the window text, font and image to the blue buttons. It also helps simplyfy the way the data is stored in the source code. Problem was though there was still the dreaded flicker - flicker that occurs between the time the dialog background has been drawn and the time that all of the controls have been drawn.

Hmm.

So then I resigned myself to custom-drawing the whole interface as the main window's background + looking for old code I had that would handle the mouse messages.

But then I had an idea..

Couldn't I just subclass some button controls and in the WindowProc, just pass everything to the default window proc except for the drawing code? Wouldn't that do what I want?

So, I did just that. Made a simple struct for the data each button would need, updating the position of each whenever the window is moved. When it comes time to paint the window, I draw buttons at the calculated positions and move the invisible buttons to the same position. So with a few iines of code to move the controls and a few more to subclass the original WndProc, we've done away with the need to do dirty, lowish-level mouse-work. You may wish to handle mouse-events for the buttons by drawing directly to the window hdc, rather than the (full-size) off-screen one. If this wasn't quite quick enough, you could create a memDC the size of the button, draw the button with it's altered state (hover, active, focus) to this DC, before BitBlt to the screen.

I've (over)simplified a few things and just whipped up a 'short' sample that I hope will point you in a better direction.

Bear in mind, I've written the code to be clear and simple, rather than efficient. Improvements would certainly include
e.g
load EMF file just once
only create memDC and memBmp when window size changes


I suggest you just do the same trick for the 'buttons' on the orange panel with the map. That is, write a function that will draw a button covering a specified rect, on a specified hdc, with specified image and text. Keep the position of each one somewhere, update it when you re-size the window, re-position the invisible buttons in same positions, done!

Lastly (whew!) I noticed that the window could me made so small that the interface became very ugly, with some elements overlapping others. In this case, you may wish to look at the WM_GETMINMAXINFO message. By doing so, you can tell window the largest and smallest size the window can be made by the user. I've handled that message, using the size that avoided this.

So, the code!

resource.rc
XML
// Generated by ResEdit 1.5.11
// Copyright (C) 2006-2012
// http://www.resedit.net
#include <windows.h>
#include <commctrl.h>
#include <richedit.h>
#include "resource.h"

//
// Dialog resources
//
LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL
DLG_MAIN DIALOG 0, 0, 186, 95
STYLE DS_3DLOOK | DS_CENTER | DS_SHELLFONT | WS_CAPTION | WS_VISIBLE | WS_POPUP | WS_THICKFRAME | WS_SYSMENU
EXSTYLE WS_EX_WINDOWEDGE
CAPTION "Dialog"
FONT 8, "Ms Shell Dlg"
{
}


resource.h
C++
#ifndef IDC_STATIC
#define IDC_STATIC (-1)
#endif

#define DLG_MAIN                                100



main.cpp
C++
#define UNICODE
#define WINVER 0x0500

#include <windows.h>
#include <commctrl.h>
#include <stdio.h>
#include <gdiplus.h>
#include "resource.h"

#define firstBigBtnId 5000

using namespace Gdiplus;

HINSTANCE hInst;
WNDPROC oldBtnProc;

typedef struct
{
    wchar_t *text;
    RECT rect;
    HBITMAP img;
}  bigBtn_t, *pBigBtn_t;

bigBtn_t bigBtns[4] =
 {
     { L"УНОС ПОДАТАКА" },
     { L"ПРЕГЛЕД ПОДАТАКА" },
     { L"ИЗВЕШТАЈ" },
     { L"ПРЕТРАГА" }
 };

void GradientFillTriangle(HDC hdc, POINT p1, POINT p2, POINT p3, COLORREF col1, COLORREF col2, COLORREF col3)
{
    TRIVERTEX vertex[3];
    GRADIENT_TRIANGLE gTri = {0,1,2};

    vertex[0].x = p1.x;
    vertex[0].y = p1.y;
    vertex[0].Red = GetRValue(col1) << 8;
    vertex[0].Green = GetGValue(col1) << 8;
    vertex[0].Blue = GetBValue(col1) << 8;
    vertex[0].Alpha = 255;

    vertex[1].x = p2.x;
    vertex[1].y = p2.y;
    vertex[1].Red = GetRValue(col2) << 8;
    vertex[1].Green = GetGValue(col2) << 8;
    vertex[1].Blue = GetBValue(col2) << 8;
    vertex[1].Alpha = 255;

    vertex[2].x = p3.x;
    vertex[2].y = p3.y;
    vertex[2].Red = GetRValue(col3) << 8;
    vertex[2].Green = GetGValue(col3) << 8;
    vertex[2].Blue = GetBValue(col3) << 8;
    vertex[2].Alpha = 255;

    GradientFill(hdc, vertex, 3, &gTri, 1, GRADIENT_FILL_TRIANGLE );
}

void squareRadialFillRect(HDC hdc, RECT fillMe, COLORREF colInner, COLORREF colOuter)
{
    POINT p1, p2, p3;
    p3.x = fillMe.left + (fillMe.right - fillMe.left) / 2;
    p3.y = fillMe.top + (fillMe.bottom - fillMe.top) / 2;

    p1.x = fillMe.left;     p1.y = fillMe.top;
    p2.x = fillMe.right;    p2.y = fillMe.top;
    GradientFillTriangle(hdc, p1,p2,p3, colOuter, colOuter, colInner);

    p1 = p2;
    p2.x = fillMe.right;
    p2.y = fillMe.bottom;
    GradientFillTriangle(hdc, p1,p2,p3, colOuter, colOuter, colInner);

    p1 = p2;
    p2.x = fillMe.left;
    p2.y = fillMe.bottom;
    GradientFillTriangle(hdc, p1,p2,p3, colOuter, colOuter, colInner);

    p1 = p2;
    p2.x = fillMe.left;
    p2.y = fillMe.top;
    GradientFillTriangle(hdc, p1,p2,p3, colOuter, colOuter, colInner);
}

void diagGradFillRect(HDC hdc, RECT fillMe, COLORREF colDiag, COLORREF colCorners, bool isDiagLeftToRight)
{
    POINT p1,p2,p3;
    if (isDiagLeftToRight == true)
    {
        p1.x = fillMe.left;
        p1.y = fillMe.top;

        p2.x = fillMe.right;
        p2.y = fillMe.bottom;

        p3.x = fillMe.right;
        p3.y = fillMe.top;
        GradientFillTriangle(hdc, p1,p2,p3,colDiag,colDiag,colCorners);

        p3.x = fillMe.left;
        p3.y = fillMe.bottom;
        GradientFillTriangle(hdc, p1,p2,p3,colDiag,colDiag,colCorners);
    }
}

void GradientFillRect(HDC hdc, RECT fillMe, COLORREF colStart, COLORREF colEnd, bool isVertical)
{
    TRIVERTEX vertex[2];
    GRADIENT_RECT gRect;
    gRect.UpperLeft = 0;
    gRect.LowerRight = 1;
    vertex[0].x = fillMe.left;
    vertex[0].y = fillMe.top;
    vertex[0].Red = GetRValue(colStart) << 8;
    vertex[0].Green = GetGValue(colStart) << 8;
    vertex[0].Blue = GetBValue(colStart) << 8;
    vertex[0].Alpha = 255;

    vertex[1].x = fillMe.right;
    vertex[1].y = fillMe.bottom;
    vertex[1].Red = GetRValue(colEnd) << 8;
    vertex[1].Green = GetGValue(colEnd) << 8;
    vertex[1].Blue = GetBValue(colEnd) << 8;
    vertex[1].Alpha = 255;

    if (isVertical)
        GradientFill(hdc, vertex, 2, &gRect, 1, GRADIENT_FILL_RECT_V );
    else
        GradientFill(hdc, vertex, 2, &gRect, 1, GRADIENT_FILL_RECT_H );
}

void drawBigBtn(HDC dst, RECT btnRect, wchar_t *wndText)
{
    RECT topRect, botRect;
    topRect = botRect = btnRect;
    topRect.bottom = botRect.top = ((btnRect.bottom - btnRect.top)/2) + btnRect.top;
    GradientFillRect(dst, topRect, RGB(0x95,0xb3,0xd7), RGB(0x4f,0x8b,0xb0), true);
    GradientFillRect(dst, botRect, RGB(0x4f,0x8b,0xb0), RGB(0x95,0xb3,0xd7), true);

    HBRUSH blueBrush, oldBrush;
    blueBrush = CreateSolidBrush(RGB(79,129,189));
    oldBrush = (HBRUSH) SelectObject(dst, blueBrush);
    FrameRect(dst, &btnRect, blueBrush);
    SelectObject(dst, oldBrush);
    DeleteObject(blueBrush);


    RECT textRect = btnRect;
    textRect.top = textRect.bottom - 40;
    SetBkMode(dst, TRANSPARENT);

 //   mOldFont = (HFONT)SelectObject( mMemDC, mFont );
   int textLen = wcslen(wndText);
//    wndText = new wchar_t[textLen+2];
//    ZeroMemory(wndText, textLen+2 * sizeof(wchar_t));
//    GetWindowTextW(mHwnd, wndText, textLen+1);
    DrawTextEx( dst, wndText, textLen, &textRect, DT_CENTER | DT_VCENTER | DT_SINGLELINE | DT_WORDBREAK, 0 );
//    SelectObject(mMemDC, mOldFont);
    delete(wndText);

}

void drawFooter(HDC dst, RECT footerRect)
{
    RECT topRect, botRect;
    topRect = botRect = footerRect;
    topRect.bottom = botRect.top = ((footerRect.bottom - footerRect.top)/2) + footerRect.top;

    GradientFillRect(dst, topRect, RGB(0x31,0x83,0x99), RGB(0x45,0xa7,0xc1), true);
    GradientFillRect(dst, botRect, RGB(0x45,0xa7,0xc1), RGB(0x31,0x83,0x99), true);
}

void drawHeader(HDC dst, RECT headerRect)
{
    HBRUSH b1;
    int i,j,headerHeight = (headerRect.bottom - headerRect.top)+1;

        b1 = CreateSolidBrush(RGB(230,230,230));
        FillRect(dst, &headerRect,b1);
        DeleteObject(b1);
        HPEN oldPen, curPen;
        curPen = CreatePen(PS_SOLID, 1, RGB(216,216,216));
        oldPen = (HPEN)SelectObject(dst, curPen);
        for (j=headerRect.top;j<headerRect.bottom;j+=10)
        {
            MoveToEx(dst, headerRect.left, j, NULL);
            LineTo(dst, headerRect.right, j);
        }

        for (i=headerRect.left;i<headerRect.right;i+=10)
        {
            MoveToEx(dst, i, headerRect.top, NULL);
            LineTo(dst, i, headerRect.bottom);
        }
        SelectObject(dst, oldPen);
        DeleteObject(curPen);
        MoveToEx(dst, headerRect.left,headerRect.bottom,NULL);
        LineTo(dst, headerRect.right,headerRect.bottom);
}

void onPaint(HWND hwndDlg, WPARAM wParam, LPARAM lParam)
{
    RECT mRect, topRect, midRect, botRect;
    RECT btnRects[4], orangeRect;
    PAINTSTRUCT ps;
    HDC hdc;
    const int bannerHeight = 120, footerHeight=32;
    int btnSize=150, margin=16;
    int i, j;

    // 1 calc rects for header, footer and body
    GetClientRect(hwndDlg, &mRect);
    topRect = midRect = botRect = mRect;
    topRect.bottom = topRect.top + bannerHeight;
    botRect.top = botRect.bottom - footerHeight;
    midRect.top = topRect.bottom;
    midRect.bottom = botRect.top;

    // 2. Reposition the 4 invisible big button controls (subclassed control with empty WM_PAINT and WM_ERASEBKG handlers)
    RECT tmpRect;
    HWND tmpWnd;
    int n = 4;
    for (i=0; i<n; i++)
    {
        tmpRect = bigBtns[i].rect;
        tmpWnd = GetDlgItem(hwndDlg, firstBigBtnId+i);
        SetWindowPos(tmpWnd,HWND_TOP,tmpRect.left,tmpRect.top,btnSize,btnSize,SWP_NOZORDER);
    }

    orangeRect.top = midRect.top + margin;
    orangeRect.bottom = midRect.bottom - margin;
    orangeRect.right = midRect.right - margin;
    orangeRect.left = orangeRect.right - (mRect.right-mRect.left)/4;

    HBRUSH b1,b2,b3;

    hdc = BeginPaint(hwndDlg, &ps);

        HBITMAP memBmp, oldMemBmp;
        HDC memDC;
        memDC = CreateCompatibleDC(hdc);
        memBmp = CreateCompatibleBitmap(hdc, mRect.right,mRect.bottom);
        oldMemBmp = (HBITMAP)SelectObject(memDC, memBmp);

        // center portion of backround
        diagGradFillRect(memDC, midRect, RGB(218,228,240), RGB(149,179,215), true);

//DeferWindowPos()

        // draw map
        Graphics *graphics;
        Image *mapImg;
        mapImg = Image::FromFile(L"map2.emf");
        graphics = Graphics::FromHDC(memDC);//(hdc);

        graphics->SetSmoothingMode( SmoothingModeHighQuality );
        graphics->SetInterpolationMode( InterpolationModeHighQualityBicubic );

        const int o_height = mapImg->GetHeight(), o_width =  mapImg->GetWidth();
        float scale = 0.5;
        int mapPosX, mapPosY;

        scale = (float)orangeRect.right / o_width;
        if ( (float)(orangeRect.bottom-orangeRect.left)/o_height  <  scale)
            scale = (float)(orangeRect.bottom-orangeRect.left)/o_height;

        int marginX = (orangeRect.right - orangeRect.left) - (o_width*scale);
        int marginY = (orangeRect.bottom - orangeRect.top) - (o_height*scale);

        marginX /= 2;
        marginY /= 2;

        mapPosX = orangeRect.left + marginX; //( ((orangeRect.right - orangeRect.left) - (o_width)) / 2);
        mapPosY = orangeRect.top + marginY; //( ((orangeRect.right - orangeRect.left) - (o_width)) / 2);

        squareRadialFillRect(memDC, orangeRect, RGB(0xff,0xc8,0xaa), RGB(0xff,0x96,0x48));
        graphics->DrawImage(mapImg,
                            mapPosX, //orangeRect.left + (((orangeRect.right-mRect.left)-(o_width/2) ) / 2),
                            mapPosY, //orangeRect.top,//orangeRect.top + (((orangeRect.bottom-mRect.top)-(o_height/2) ) / 2),
                            (int)(o_width*scale),(int)(o_height*scale) );
        delete graphics;
        delete mapImg;


        // header
        drawHeader(memDC, topRect);

        // draw footer
        drawFooter(memDC, botRect);

        // draw buttons
        for (i=0;i<4;i++)
            drawBigBtn(memDC, bigBtns[i].rect, bigBtns[i].text);

        BitBlt(hdc,0,0,mRect.right,mRect.bottom,memDC,0,0,SRCCOPY);
        SelectObject(memDC, oldMemBmp);
        DeleteObject(memBmp);
        DeleteDC(memDC);

    EndPaint(hwndDlg, &ps);
}

LRESULT CALLBACK invisibleBtnProc(HWND hwndBtn, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    switch (uMsg)
    {
    case WM_PAINT:
        ValidateRect(hwndBtn, NULL);
        return 0;
    case WM_ERASEBKGND:
        return 1;
    }
    return CallWindowProc(oldBtnProc, hwndBtn, uMsg, wParam, lParam);
}


void onSize(HWND hwndDlg, WPARAM wParam, LPARAM lParam)
{
    RECT mRect, topRect, botRect, midRect;
    const int btnSize=150, margin=16;

    // 1 calc rects for header, footer and body
    GetClientRect(hwndDlg, &mRect);

    bigBtns[0].rect.left = ( 3  * ( mRect.right - mRect.left ) / 4 - 340 ) / 3;
    bigBtns[0].rect.top = 120 + mRect.top + ( mRect.bottom - mRect.top - 450 ) / 3;
    bigBtns[0].rect.right = bigBtns[0].rect.left + btnSize;
    bigBtns[0].rect.bottom = bigBtns[0].rect.top + btnSize;

    bigBtns[1].rect.left = 150 + 2 * ( 3  * ( mRect.right - mRect.left ) / 4 - 340 ) / 3;
    bigBtns[1].rect.top = 120 + ( mRect.bottom - mRect.top - 450 ) / 3;
    bigBtns[1].rect.right = bigBtns[1].rect.left + btnSize;
    bigBtns[1].rect.bottom = bigBtns[1].rect.top + btnSize;

    bigBtns[2].rect.left = ( 3  * ( mRect.right - mRect.left ) / 4 - 340 ) / 3;
    bigBtns[2].rect.top = 270 + 2 * ( mRect.bottom - mRect.top - 450 ) / 3;
    bigBtns[2].rect.right = bigBtns[2].rect.left + btnSize;
    bigBtns[2].rect.bottom = bigBtns[2].rect.top + btnSize;

    bigBtns[3].rect.left = 150 + 2 * ( 3  * ( mRect.right - mRect.left ) / 4 - 340 ) / 3;
    bigBtns[3].rect.top = 270 + 2 * ( mRect.bottom - mRect.top - 450 ) / 3;
    bigBtns[3].rect.right = bigBtns[3].rect.left + btnSize;
    bigBtns[3].rect.bottom = bigBtns[3].rect.top + btnSize;

    InvalidateRect(hwndDlg,NULL,false);
}

LRESULT CALLBACK DlgMain(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    switch(uMsg)
    {
    case WM_INITDIALOG:
    {
        HWND btn;
        int i, n=4;
        SetWindowPos(hwndDlg, HWND_TOP, 200,150,996,547,SWP_NOZORDER);
        for (i=0; i<n; i++)
        {
            btn = CreateWindow(WC_BUTTON, L"", WS_VISIBLE|WS_CHILD, 0,0,0,0, hwndDlg, (HMENU)(firstBigBtnId+i), hInst, NULL);
            oldBtnProc = (WNDPROC)SetWindowLong(btn, GWL_WNDPROC, (long) invisibleBtnProc);
            printf("%ld\n", oldBtnProc);
        }
    }
    return TRUE;

    case WM_CLOSE:
    {
        EndDialog(hwndDlg, 0);
    }
    return TRUE;

    case WM_ERASEBKGND:
        return 1;

    case WM_PAINT:
            onPaint(hwndDlg, wParam, lParam);
        return 0;

    case WM_GETMINMAXINFO:
        MINMAXINFO *lpmmi;
        lpmmi = (LPMINMAXINFO) lParam;
        lpmmi->ptMinTrackSize.x = 996;
        lpmmi->ptMinTrackSize.y = 548;
        return 0;

    case WM_SIZE:
        onSize(hwndDlg, wParam, lParam);
        return 0;

    case WM_COMMAND:
    {
        switch(LOWORD(wParam))
        {
            case firstBigBtnId+0:
            case firstBigBtnId+1:
            case firstBigBtnId+2:
            case firstBigBtnId+3:
                MessageBeep(MB_ICONEXCLAMATION);
            break;
        }
    }
    return TRUE;
    }
    return FALSE;
}


int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
{
    GdiplusStartupInput gdiplusStartupInput;
    ULONG_PTR           gdiplusToken;
    hInst=hInstance;
    InitCommonControls();

	/************** Initialize GDI+. *************************/
    GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);

    int result = DialogBox(hInst, MAKEINTRESOURCE(DLG_MAIN), NULL, (DLGPROC)DlgMain);
  	GdiplusShutdown(gdiplusToken);
  	return result;
}
 
Share this answer
 
Comments
AlwaysLearningNewStuff 7-Nov-13 10:48am    
Thank you so much!!!

I can learn so much from this!

I will try it out but it might take me some time to fully grasp all of the concepts implemented here.

In my main project I have handled WM_GETMINMAXINFO too, so the interface doesn't get ugly, but have omitted it to keep the demo as small as possible.

At the moment there is no need to interface with the keyboard in the main window.

I do not know if I have covered everything else ( I am in awe to be honest! ), so we shall definitely "talk" again, just give me some time for all of this to "sink in", since there is a lot of stuff to learn!

Thank you so much, again!

Best regards.
enhzflep 8-Nov-13 3:30am    
That's okay, you're most welcome. :)
If you've got any questions about how/why something works or the rationale behind a decision, just ask! I'll do my best to answer.
Cheers.
AlwaysLearningNewStuff 8-Nov-13 22:26pm    
I have copy-pasted this code, successfully compiled it, but got a crash.

I have got a debug assertion error, saying: Expression:_BLOCK_TYPE_IS_VALID(pHead->nBlockUse)

Can you please upload a demo project somewhere, when you have time of course, so we do not waste time searching for errors?

Or maybe I can fix this easily?

I have tried finding an error in your code, but have failed.Everything seems to be properly deleted/released.

Thanks again.

Regards.
enhzflep 9-Nov-13 0:13am    
Oh... :scratches-his-head:

I'm really not sure of the source of this error. I don't use VS generally speaking, so didn't know what to make of the error. I'd thought it may be related to gdiPlus and the way that I created/deleted instances of the Graphics class.

I.e I used Graphics *graphics = Graphics::FromHDC(tgtHdc);, followed later by delete when I later realized I could have simply writen Graphics graphics(tgtHdc); (without needing to delete it later. Its destructor would be called when it went out of scope)

But after building both release and debug versions of the code, I didn't receive any errors or Gdi leaks. # of GDI objects hovers at 22, occasionally peaking at 26 or 27 when resizing. But still, no error. Hmmmm.

About then I noticed that the cyrillic text displayed on the big buttons was incorrectly displayed when built with VS (I use GCC and Code::Blocks + ResEdit) I guess there's something I've done wrong there(?).

I'll have a look to see if I can locate the source of this inconsistancy, and will upload a copy of the project(s) and resources within 1/2 an hour, regardless of success/failure in locating the source of the text drawing bug.

I've built using VS2010 and Code::Blocks 12.11 and will include project files for each. I'll also include a gcc build that statically links the CRT etc libraries so it shouldn't have any dependencies other than those included with windows itself. Lastly, I don't have access to an XP virtual/real machine at the moment, I've just been using Win7. I hope that's not the source of troubles..

AHHHH! Just remembered that I had problems building your source-code, since we use different encoding on the source files. I've encoded it in UTF-8, which Code-Blocks likes. When I tried to build your unconverted code, I got a zillion eerrors about "unexpected null". I expect that the text is appearing incorrectly on the buttons due to this. When I looked at the text of the bigButtons with the debugger, it showed me the same thing that was printed on screen - leading me to think it's just an encoding issue.

I'll zip and upload the project. Expect a link shortly!
:)
enhzflep 9-Nov-13 0:58am    
Okay, here's a link to the project. I've included debug and release builds from both MSVC and GCC. I linked the gcc libs statially, so it shouldn't have any (not included) dependancies, though can't be sure if I re-built both debug and release after setting this flag.

Interestingly, I did uncover another error. The VS debug build displays the buttons correctly (with the exception of the text encoding) - yet the VS release mode vuild does something funky with the buttons - it 'draws' them when you click them!! You have to resize the window to remove them from view. Most confusing.. Perhaps I will have to break out the VS debugger after all. Buggger!

Anyhow, here's the link to the projects: EDIT: Bad-link removed. See bottom of post..


Finally (for now), thanks for the introduction to some parts of GDIplus I've not used before. You should have seen the look on my face when I realized that EMF and WMF files can hold vector-based images. It got even brighter when I realized I could use Inkscape to create them, and brighter again still, when I read the VS help docs that showed how I could record them, also how to use gradients. WOW!! <nerd>Talk about a lot of really enjoyable reading coming up!</nerd>

Regards.

EDIT: new updated link: myGeo2 project files and executables
You should look at the WM_ERASEBKGND message. Basically return 0, this will indicate that the window still needs to be erased, and WM_PAINT should take responsibility for painting the control.

I believe that will solve your problem.
 
Share this answer
 
Comments
AlwaysLearningNewStuff 3-Nov-13 8:15am    
Thank you so much Mr.Watt for trying to help me, I highly appreciate it ( I also use this opportunity to personally thank you for all those wonderful articles on GDI, they have helped me a lot ) !

What do you mean by "WM_PAINT should take responsibility for painting the control"?

Can you be more specific ?

Should I do it the way I have described in the section A small note for second question ?

Thank you again.

Regards.
Paul M Watt 3-Nov-13 9:23am    
I added the comment that WM_PAINT takes responsibility as information. You dont have to change anything you are doing with how you handle WM_PAINT.

I read through your write-up in greater detail, and it sounds like you have all of the the bases covered, the CS_VREDRAW and CS_HREDRAW flags, CLIPCHILDREN etc.

I thought it would be helpful if I gave you the purpose of a few of the techniques you described so you could decide if you were using them appropriately and make any necessary adjustments or simplify your program if possible.

SS_OWNERDRAW: This is the mechanism that allows the parent window to take responsibility to paint the child window. No subclassing is required with this flag.

CTL_COLORXX: A message is sent to the parent to allow them to configure the HDC for painting a child control, such as the background brush, pen colors and styles etc. No subclassing is required.


As I thought about it, you shouldnt have to use the CTL_COLOR messages on your static controls if you made them ownerdraw. Also, I am not clear on which control you refer to when you say subclass. Did you subclass the child controls you are painting, or are you referring to the parent window of the controls?

Thank you for the compliments on my articles.
AlwaysLearningNewStuff 3-Nov-13 10:16am    
Mr.Watt,

The orange static control is subclassed as it has 4 child static controls ( I think that linked picture explains it very clearly, just take a look at it ).

The demo project is just an optimized version of the project I work on, in order to illustrate the problems I face.

When user clicks on the one of the 4 child static controls ( map, or any of the 3 texts, in the orange static control ), certain actions take place.

These are omitted since they are irrelevant to the question I have asked.

The reason I have decided to owner draw child controls was because I didn't know how to draw bitmap and text in static control ( I was just starting out with Win32).

The only reason subclassing exists is because I need to handle notifications of 4 child static controls, and since they are children of orange static control, I thought to subclass it in order to handle them.

Just to clarify again, I have a "dummy" orange static control which only purpose is to have its background painted with orange gradient.

On top of this control, there are 4 child static controls ( 3 of them have picture on the left and text, and one has a map ).

I believe that my design can be greatly improved, that is why I have asked a subquestion in my post under A small note for second question.

Hopefully I have managed to clear things out.

I would strongly advise you to look at the project source code, as I believe everything is well explained there (I have got an impression that you haven't looked at it yet).

If there are further questions, please feel free to ask, I will respond in the shortest amount of time possible.

Thank you for your swift comment.

Regards.

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900