Using transfer buffers with OWL list boxes.
Even a trivial Windows application will use
dialog boxes. Modal dialog boxes, in particular, provide an excellent
form of communication between your application and the user. Unfortunately,
initializing a list box in a dialog box and retrieving the selected
item from the user in a traditional Windows application can be
an error-prone process.
In this article, we'll show how you can use a transfer
buffer to initialize list boxes easily in a dialog box of an ObjectWindows
Library (OWL) application. In addition, we'll show
how you can use the transfer buffer to store and retrieve data
other than just the index of the selected item.
Initializing list box data
In a Windows application written in C, you'll specify most
of the behavior of a dialog box in its corresponding dialog box
procedure. As you're probably aware, Windows will call
your dialog box procedure any time Windows needs to send a message
to the dialog box. Figure A shows the format of a routine dialog
box procedure.
Figure A - In a traditional Windows application, you typically perform the initialization and data retrieval from a dialog box procedure.
BOOL CALLBACK _export MyDlgProc(HWND hDlg,
UINT msg, WPARAM wParam, LPARAM lParam)
{
int selectedItem;
switch(msg)
{
case WM_INITDIALOG:
char* str1 = "Bob";
char* str2 = "Bill";
char* str3 = "Fred";
// Add the strings
SendDlgItemMessage(hDlg, IDD_LIST,
LB_ADDSTRING, 0, (LPARAM) str1)
SendDlgItemMessage(hDlg, IDD_LIST,
LB_ADDSTRING, 0, (LPARAM) str1)
SendDlgItemMessage(hDlg, IDD_LIST,
LB_ADDSTRING, 0, (LPARAM) str1)
// Select Item 0
SendDlgItemMessage(hDlg, IDD_LIST,
LB_SETCURSEL, 0, 0)
return FALSE;
case WM_COMMAND:
switch(wParam)
{
case IDOK:
selectedItem = SendDlgItemMessage(hDlg,
IDD_LIST, LB_GETCURSEL, 0, 0);
// Take action on the selected item
break;
case IDCANCEL:
EndDialog(hDlg, NULL);
break;
}
default:
return FALSE;
}
return TRUE; // We handled this message
}
As you can see, Windows holds the string and index information.
If you want to perform multiple operations on the strings that
appear in the list box, you'll need to build an array and
initialize each element yourself. If you create the list box using
the LBS_SORT flag (to automatically sort the strings
alphabetically), you'll need to alphabetize your array
as well.
If you want to redisplay a dialog box at a later time and reinitialize
its controls to the same values they had before, you'll
need to write code to store these values and then reset each control.
To reinitialize a list box this way would be cumbersome.
OWL dialog boxes
The basic purpose behind using the OWL framework is simplifying
Windows programming. To this end, OWL uses a mechanism introduced
in OWL 1.0: transfer buffers.
For each type of control that can display data in a dialog box,
there is a corresponding type of transfer buffer. Table A shows
the relationship between the OWL objects you use to create and
manipulate a dialog box, their transfer buffers, and the Window
controls the OWL objects use.
Table A - In an OWL application, an OWL object can use a transfer buffer to communicate with its corresponding Windows controls.
|
|
TStatic |
char [] |
Static Text |
TEdit |
char [] |
Edit Field |
TList Box |
TListBoxData |
List Box |
TComboBox |
TComboBoxData |
Combo Box |
TRadioButton |
WORD |
Radio Button |
TCheckBox |
WORD |
Check Box |
For complex dialog boxes, you'll create a structure that
contains an appropriate type of transfer buffer for each Windows
control you want to initialize. (If you don't want to initialize
a particular control, call the corresponding OWL object's
member function SetTransferStatus(FALSE).)
When you're ready to initialize the dialog box, you'll
need to create an instance of your transfer buffer structure.
After you've initialized each element of the transfer buffer
structure, you'll need to make sure an OWL object exists
for each control. Then, you'll call the SetTransferBuffer()
member function of the TDialog-derived object with the
structure's address.
When you call the TDialog-derived object's
Execute()
member function, each OWL object will initialize its control using
part of the data from the transfer buffer structure. (Because
the OWL objects perform this action in the same order you created
them, you'll need to make sure the corresponding transfer
buffer in the structure appears in this order as well.)
Finally, if the user exits the dialog box and the Execute()
function returns the value IDOK, you'll be able
to extract data values from each element of the transfer buffer.
If the user cancels the dialog box instead, the OWL objects won't
update the transfer buffer, and it will contain the same data
as before.
Transfer buffers for list boxes
For list boxes, you'll use a transfer buffer that's
an instance of the TListBoxData class. This class provides
four key data members you can use to initialize and retrieve data
from a list box: SelCount, SelIndices,
Strings,
and ItemDatas.
SelCount is an integer value that equals the number of
items the user selected. You'll never set this value directly;
OWL will set it for youbut only if the user clicks an
OK button.
SelIndices is an array of integers beginning with item
0. This array contains the index of each item the user selected.
(You'll use the SelCount data member to determine
how many items in this array are valid.)
Strings is an array of string
objectsnot
to be confused with String objects from the standard
library class. Each element of this array contains the text for
an item in the list box. You can return a pointer to the actual
string by calling the c_str() member function of a
string
object. By the way, if you create the list box using the LBS_SORT
style, the OWL framework will automatically rearrange these strings
into alphabetical order.
ItemDatas is an array of DWORD
values. You don't
have to use this array, but if you do, you can place in it ID
numbers, initial indices, or even pointers to objects (since a DWORD
is an unsigned long value). If the OWL framework
rearranges the strings in the Strings data member, it
will rearrange the ItemDatas values accordingly.
Getting transferred
Listing A: TRANSBUF.CPP
#include "owlpch.h"
#include <owl\checkbox.h>
#include <owl\listbox.h>
#include <stdio.h>
class TBufferApp : public TApplication {
public:
TBufferApp() {}
~TBufferApp() {}
void InitMainWindow()
{
TFrameWindow* frame = new TFrameWindow( 0, "TRANSBUF.EXE");
frame->AssignMenu(1);
SetMainWindow( frame );
}
void ViewList();
DECLARE_RESPONSE_TABLE(TBufferApp);
};
DEFINE_RESPONSE_TABLE1(TBufferApp,TApplication)
EV_COMMAND(101,ViewList),
END_RESPONSE_TABLE;
struct MyDialogBuffer
{
TListBoxData list;
};
void TBufferApp::ViewList()
{
MyDialogBuffer buf;
buf.list.AddStringItem("Fred (1)", 100, TRUE);
buf.list.AddStringItem("Bob (2)", 23);
buf.list.AddStringItem("Bill (3)", 506);
TDialog* dialog = new TDialog(GetMainWindow(), 1000);
TListBox* listbox = new TListBox(dialog, 1500);
dialog->SetTransferBuffer(&buf);
while(dialog->Execute() == IDOK){
char temp[80];
int idx = buf.list.GetSelIndices()[0];
int itemID = (int)buf.list.GetItemDatas()[idx];
const char* itemText = buf.list.GetStrings()[idx].c_str();
sprintf(temp,
"You selected %s, ID %d at index %d",
itemText,
itemID,
idx);
GetMainWindow()->MessageBox(temp, "Selected Item Detail");
}
dialog->Destroy();
}
int
OwlMain(int, char**)
{
return TBufferApp().Run();
}
When you finish entering the code, choose Save from the File menu.
Then, choose New from the File menu and enter the resource code
from Listing B in the editing window that appears.
Listing B: TRANSBUF.RC
1000 DIALOG 19, 15, 154, 91
STYLE WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "List Box"
{
DEFPUSHBUTTON "OK", IDOK, 19, 65, 50, 14
LISTBOX 1500, 15, 12, 124, 45, LBS_STANDARD
PUSHBUTTON "Cancel", IDCANCEL, 84, 65, 50, 14
}
1 MENU
{
POPUP "Menu"
{
MENUITEM "Dialog", 101
}
}
When you finish entering the code for the resource file, choose
Save from the File menu, enter TRANSBUF.RC in the Save
File As dialog box, and then click OK. To allow the compiler to
use its default module definition file, right-click on the name
TRANSBUF [.DEF] in the project window and choose Delete
Node from the pop-up menu. Click Yes when the IDE asks if you
want to delete this node.
To build and run the application, choose Run from the Debug menu.
When the TRANSBUF.EXE window appears, choose Dialog from the menu.
When the dialog box appears, it should resemble the one shown
in Figure B.
Figure B - The main dialog box's list box contains all the data we placed in the transfer buffer.
Notice that the selected item in the list boxFred
(1)was the first item we added to the transfer buffer,
but it now appears at the end of the list. This is true because
we used the LBS_STANDARD style in the resource description
of the list box, which specifies the LBS_SORT option
to sort the list items alphabetically.
Click on the item Bill (3) and then click OK. When the
dialog box disappears, a message box will appear in its place.
In this message box, you'll see the correct text, ID number,
and index data for this item, as shown in Figure C.
Figure C - If you click OK in the dialog box, you'll see detailed information about that item in this message box.
When you dismiss the message box, you'll notice that the
dialog box reappears with the item Bill (3) selected. Since
the association between the dialog box object dialog
and the transfer buffer buf is still valid (we haven't
destroyed either object), the dialog box object will reinitialize
the list box control with previous values. If you cancel the dialog
box and then redisplay it, you'll notice that the ViewList()
function resets the current selection to Fred (1).
To exit the application, double-click on its System menu icon.
To exit the IDE, choose Exit from the File menu.
Next month, we'll show how you can use a transfer buffer
to manipulate a more complex dialog box. In addition, we'll
look at the transfer buffer elements for the other Windows controls,
including combo boxes.
Conclusion
Transfer buffers are a valuable component of the OWL framework.
By taking advantage of transfer buffers in your OWL applications,
you'll simplify communication between your dialog box and
the rest of the application.
|