Standard Controls: Button, Edit, List Box
I realize I've already used buttons in previous examples, so you should already be more or less familiar with them, however I figured that since I was using them in this example I might as well add it to the title for the sake of being complete.
One thing to remember about controls is that they are just windows. Like any other window they have a window procedure, a window class etc... that is registered by the system. Anything you can do with a normal window you can do with a control.
As you may remember from our earlier discussion of the message loop, windows communicate using messages,
you send them to get a control to do something, and when an event occurs on the control it
will send you a notification message back.
For the standard controls this notification will be a
The messages you send are widely varied between each control, and each control has it's own set
of messages. Once in a while the same message will be used for more than one kind of control,
but in general they will only work on the control they are intended for. This is especially
annoying with the listbox and combobox messages (
On the other hand, generic messages like
You can send messages using the
One of the most commonly used controls in the windows environment, the EDIT control, is used to allow the user to enter, modify, copy, etc... text. Windows Notepad is little more than a plain old window with a big edit control inside it.
Here is the code used to interface with the edit control in this example:
SetDlgItemText(hwnd, IDC_TEXT, "This is a string");
That's all it takes to change the text contained in the control (this can be used for pretty much any control that has a text value associated with it, STATICs, BUTTONs and so on).
Retreiving the text from the control is easy as well, if we can assume that the text is small and does not exceed a certain length of say, MAX_PATH:
VAR str : ARRAY [0..MAX_PATH] OF CHAR; BEGIN GetDlgItemText(hwnd, IDC_TEXT, str, SIZE(str)); (* ... *)
If the length of the text from the control is large, there is slightly more work:
VAR len : INTEGER; buf : POINTER TO ARRAY OF CHAR; BEGIN len := GetWindowTextLength(GetDlgItem(hwnd, IDC_TEXT)); IF len>0 THEN NEW(buf,len+1); GetDlgItemText(hwnd, IDC_TEXT, buf^, len + 1); (*... do stuff with text ... *) DISPOSE(buf); END; (* ... *)
First of all, we need to allocate some memory to store the string in, it won't just return us
a pointer to the string already in memory. In order to do this, we first need to know how
much memory to allocate. There isn't a
Now that we have the length, we can allocate some
memory. Here I've added a check to see if there is any text to begin with, since most likely
you don't want to be working with an empty string... sometimes you might, but that's up to you.
Assuming that there is something there to work with, we call
Note that I added
If I lost you talking about null terminators, please refer to the Modula-2 language tutorial which discusses strings.
Finally we can call
After we're all done using the text (which we'll get to in a moment), we need to free up
the memory that we allocated. To accomplish this, we simply call
Edits with Numbers
Entering text is all well and fine, but what if you want the user to enter in a number? This is a pretty common task, and fortunately there is an API to make this simpler, which takes care of all the memory allocation, as well as converting the string to an integer value.
VAR bSuccess : BOOL; nTimes : INTEGER; BEGIN nTimes := GetDlgItemInt(hwnd, IDC_NUMBER, bSuccess, FALSE);
Another useful feature is the
Another handy control is the list box. This is the last standard control that I'm going to cover for now.
The first thing you'll want to do with a listbox is add items to it.
index := SendDlgItemMessage(hwnd, IDC_LIST, LB_ADDSTRING, 0, CAST(LPARAM,ADR("Hi there!")));
As you can see, this is a pretty simple task. If the listbox has the
This message returns the index of the new item either way, and we can use this to perform other tasks on the item, such as associating some data with it. Usually this will be things like a pointer to a struct containing more information, or maybe an ID that you will use to identify the item, it's up to you.
SendDlgItemMessage(hwnd, IDC_LIST, LB_SETITEMDATA, index, nTimes);
The whole purpose of listboxes is to allow the user to select things from a list. Now sometimes we don't care when exactly they do this, for example with our Remove button, we don't need to know when the selection changes right away, we just check when the user activates the button.
However, sometimes you want to be able to do something right away, perhaps
display different or updated information based on what items are selected. In order to do
this we need to handle the notification messages that the listbox passes to us. In this
case, we are interested in
| WM_COMMAND : CASE LOWORD(wParam) OF | IDC_LIST : (* It's the listbox, check the notification code *) IF HIWORD(wParam) = LBN_SELCHANGE THEN (* Selection changed, do stuff here. *) List(hwnd); END; (* other controls *) ELSE END;
Getting Data from the ListBox
Now that we know the selection has changed, or at the request of the user, we need to get the selection from the listbox and do something useful with it.
In this example I've used a multiselection list box, so getting the list of selected items
is a little trickier. If it were a single selection listbox, than you could simply send
First we need to get the number of selected items, so that we can allocate a buffer to save the indexes in.
VAR hList : HWND; count : INTEGER; buf : POINTER TO ARRAY OF INTEGER; BEGIN hList := GetDlgItem(hwnd, IDC_LIST); count := SendMessage(hList, LB_GETSELCOUNT, 0, 0);
Then we allocate a buffer based on the number of items, and send
NEW(buf,count) SendMessage(hList, LB_GETSELITEMS, count, CAST(LPARAM,buf)); (* ... Do stuff with indexes *) DISPOSE(buf);
In this example,
One of the things you would likely want to do with this list of indexes, is retreive the data associated with each item, and do some processing with it. This is just as simple as setting the data was originally, we just send another message.
data := SendMessage(hList, LB_GETITEMDATA, index, 0);
If the data was some other type of value (anything that is 32-bits) you could simply cast
to the appropriate type.
For example if you stored
VAR hData : HBITMAP BEGIN (* ... *) hData := CAST( HBITMAP, SendMessage(hList, LB_GETITEMDATA, index, 0) ); (* ... *)
Like buttons, static controls are largely trivial, but for the sake or being complete I
include them here. Static controls are usually just that, static, meaning they don't change
or really do anything else very special, they're largely for displaying text to the user.
However you can make them slightly more useful by assigning them a unique ID (VC++ assigns
a default ID of
In the example code, I use one to display the data of the item selected in the list box, assuming one and only one is selected.
SetDlgItemInt(hwnd, IDC_SHOWCOUNT, data, FALSE);
Copyright © 1998-2011, Brook Miles. All rights reserved. Adapted for Modula-2 by Frank Schoonjans, with permission.