Using transfer buffers with OWL list boxes.

Back Up Next

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 you­­but 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 objects­­not 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 box­­Fred (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.


Copyright © 1998-2001 Yura Bidus. All rights reserved.