Buttons and bad code

I am teaching myself how to program and I was hoping you can help me with the use of buttons and correct or point out errors, bad code practice, etc. in what I have currently been working on.

My program draws the displays relative to the width or height of the window to a memDC (hdcBuffer). I have found when I try to add a button it causes a lot of problems, and it appears to have two DCs one with the button in and one with it out and constantly flicks in-between them when re-sizing the window.

Also I would really appreciate it if you told me any mistakes, improvements, bad code etc. so that I can learn as well as improve (Anything, no matter how small!).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
case WM_CREATE:

	// Set format settings
	cOB = 36;
	cIB = 5;
	cBB = 250;
	monitorSelected = 0;

	// Load primary.bmp
	hInstance = ((LPCREATESTRUCT)lParam)->hInstance;
	hBP = LoadBitmap(hInstance, TEXT("primaryDisplay"));
	GetObject(hBP, sizeof(BITMAP), &bmpResource);
	cxP = bmpResource.bmWidth;
	cyP = bmpResource.bmHeight;

	// Load secondary.bmp
	hInstance = ((LPCREATESTRUCT)lParam)->hInstance;
	hBS = LoadBitmap(hInstance, TEXT("secondaryDisplay"));
	GetObject(hBS, sizeof(BITMAP), &bmpResource);
	cxS = bmpResource.bmWidth;
	cyS = bmpResource.bmHeight;

	// Poll monitor information and window information
	sA.pollMonitorInformation();
	GetClientRect(hwnd, &rectCA);

	return 0;
