Windows Tutorial 3

Creating window assets

What good is a window when there is nothing in it, right? In this windows programming tutorial we will focus on creating usable windows assets. Some examples are edit boxes, buttons, list boxes, etc.


Prerequisites

Project type: Windows
Include files: windows.h
Library files: N/A



Creating a push button

Buttons are often used to initiate an action in an application, similar to the Yes, No, and OK buttons seen in message boxes, we can create our own buttons to do whatever we want. We might use a button to open up a separate window, for example.

To use a button, we firstly define a unique number for it. Using a 'define' statement makes it easier to manage our window assets later on. Normally it is best to keep your 'define' statements towards the top of your code to keep them in scope for the duration off the application.

#define IDC_MAIN_BUTTON 101
First, let me introduce to you another windows message that we can use in the LRESULT CALLBACK procedure (the windows message handler - if you remember from tutorial 2).

WM_CREATE can be used to populate our window with all sorts of windows assets. It is also very useful if you need to do any initializing for other parts of your application. For example, we can initialize Winsock for networking (which is exactly what we will do in Winsock Tutorial 6).

So, we need to add the following after switch(msg), if we use the final code from the previous tutorial.
case WM_CREATE:
	{
	}
	break;

Having done that we can now move on to the creation of the actual button.

Creating a button is very similar to creating a window, except we will use predefined classes supplied in the windows header files.

We can place the following code snippet in the WM_CREATE case. So, it will be placed into our window during creation.
HWND hWndButton=CreateWindowEx(NULL, 
		"BUTTON",
		"OK",
		WS_TABSTOP|WS_VISIBLE|WS_CHILD|BS_DEFPUSHBUTTON,
		50,
		220,
		100,
		24,
		hWnd,
		(HMENU)IDC_MAIN_BUTTON,
		GetModuleHandle(NULL),
		NULL);
We can set the first parameter to NULL as this is for 'extended styles', which we wont use for our button. As a side note we could have used CreateWindow() instead of CreateWindowEx() and left off the first parameter. But I have chosen to use CreateWindowEx() for consistency.

Parameter two is the main defining factor that determines that we are creating a button. The word 'BUTTON' is a predefined WNDCLASSEX window class. There are other predefined classes that we can use to create something entirely different. We will briefly go through some of these shortly.

The third parameter is what the button will display. In our case it will display 'OK'.

Parameter four is the style of the button. There are many different types of buttons available. In this case we are using the default push button (BS_DEFPUSHBUTTON). For a full overview of all the button types, I have linked the appropriate page on MSDN at the end of this tutorial.

But, here is a quick example of all of the different types.

Types of buttons available

Parameters five through to nine set the position and size of the button.

The next parameter is the handle to the parent window (which is important if we want the button to respond to the user input), followed by the identifier for the button. To refresh your memory, that was the DEFINE command we used earlier.

We don't need the last two parameters, so we can set them to NULL.

If we executed our program from here we should be greeted with an 'OK' button on the screen (which wont respond to clicks at this stage).

Creating an edit box

While we are at it, we might as well create an edit box so we can put our newly created button to good use.

One of the primary uses for an edit box is to act as a specified area for text input. For example, the run box.

Creating an edit box is virtually identical to creating a push button. We first setup a DEFINE for the unique number for the asset and then call the create window function like we did previously, with some minor changes.
hEdit=CreateWindowEx(WS_EX_CLIENTEDGE,
		"EDIT",
		"",
		WS_CHILD|WS_VISIBLE|ES_MULTILINE|ES_AUTOVSCROLL|ES_AUTOHSCROLL,
		50,
		100,
		200,
		100,
		hWnd,
		(HMENU)IDC_MAIN_EDIT,
		GetModuleHandle(NULL),
		NULL);
Once again our parameters are used in the same way as when we created our button. But, in this case we take advantage of parameter one, to give us a nice border around our newly created edit box.

You may have noticed the second parameter is now 'EDIT'. Again, this is a predefined class that has been generously provided by our friends at Microsoft. This tells our program that we intend on making an edit box.

So, now if we execute our application we will be greeted by an edit box and a button. We are now getting somewhere!

We can set the text in the text box by sending a windows message if we want to.

SendMessage(hEdit,WM_SETTEXT,NULL,(LPARAM)"Insert text here...");

Putting it all to good use

To make use of our newly created assets, we need to catch the windows messages in our callback procedure.

The windows message we need to take advantage of is WM_COMMAND. Every time we click a button of type text into a message box the WM_COMMAND message is being called. It is the LOWORD of wParam that tells us exactly what is going on.

To determine, specifically, if the button is pressed, we need to add the following to the callback procedure;
case WM_COMMAND:
	switch(LOWORD(wParam))
	{
		case IDC_MAIN_BUTTON:
		{
		}
		break;
	}
	break;
When the button is pressed the code after IDC_MAIN_BUTTON is executed. Remember that IDC_MAIN_BUTTON was defined earlier as 101. Hopefully, it now makes more sense that this is a better way of doing things, than trying to remember what 101 is.

