WanderingMathematician
WanderingMathematician

Reputation: 443

How do you create a button outside the WM_CREATE message in win32?

I'm using c++ with win32 and gdi+ for graphics.
When I initialize a button outside WM_CREATE, specifically in a WM_TIMER message, I can't draw anything else, after that one frame is drawn. Here's a snippet of the code:

case WM_TIMER:
    RECT client;
    GetClientRect(hWnd, &client);
    client.bottom-=100; //The bottom hundred pixels are static after I draw the first frame, so I never update them
    if(starting==true)
    {
        starting=false;
        hdc = GetDC(hWnd);
        hdcBuf = CreateCompatibleDC(hdc);
        hdcMem = CreateCompatibleDC(hdcBuf);
        hbmBackBM = CreateCompatibleBitmap(hdc, client.right, client.bottom );
        hbmOldBackBM = (HBITMAP)SelectObject(hdcBuf, hbmBackBM);

        Graphics temp(hdc);
        SolidBrush yelloworange(Color(250,225,65));
        temp.FillRectangle(&yelloworange,0,client.bottom,client.right,100); //Fill the bottom with yellow
        buttons[0]=CreateWindow("button","makereg", WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON, 100, 630, 60, 20, hWnd, HMENU(IDB_MAKEREG), NULL, NULL);
        //buttons[1]=CreateWindow("button","destroyreg", WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON, 100, 670, 80, 20, hWnd, HMENU(IDB_MAKEREG+1), NULL, NULL);
    }
    Graphics g(hdcBuf);

The first part is for double buffering, and the variables that I instantiate are global. I delete the HDCs and HBITMAPs in WM_DESTROY. starting is a global boolean that is instantiated as true. I do all of my drawing in this WM_TIMER message. If I comment out just the two lines where the buttons are created, everything runs normally. With them, it only draws out what is left in this WM_TIMER, and does not draw in the next one. All of the other drawing code is done to hdcBuf or g, and hdcBuf is then BitBlt'd onto hdc.

I tried creating the button in WM_CREATE, and then showing it in WM_TIMER, but that caused the same problem. I can't create and show the window in WM_CREATE, because otherwise it gets drawn over when I fill the bottom 100 pixels with a yellow color.

Is there a way to create and show a button outside WM_CREATE and outside WM_PAINT without crashing the rest of the code?

EDIT: Here is some of the code that stops working, in WM_TIMER:

if(mousex!=uptomousex && mousey!=uptomousey && lbuttondown==true) // this code draws a rectangle between the point where the user begins holding the left mousebutton, and where the mouse is right now.
{
    if(uptomousex-mousex>0 && uptomousey-mousey>0)
        g.DrawRectangle(&(p[0]), mousex, mousey, uptomousex-mousex, uptomousey-mousey);
    else if(uptomousex-mousex<0 && uptomousey-mousey>0)
        g.DrawRectangle((&p[0]), uptomousex, mousey, mousex-uptomousex, uptomousey-mousey);
    else if(uptomousex-mousex>0 && uptomousey-mousey<0)
        g.DrawRectangle((&p[0]), mousex, uptomousey, uptomousex-mousex, mousey-uptomousey);
    else if(uptomousex-mousex<0 && uptomousey-mousey<0)
        g.DrawRectangle(&(p[0]), uptomousex, uptomousey, mousex-uptomousex, mousey-uptomousey);
}

Some global variables:

bool lbuttondown=false;
float mousex=0;
float mousey=0;
float uptomousex=0;
float uptomousey=0;

Elsewhere in WndProc...

case WM_LBUTTONDOWN:
    lbuttondown=true;
    mousex=(float)GET_X_LPARAM(lParam);
    mousey=(float)GET_Y_LPARAM(lParam);
    uptomousex=mousex;
    uptomousey=mousey;
    break;
case WM_MOUSEMOVE:
    if(mousex!=GET_X_LPARAM(lParam) && mousey!=GET_Y_LPARAM(lParam))
    {
        uptomousex=(float)GET_X_LPARAM(lParam);
        uptomousey=(float)GET_Y_LPARAM(lParam);
    }
    break;

Upvotes: 2

Views: 1703

Answers (2)

Flot2011
Flot2011

Reputation: 4671

You are creating/getting at least 3 Device Context instances on each timer call, and you never delete/release them (at least in the sample that you posted), so no surprise that you are ending by crushing the whole GDI system.

For each GetDC() call, ReleaseDC() should be called,

for each CreateCompatibleDC() call, DeleteObject() should be called.

Upvotes: 1

WanderingMathematician
WanderingMathematician

Reputation: 443

I have not figured out why this happens, but I have created a simple patch: my own button class, which I'll share here:

class button
{
public:
    int x;
    int y;
    int width;
    int height;
    string text;
    void (*func)(short);
    button(int px, int py, int w, int h, string txt, void (*f)(short))
    {
        x=px;
        y=py;
        width=w;
        height=h;
        text=txt;
        func=*f;
    }
};

Then, there is a global vector of buttons, allbuttons, and in the WM_LBUTTONUP message I loop through allbuttons and check if the click was in the button, and if so I call func.

Its simple, more flexible, and it actually works. However, the graphics are worse, and I would still like to find out why the windows button is dysfunctional, just out of curiosity.

Upvotes: 0

Related Questions