case WM_PAINT:
	hdcDisplay = GetDC(hwnd);
	hdcBuffer = CreateCompatibleDC(hdcDisplay);
	hdcP = CreateCompatibleDC(hdcDisplay);
	hdcS = CreateCompatibleDC(hdcDisplay);

	hBP = static_cast<HBITMAP>(SelectObject(hdcP, hBP));
	hBDP = static_cast<HBITMAP>(SelectObject(hdcS, hBS));

	rV = (static_cast<double>(GetSystemMetrics(78)) / (GetSystemMetrics(79)));

	cxMV = max(0, static_cast<int>(min((rectCA.right - cOB), (((rectCA.bottom - cOB - cBB) * rV)))));
	cyMV = max(0, static_cast<int>(min((rectCA.bottom - cOB - cBB), (((rectCA.right - cOB) / rV)))));

	cxBV = (rectCA.right - cxMV) / 2;

	hBD = CreateCompatibleBitmap(hdcDisplay, rectCA.right, rectCA.bottom);
	hBDP = static_cast<HBITMAP>(SelectObject(hdcBuffer, hBD));

	BitBlt(hdcBuffer, 0, 0, rectCA.right, rectCA.bottom, hdcDisplay, 0, 0, WHITENESS);

	hwndBPF = CreateWindow(
	TEXT("BUTTON"),  // Predefined class;
	TEXT("OK"),      // Button text
	WS_TABSTOP | WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON,  // Styles
	(rectCA.right - rectCA.left) / 2,         // x position
	rectCA.bottom - cBB + 10,         // y position
	100,        // Button width
	100,        // Button height
	hwnd,     // Parent window
	NULL,       // No menu.
	(HINSTANCE)GetWindowLong(hwnd, GWL_HINSTANCE),
	NULL);

	SelectObject(hdcBuffer, GetStockObject(DC_PEN));
	SetDCPenColor(hdcBuffer, RGB(205, 205, 205));

	Rectangle(hdcBuffer, cxBV, cOB + cyMV, rectCA.right - cxBV, cOB + cyMV + 1);

	for (i = 0; i < sA.getNumberOfMonitors(); i++)
	{
		rectT = {
			static_cast<int>((static_cast<float>(sA.getMonitor(i)->rcMonitor.left - GetSystemMetrics(76)) / (GetSystemMetrics(78))) * cxMV) + (cxBV)+cIB,
			static_cast<int>((static_cast<float>(sA.getMonitor(i)->rcMonitor.top - GetSystemMetrics(77)) / (GetSystemMetrics(79))) * cyMV) + (cOB / 2) + cIB,
			static_cast<int>((static_cast<float>(sA.getMonitor(i)->rcMonitor.right - GetSystemMetrics(76)) / (GetSystemMetrics(78))) * cxMV) + (cxBV)-cIB,
			static_cast<int>((static_cast<float>(sA.getMonitor(i)->rcMonitor.bottom - GetSystemMetrics(77)) / (GetSystemMetrics(79))) * cyMV) + (cOB / 2) - cIB,
		};

		SetStretchBltMode(hdcBuffer, HALFTONE);
		if (i == monitorSelected)
		{
			// X BORDERS
			StretchBlt(hdcBuffer, rectT.left - cIB, rectT.top, cIB, rectT.bottom - rectT.top, hdcS, 0, cIB, cIB, 600, SRCCOPY);
			StretchBlt(hdcBuffer, rectT.right, rectT.top, cIB, rectT.bottom - rectT.top, hdcS, cxP - cIB, cIB, cIB, 600, SRCCOPY);

			// Y BORDERS
			StretchBlt(hdcBuffer, rectT.left, rectT.top - cIB, rectT.right - rectT.left, cIB, hdcS, cIB, 0, cxP - (cIB * 2), cIB, SRCCOPY);
			StretchBlt(hdcBuffer, rectT.left, rectT.bottom, rectT.right - rectT.left, cIB, hdcS, cIB, cyP - cIB, cxP - (cIB * 2), cIB, SRCCOPY);
		}
		else
		{
			// X BORDERS
			StretchBlt(hdcBuffer, rectT.left - cIB, rectT.top, cIB, rectT.bottom - rectT.top, hdcP, 0, cIB, cIB, 600, SRCCOPY);
			StretchBlt(hdcBuffer, rectT.right, rectT.top, cIB, rectT.bottom - rectT.top, hdcP, cxP - cIB, cIB, cIB, 600, SRCCOPY);

			// Y BORDERS
			StretchBlt(hdcBuffer, rectT.left, rectT.top - cIB, rectT.right - rectT.left, cIB, hdcP, cIB, 0, cxP - (cIB * 2), cIB, SRCCOPY);
			StretchBlt(hdcBuffer, rectT.left, rectT.bottom, rectT.right - rectT.left, cIB, hdcP, cIB, cyP - cIB, cxP - (cIB * 2), cIB, SRCCOPY);
		}

		if (i == monitorSelected)
		{
			// Corner BORDERS
			BitBlt(hdcBuffer, rectT.left - cIB, rectT.top - cIB, cIB, cIB, hdcS, 0, 0, SRCCOPY);
			BitBlt(hdcBuffer, rectT.right, rectT.top - cIB, cIB, cIB, hdcS, cxP - cIB, 0, SRCCOPY);
			BitBlt(hdcBuffer, rectT.left - cIB, rectT.bottom, cIB, cIB, hdcS, 0, cyP - cIB, SRCCOPY);
			BitBlt(hdcBuffer, rectT.right, rectT.bottom, cIB, cIB, hdcS, cxP - cIB, cyP - cIB, SRCCOPY);
		}
		else
		{
			// Corner BORDERS
			BitBlt(hdcBuffer, rectT.left - cIB, rectT.top - cIB, cIB, cIB, hdcP, 0, 0, SRCCOPY | NOMIRRORBITMAP);
			BitBlt(hdcBuffer, rectT.right, rectT.top - cIB, cIB, cIB, hdcP, cxP - cIB, 0, SRCCOPY | NOMIRRORBITMAP);
			BitBlt(hdcBuffer, rectT.left - cIB, rectT.bottom, cIB, cIB, hdcP, 0, cyP - cIB, SRCCOPY | NOMIRRORBITMAP);
			BitBlt(hdcBuffer, rectT.right, rectT.bottom, cIB, cIB, hdcP, cxP - cIB, cyP - cIB, SRCCOPY | NOMIRRORBITMAP);
		}

		rM = (static_cast<double>(sA.getMonitor(i)->rcMonitor.right - sA.getMonitor(i)->rcMonitor.left) / sA.getMonitor(i)->rcMonitor.bottom - sA.getMonitor(i)->rcMonitor.top);

		cxMM = static_cast<int>(min((cxP), (cyP) * rM));
		cyMM = static_cast<int>(min((cyP), (cxP) / rM));

		if (sA.getMonitor(i)->rcMonitor.bottom == sA.getMonitor(i)->rcWork.bottom &&
			sA.getMonitor(i)->rcMonitor.top == sA.getMonitor(i)->rcWork.top &&
			sA.getMonitor(i)->rcMonitor.left == sA.getMonitor(i)->rcWork.left &&
			sA.getMonitor(i)->rcMonitor.right == sA.getMonitor(i)->rcWork.right)
		{
			StretchBlt(hdcBuffer, rectT.left, rectT.top, rectT.right - rectT.left, rectT.bottom - rectT.top, hdcS, cIB, cyP - cyMM + cIB, cxMM - (cIB * 2), cyMM - (cIB * 2), SRCCOPY);
		}
		else
		{
			StretchBlt(hdcBuffer, rectT.left, rectT.top, rectT.right - rectT.left, rectT.bottom - rectT.top, hdcP, cIB, cyP - cyMM + cIB, cxMM - (cIB * 2), cyMM - (cIB * 2), SRCCOPY);
		}
	}

	BitBlt(hdcDisplay, 0, 0, rectCA.right, rectCA.bottom, hdcBuffer, 0, 0, SRCCOPY);

	SelectObject(hdcBuffer, hBDP);
	SelectObject(hdcP, hBP);
	SelectObject(hdcS, hBDP);
	DeleteObject(hBD);
	DeleteDC(hdcDisplay);
	DeleteDC(hdcBuffer);
	DeleteDC(hdcP);
	DeleteDC(hdcS);
	return 0;
