Some problems creating a GUI with the WinAPI in C++, as well as some questions.

Hello, I have been working with dos based programs for awhile now, and I wanted to try and move on to creating actual GUI based applications. Problem is that I am having a bit of trouble with it, I found a few guides online but they are either very very complicated or they are for C.

Right now I have been trying to convert a command prompt program that I made for class over to GUI but I am having alot of problems(Most of the time I don't even know where/how to start), The program just takes a value and converts it to USD, Euro and Yen, I thought about using edit boxes to hold the values but not only do I have no idea how to access that data inside them, but when I try and use the ES_NUMBER style it prevents the box from accepting decimal numbers.

Here is my code so far and my questions about how the whole thing works.

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
#include <windows.h>

HINSTANCE Currency_GUI_hInst;

HWND MainWindow;
HWND USDBox;
//HWND YenBox;
//HWND EuroBox;

enum {ID_EDIT_USD = 1};


LRESULT APIENTRY MainWindowProc(HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam);

int APIENTRY WinMain (HINSTANCE hInst, HINSTANCE hPrev, LPSTR line, int CmdShow)
{
    Currency_GUI_hInst = hInst;
    MSG msg;
    WNDCLASS wc;
    wc.cbClsExtra = 0;
	wc.cbWndExtra = 0;
	wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
	wc.hInstance = hInst;
	wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
	wc.hCursor = LoadCursor(NULL,IDC_ARROW);
	wc.lpfnWndProc = (WNDPROC) MainWindowProc;
	wc.lpszClassName = "Currency GUI Calc";
	wc.lpszMenuName = NULL;
	wc.style = CS_HREDRAW | CS_VREDRAW;


	RegisterClass(&wc);

	MainWindow = CreateWindow(
    "Currency GUI Calc",
    "Currency Calculator",
    WS_SYSMENU,
    CW_USEDEFAULT,
    CW_USEDEFAULT,
    256,
    256,
    NULL,
    NULL,
    hInst,
    NULL);

    ShowWindow(MainWindow, SW_SHOW);
    UpdateWindow(MainWindow);

	while(GetMessage(&msg,0,0,0))
	{
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}

	return msg.wParam;
}

LRESULT APIENTRY MainWindowProc(HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam)
{
    switch (msg)
    {
        case WM_DESTROY:
        PostQuitMessage(0);
        break;
        case WM_CREATE:
        USDBox = CreateWindow("Edit","0.00",ES_CENTER | WS_BORDER | ES_NUMBER | WS_CHILD | WS_VISIBLE | ES_AUTOHSCROLL | ES_RIGHT, 5,35,100,35,hwnd,(HMENU)ID_EDIT_USD,Currency_GUI_hInst,0);

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


With the edit box is it possible to tell windows to automatically adjust the length and width based on font settings?

This question is about the edit box, but more to do with child windows in general, when creating a child window is it the WS_CHILD style that tells windows that it is a child? Or is it the hwnd?

Also, how do I retrieve the data from a edit box? As well as change the data? I tried entering a float variable as the "LPCTSTR" but my compiler tells me that it must be a constant char.

Last question is rather minor, is there a way to change the background color of the main window to a custom color? So that I can choose the RGB values myself like you can do in HTML with Hex?
Last edited on

With the edit box is it possible to tell windows to automatically adjust the length and width based on font settings?

This question is about the edit box, but more to do with child windows in general, when creating a child window is it the WS_CHILD style that tells windows that it is a child? Or is it the hwnd?

Also, how do I retrieve the data from a edit box? As well as change the data? I tried entering a float variable as the "LPCTSTR" but my compiler tells me that it must be a constant char.

Last question is rather minor, is there a way to change the background color of the main window to a custom color? So that I can choose the RGB values myself like you can do in HTML with Hex?


You can figure out the size of a character using the TEXTMETRICS struct. First you would call GetDC() in WM_CREATE to get a handle to a device context, then you would call GetTextMetrics() and the horiz & vert size of a char in the given font could be extracted. If you wanted the text box to be able to hold 20 characters you would multiply by 20 for the width of the text box in your CreateWindow("edit",....) call.

If a window has the WS_CHILD style it is a child window and the parent will have to be filled in also in the CreateWindow() call.

Data is inserted and retrieved to and from edit controls (as well as many other controls) through...

GetWindowText()
SetWindowText()

This would put a value in USDBox...

char szBuffer[64];
strcpy(szBuffer,"1.23456");
SetWindowText(USDBox,szBuffer);

You ought to get yourself a copy of one of Petzold's Win32 books.

Thank you for the reply, I researched a bit more on the subject and finally managed to get some code thrown together that works well enough.

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

#include <windows.h>
#include <cstdlib>
#include <iostream>

HINSTANCE Currency_GUI_hInst;

HWND MainWindow;
HWND USDBox, USDLabel;
HWND YenBox, YenLabel;
HWND EuroBox, EuroLabel;

double USDBoxDataDouble = 0.00, EuroBoxDataDouble = 0.00, YenBoxDataDouble = 0.00;

bool USDIsWorking = false, EuroIsWorking = false, YenIsWorking = false;

TCHAR USDBoxData[21];
TCHAR EuroBoxData[21];
TCHAR YenBoxData[21];

enum {ID_EDIT_USD = 1, ID_EDIT_YEN, ID_EDIT_EURO};


LRESULT APIENTRY MainWindowProc(HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam);

int APIENTRY WinMain (HINSTANCE hInst, HINSTANCE hPrev, LPSTR line, int CmdShow)
{
    Currency_GUI_hInst = hInst;
    MSG msg;
    WNDCLASS wc;
    wc.cbClsExtra = 0;
	wc.cbWndExtra = 0;
	wc.hbrBackground = (HBRUSH)GetStockObject(DKGRAY_BRUSH);
	wc.hInstance = hInst;
	wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
	wc.hCursor = LoadCursor(NULL,IDC_ARROW);
	wc.lpfnWndProc = (WNDPROC) MainWindowProc;
	wc.lpszClassName = "Currency GUI Calc";
	wc.lpszMenuName = NULL;
	wc.style = CS_HREDRAW | CS_VREDRAW;


	RegisterClass(&wc);

	MainWindow = CreateWindow(
    "Currency GUI Calc",
    "Currency Calculator",
    WS_SYSMENU,
    CW_USEDEFAULT,
    CW_USEDEFAULT,
    192,
    128,
    NULL,
    NULL,
    hInst,
    NULL);

    ShowWindow(MainWindow, SW_SHOW);
    UpdateWindow(MainWindow);

	while(GetMessage(&msg,0,0,0))
	{
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}

	return msg.wParam;
}

LRESULT APIENTRY MainWindowProc(HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam)
{
    switch (msg)
    {
        case WM_DESTROY:
        PostQuitMessage(0);
        break;
        case WM_CREATE:
        USDBox = CreateWindow("Edit","0.00",ES_CENTER | WS_BORDER | WS_CHILD | WS_VISIBLE | ES_AUTOHSCROLL | ES_RIGHT, 5,20,100,20,hwnd,(HMENU)ID_EDIT_USD,Currency_GUI_hInst,0);
        EuroBox = CreateWindow("Edit","0.00",ES_CENTER | WS_BORDER | WS_CHILD | WS_VISIBLE | ES_AUTOHSCROLL | ES_RIGHT, 5,40,100,20,hwnd,(HMENU)ID_EDIT_EURO,Currency_GUI_hInst,0);
        YenBox = CreateWindow("Edit","0.00",ES_CENTER | WS_BORDER | WS_CHILD | WS_VISIBLE | ES_AUTOHSCROLL | ES_RIGHT, 5,60,100,20,hwnd,(HMENU)ID_EDIT_YEN,Currency_GUI_hInst,0);
        USDLabel = CreateWindow("Static",":USD",WS_CHILD | WS_VISIBLE,105,21,50,15,hwnd,0,Currency_GUI_hInst,0);
        EuroLabel = CreateWindow("Static",":Euro",WS_CHILD | WS_VISIBLE,105,41,50,15,hwnd,0,Currency_GUI_hInst,0);
        YenLabel = CreateWindow("Static",":Yen",WS_CHILD | WS_VISIBLE,105,61,50,15,hwnd,0,Currency_GUI_hInst,0);

        break;

        case WM_COMMAND:

            switch (HIWORD(wParam))
            {
                case EN_CHANGE:


                switch (LOWORD(wParam))
                {
                    case ID_EDIT_USD:
                    if (YenIsWorking == false && EuroIsWorking == false)
                    {
                        USDIsWorking = true;
                        SendMessage(USDBox,WM_GETTEXT,(WPARAM)20, (LPARAM)USDBoxData);
                        USDBoxDataDouble = strtod(USDBoxData, NULL);

                        EuroBoxDataDouble = USDBoxDataDouble * .7582;
                        YenBoxDataDouble = USDBoxDataDouble * 116.79;

                        sprintf ( EuroBoxData, "%.2f", EuroBoxDataDouble );
                        SendMessage(EuroBox, WM_SETTEXT, (WPARAM)0, (LPARAM)EuroBoxData);

                        sprintf ( YenBoxData, "%.2f", YenBoxDataDouble );
                        SendMessage(YenBox, WM_SETTEXT, (WPARAM)0, (LPARAM)YenBoxData);
                        USDIsWorking = false;
                    }

                    break;



                    case ID_EDIT_EURO:
                    if (USDIsWorking == false && YenIsWorking == false)
                    {
                        EuroIsWorking = true;
                        SendMessage(EuroBox,WM_GETTEXT,(WPARAM)20, (LPARAM)EuroBoxData);
                        EuroBoxDataDouble = strtod(EuroBoxData, NULL);

                        USDBoxDataDouble = EuroBoxDataDouble / .7582;
                        YenBoxDataDouble = USDBoxDataDouble * 116.79;

                        sprintf ( USDBoxData, "%.2f", USDBoxDataDouble );
                        SendMessage(USDBox, WM_SETTEXT, (WPARAM)0, (LPARAM)USDBoxData);

                        sprintf ( YenBoxData, "%.2f", YenBoxDataDouble );
                        SendMessage(YenBox, WM_SETTEXT, (WPARAM)0, (LPARAM)YenBoxData);
                        EuroIsWorking = false;
                    }

                    break;

                    case ID_EDIT_YEN:
                    if (USDIsWorking == false && EuroIsWorking == false)
                    {
                        YenIsWorking = true;
                        SendMessage(YenBox,WM_GETTEXT,(WPARAM)20, (LPARAM)YenBoxData);
                        YenBoxDataDouble = strtod(YenBoxData, NULL);

                        USDBoxDataDouble = YenBoxDataDouble / 116.79;
                        EuroBoxDataDouble = USDBoxDataDouble * .7582;

                        sprintf ( EuroBoxData, "%.2f", EuroBoxDataDouble );
                        SendMessage(EuroBox, WM_SETTEXT, (WPARAM)0, (LPARAM)EuroBoxData);

                        sprintf ( USDBoxData, "%.2f", USDBoxDataDouble );
                        SendMessage(USDBox, WM_SETTEXT,(WPARAM)0, (LPARAM)USDBoxData);
                        YenIsWorking = false;
                    }



                    break;

                }


                break;
            }

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



As far as I am concerned this topic is solved, though if anyone could give me some advice on how to make the code a bit more efficient and faster I would be grateful.

For the font size thing, would I call it like this?
{
HDC hdc;
hdc = GetDC(hwnd);
int size = GetTextMetrics(hdc, tmHeight);
}
I have no idea what to put in the "lptm" thing, or even how to get the font value. According to MSDN GetTextMetrics returns a bool, not a number.
Glad you are making progress HarbingTarbl.

You are going to have to disabuse yourself of the concept of returning data through function return values if you are going to use the Windows Api ( application programming interface ), because data is simply not returned that way. What gets returned in return values such as the BOOL you mentioned above with regard to the GetTextMetrics() call are success/failure codes. The data you want & need conversely is returned through function parameters - particularly structs. In the case above, if you wanted to know what the pixel height & width was of the particular font presently selected into the device context on your computer, you would do this in the WM_CREATE handler code...

case WM_CREATE:
{
HDC hDC;
TEXTMETRICS tm;
unsigned int iCharWidth, iCharHeight;


hDC=GetDC(hwnd);
GetTextMetrics(hDC, &tm);
iCharWidth=tm.tmHeight;
iCharWidth=tm.tmAveCharWidth;
ReleaseDC(hwnd,hDC);
}
break;

Note that tm is declared as a struct of TEXTMETRIC type. Its address (a pointer) is passed to GetTextMetrics() uninitialized. The function loads the struct when it is called, and your metrics are returned to you through the tm parameter - now initialized.

This EXACT pattern is repeated everywhere in the Api. It might take some time getting used to. Take a look for example at your msg parameter to GetMessage() in your WinMain(). Note that you are passing it to GetMessage() uninitialized; it is initialized after the call, then passed to translate message.
Last edited on
Topic archived. No new replies allowed.