To finish things of we might make the purpose of this particular button to display the contents of our edit box into a message box by adding the appropriate code into our IDC_MAIN BUTTON case.
LPWSTR buffer[256];
SendMessage(hEdit,
		WM_GETTEXT,
		sizeof(buffer)/sizeof(buffer[0]),
		reinterpret_cast<LPARAM>(buffer));
MessageBox(NULL,
		(LPWSTR)buffer,
		"Information",
		MB_ICONINFORMATION);
Now we have a fully working edit box and button combo. We are now starting to open up all sorts of possibilities that can now be used in your own applications.

The Full Code

#include <windows.h>

#define IDC_MAIN_BUTTON	101			// Button identifier
#define IDC_MAIN_EDIT	102			// Edit box identifier
HWND hEdit;

LRESULT CALLBACK WinProc(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam);

int WINAPI WinMain(HINSTANCE hInst,HINSTANCE hPrevInst,LPSTR lpCmdLine,int nShowCmd)
{
	WNDCLASSEX wClass;
	ZeroMemory(&wClass,sizeof(WNDCLASSEX));
	wClass.cbClsExtra=NULL;
	wClass.cbSize=sizeof(WNDCLASSEX);
	wClass.cbWndExtra=NULL;
	wClass.hbrBackground=(HBRUSH)COLOR_WINDOW;
	wClass.hCursor=LoadCursor(NULL,IDC_ARROW);
	wClass.hIcon=NULL;
	wClass.hIconSm=NULL;
	wClass.hInstance=hInst;
	wClass.lpfnWndProc=(WNDPROC)WinProc;
	wClass.lpszClassName="Window Class";
	wClass.lpszMenuName=NULL;
	wClass.style=CS_HREDRAW|CS_VREDRAW;

	if(!RegisterClassEx(&wClass))
	{
		int nResult=GetLastError();
		MessageBox(NULL,
			"Window class creation failed\r\n",
			"Window Class Failed",
			MB_ICONERROR);
	}

	HWND hWnd=CreateWindowEx(NULL,
			"Window Class",
			"Windows application",
			WS_OVERLAPPEDWINDOW,
			200,
			200,
			640,
			480,
			NULL,
			NULL,
			hInst,
			NULL);

	if(!hWnd)
	{
		int nResult=GetLastError();

		MessageBox(NULL,
			"Window creation failed\r\n",
			"Window Creation Failed",
			MB_ICONERROR);
	}

    ShowWindow(hWnd,nShowCmd);

	MSG msg;
	ZeroMemory(&msg,sizeof(MSG));

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

	return 0;
}

LRESULT CALLBACK WinProc(HWND hWnd,UINT msg,WPARAM wParam,LPARAM lParam)
{
	switch(msg)
	{
		case WM_CREATE:
		{
			// Create an edit box
			hEdit=CreateWindowEx(WS_EX_CLIENTEDGE,
				"EDIT",
				"",
				WS_CHILD|WS_VISIBLE|
				ES_MULTILINE|ES_AUTOVSCROLL|ES_AUTOHSCROLL,
				50,
				100,
				200,
				100,
				hWnd,
				(HMENU)IDC_MAIN_EDIT,
				GetModuleHandle(NULL),
				NULL);
			HGDIOBJ hfDefault=GetStockObject(DEFAULT_GUI_FONT);
			SendMessage(hEdit,
				WM_SETFONT,
				(WPARAM)hfDefault,
				MAKELPARAM(FALSE,0));
			SendMessage(hEdit,
				WM_SETTEXT,
				NULL,
				(LPARAM)"Insert text here...");

			// Create a push button
			HWND hWndButton=CreateWindowEx(NULL,
				"BUTTON",
				"OK",
				WS_TABSTOP|WS_VISIBLE|
				WS_CHILD|BS_DEFPUSHBUTTON,
				50,
				220,
				100,
				24,
				hWnd,
				(HMENU)IDC_MAIN_BUTTON,
				GetModuleHandle(NULL),
				NULL);
			SendMessage(hWndButton,
				WM_SETFONT,
				(WPARAM)hfDefault,
				MAKELPARAM(FALSE,0));
		}
		break;

		case WM_COMMAND:
			switch(LOWORD(wParam))
            {
				case IDC_MAIN_BUTTON:
				{
					char buffer[256];
					SendMessage(hEdit,
						WM_GETTEXT,
						sizeof(buffer)/sizeof(buffer[0]),
						reinterpret_cast<LPARAM>(buffer));
					MessageBox(NULL,
						buffer,
						"Information",
						MB_ICONINFORMATION);
				}
				break;
			}
			break;

		case WM_DESTROY:
		{
			PostQuitMessage(0);
			return 0;
		}
		break;
	}

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


Things to try

Try adding experimenting with out types of assets, like combo-boxes, other types of buttons, etc. I have listed a couple of links to help you on your way.

Don't forget to visit the forums to discuss any of the topics, as it can be very confusing sometimes!

Additional information

For additional information we have provided the following links.

Microsoft (MSDN) - Full list of common controls
Microsoft (MSDN) - Additional information about buttons
Microsoft (MSDN) - More information about using edit controls


Next tutorial

Tutorial 4 - Coming soon