case WM_LBUTTONDOWN:
	xPos = LOWORD(lParam);
	yPos = HIWORD(lParam);
	for (j = 0; j < sA.getNumberOfMonitors(); j++)
	{
		rectY = {
			static_cast<int>((static_cast<float>(sA.getMonitor(j)->rcMonitor.left - GetSystemMetrics(76)) / (GetSystemMetrics(78))) * cxMV) + (cxBV),
			static_cast<int>((static_cast<float>(sA.getMonitor(j)->rcMonitor.top - GetSystemMetrics(77)) / (GetSystemMetrics(79))) * cyMV) + (cOB / 2),
			static_cast<int>((static_cast<float>(sA.getMonitor(j)->rcMonitor.right - GetSystemMetrics(76)) / (GetSystemMetrics(78))) * cxMV) + (cxBV),
			static_cast<int>((static_cast<float>(sA.getMonitor(j)->rcMonitor.bottom - GetSystemMetrics(77)) / (GetSystemMetrics(79))) * cyMV) + (cOB / 2),
		};
		if (xPos >= rectY.left &&
			xPos < rectY.right &&
			yPos >= rectY.top &&
			yPos < rectY.bottom)
		{
			monitorSelected = j;
			return 0;
		}
	}
	return 0;
case WM_ERASEBKGND:
	return 0;
case WM_SIZE:
	GetClientRect(hwnd, &rectCA);
	sA.pollMonitorInformation();
	return 0;
case WM_DISPLAYCHANGE:
	sA.pollMonitorInformation();
	return 0;
case WM_DESTROY:
	DeleteObject(hBP);
	DeleteObject(hBS);
	PostQuitMessage(0);
	return 0;
}


For extra credit, I am also trying to draw numbers on all the monitors and have it semi-transparent. But I cannot work out how to use DrawText with integers instead of a string.
I've been doing this kind of coding for 15 years and have never ever seen anyone make a CreateWindow() call in WM_PAINT handlers. That's almost inconceivable to me.
I am just trying to learn! :-D

EDIT: Seriously though, I am really confused about how to use buttons / child windows within main program. I am not sure whether I can create them the correct size outside of the WM_PAINT. This is why I am asking for help!
Last edited on
Creating and using a button control is the easiest thing in the world. The procedure for creating a main app window is to register a Window Class with RegisterClassEx() and then make a CreateWindowEx() call to create the main window. Before that call returns, and while its in its WM_CREATE handler code, make another call to CreateWindowEx() to create a child window of the "button" class. Set its parent as the main window and specify a child window control id in the (HMENU) parameter of the CreateWindowEx() call.

When the button is clicked, it will send a WM_COMMAND message to thw Window Procedure for the main window. The LOWORD of wParam will contain the child window control id of the button you set in its CreateWindowEx() call. I believe the notification message is in either the HIWORD of wParam, or the lParam (I forget), and the HWND is in either the HIWORD or lParam. I guess it would have to be in lParam, as it wouldn't fit in a 16 bit word.

I wasn't sure I wanted to help you Jack Hammered because I highly suspect you are a game programmer by looking at the code you posted, and its been my experience here that I shouldn't attempt to help game programmers because the techniques they use are arcane or esoteric in the extreme, and the best place for them to get advice is from game programming sites and other game programmers.
Last edited on
But here is an example that shows how to do it. The app puts two buttons on the main form, and puts up a MessageBox() when either is clicked. I was tempted to say a 'simple example', but I fear it will confuse you because it uses my message cracker scheme which is kind of sophisticated and complex, and I greatly fear I'll lose you with it. But it will compiule for you...

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
// Form3.h
#define Form_h

#define  IDC_BUTTON1          1500
#define  IDC_BUTTON2          1505

#define dim(x) (sizeof(x) / sizeof(x[0]))

struct WndEventArgs
{
 HWND                         hWnd;
 WPARAM                       wParam;
 LPARAM                       lParam;
 HINSTANCE                    hIns;
};

long fnWndProc_OnCreate       (WndEventArgs& Wea);
long fnWndProc_OnCommand      (WndEventArgs& Wea);
long fnWndProc_OnDestroy      (WndEventArgs& Wea);

struct EVENTHANDLER
{
 unsigned int                 iMsg;
 long                         (*fnPtr)(WndEventArgs&);
};

const EVENTHANDLER EventHandler[]=
{
 {WM_CREATE,                  fnWndProc_OnCreate},
 {WM_COMMAND,                 fnWndProc_OnCommand},
 {WM_DESTROY,                 fnWndProc_OnDestroy}
};
#endif 


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
//Form3.cpp
#include <windows.h>
#include <tchar.h>
#include "Form3.h"


long fnWndProc_OnCreate(WndEventArgs& Wea)
{
 HWND hCtl;

 Wea.hIns=((LPCREATESTRUCT)Wea.lParam)->hInstance;
 hCtl=CreateWindow(_T("button"),_T("Button #1"),WS_CHILD | WS_VISIBLE,75,60,150,30,Wea.hWnd,(HMENU)IDC_BUTTON1,Wea.hIns,0);
 hCtl=CreateWindow(_T("button"),_T("Button #2"),WS_CHILD | WS_VISIBLE,75,110,150,30,Wea.hWnd,(HMENU)IDC_BUTTON2,Wea.hIns,0);

 return 0;
}


long fnWndProc_OnCommand(WndEventArgs& Wea)
{
 switch(LOWORD(Wea.wParam))
 {
    case IDC_BUTTON1:
         MessageBox(Wea.hWnd,_T("You Thought About It Then Chose And Clicked Button #1!"),_T("Received WM_COMMAND Message!"),MB_OK);
         break;
    case IDC_BUTTON2:
         MessageBox(Wea.hWnd,_T("You Thought About It Then Chose And ClickedButton #2!"),_T("Received WM_COMMAND Message!"),MB_OK);
         break;
 }

 return 0;
}


long fnWndProc_OnDestroy(WndEventArgs& Wea)
{
 PostQuitMessage(0);
 return 0;
}



LRESULT CALLBACK fnWndProc(HWND hwnd, unsigned int msg, WPARAM wParam, LPARAM lParam)
{
 WndEventArgs Wea;

 for(unsigned int i=0; i<dim(EventHandler); i++)
 {
     if(EventHandler[i].iMsg==msg)
     {
        Wea.hWnd=hwnd, Wea.lParam=lParam, Wea.wParam=wParam;
        return (*EventHandler[i].fnPtr)(Wea);
     }
 }

 return (DefWindowProc(hwnd, msg, wParam, lParam));
}


int WINAPI WinMain(HINSTANCE hIns, HINSTANCE hPrevIns, LPSTR lpszArgument, int iShow)
{
 TCHAR szClassName[]=_T("Form3");
 WNDCLASSEX wc;
 MSG messages;
 HWND hWnd;

 wc.lpszClassName=szClassName;                wc.lpfnWndProc=fnWndProc;
 wc.cbSize=sizeof (WNDCLASSEX);               wc.style=CS_DBLCLKS;
 wc.hIcon=LoadIcon(NULL,IDI_APPLICATION);     wc.hInstance=hIns;
 wc.hIconSm=LoadIcon(NULL, IDI_APPLICATION);  wc.hCursor=LoadCursor(NULL,IDC_ARROW);
 wc.hbrBackground=(HBRUSH)COLOR_BTNSHADOW;    wc.cbWndExtra=0;
 wc.lpszMenuName=NULL;                        wc.cbClsExtra=0;
 RegisterClassEx(&wc);
 hWnd=CreateWindowEx(0,szClassName,szClassName,WS_OVERLAPPEDWINDOW,75,75,320,305,HWND_DESKTOP,0,hIns,0);
 ShowWindow(hWnd,iShow);
 while(GetMessage(&messages,NULL,0,0))
 {
    TranslateMessage(&messages);
    DispatchMessage(&messages);
 }

 return messages.wParam;
}


The reason I do things the way I do with my 'message cracker' scheme is that using the switch construct to map messages to the code which handles them becomes unmaintainable in real life type apps which are thousands upon thousands of lines of code long. You end up with a program with 10,000 lines of code in two procedures - WinMain() and your Window Procedure.
Last edited on
Freddie1 - I am not a games programmer at all, most of the code is just BitBlt the bitmaps to the HDC (Which display to scale representations of one's monitor array). Thanks a lot for the description, a lot of GUI programming seems alien and confusing to me! Then again, so does most programming...

I want to split my code down in to separate functions but again, it gets quite confusing when GUI programming IMHO. Yes, you were right, you have lost me terrible with your example.
Last edited on
Easier version of above with standard switch ...

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
//Form3.cpp
#include <windows.h>
#include <tchar.h>
#define  IDC_BUTTON1   1500
#define  IDC_BUTTON2   1505


LRESULT CALLBACK fnWndProc(HWND hWnd, unsigned int msg, WPARAM wParam, LPARAM lParam)
{
 switch(msg)
 {
   case WM_CREATE:
     {
        HINSTANCE hIns=NULL;
        HWND hCtl=NULL;
        hIns=((LPCREATESTRUCT)lParam)->hInstance;
        hCtl=CreateWindow(_T("button"),_T("Button #1"),WS_CHILD | WS_VISIBLE,75,60,150,30,hWnd,(HMENU)IDC_BUTTON1,hIns,0);
        hCtl=CreateWindow(_T("button"),_T("Button #2"),WS_CHILD | WS_VISIBLE,75,110,150,30,hWnd,(HMENU)IDC_BUTTON2,hIns,0);

        return 0;
     }
   case WM_COMMAND:
     {
        switch(LOWORD(wParam))
        {
         case IDC_BUTTON1:
              MessageBox(hWnd,_T("You Thought About It Then Chose And Clicked Button #1!"),_T("Received WM_COMMAND Message!"),MB_OK);
              break;
         case IDC_BUTTON2:
              MessageBox(hWnd,_T("You Thought About It Then Chose And ClickedButton #2!"),_T("Received WM_COMMAND Message!"),MB_OK);
              break;
        }
        return 0;
     }
   case WM_DESTROY:
     {
        PostQuitMessage(0);
        return 0;
     }
 }

 return (DefWindowProc(hWnd, msg, wParam, lParam));
}


int WINAPI WinMain(HINSTANCE hIns, HINSTANCE hPrevIns, LPSTR lpszArgument, int iShow)
{
 TCHAR szClassName[]=_T("Form3");
 WNDCLASSEX wc={0};
 MSG messages;
 HWND hWnd;

 wc.lpszClassName = szClassName;                wc.lpfnWndProc = fnWndProc;
 wc.cbSize        = sizeof (WNDCLASSEX);        wc.hInstance   = hIns;
 wc.hbrBackground = (HBRUSH)COLOR_BTNSHADOW;
 RegisterClassEx(&wc);
 hWnd=CreateWindowEx(0,szClassName,szClassName,WS_OVERLAPPEDWINDOW,75,75,320,305,HWND_DESKTOP,0,hIns,0);
 ShowWindow(hWnd,iShow);
 while(GetMessage(&messages,NULL,0,0))
 {
    TranslateMessage(&messages);
    DispatchMessage(&messages);
 }

 return messages.wParam;
}


You might get some benefit from this. Its a little getting started tutorial I wrote...

http://www.jose.it-berater.org/smfforum/index.php?topic=3389.0
Last edited on
Topic archived. No new replies allowed.