INTRODUCTION
Many moons ago in the 1990s, when I was
programming business
application development in the old Borland Turbo C++ 3.1 platform for the Windows OS, I frequently had a need to create “list boxes”. I used them to hold a variety of things such as customers, inventory items, bookkeeping transactions, invoices and more.
Below I have illustrated an example of how I created one. This particular one will hold a list of vendors in a single selection list box control. And yes, it’s crude in comparison to the
best developer platforms like Microsoft Visual Studio, but it does work!
DECLARE THE NEEDED CLASSES
First, here is the class declaration from one of the project’s C++ source code files for the “TVendDlg” class that will create the data entry screen for the vendors.
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
|
// declare “TvendDlg”, a TDialog descendant
class TVendDlg : public TDialog {
public:
virtual void SetupWindow();
virtual void VendDel(RTMessage Msg)
= [ID_FIRST + ID_DELX_];
virtual void VendChs(RTMessage Msg)
= [ID_FIRST + ID_CHS1_];
virtual void VendPrn(RTMessage Msg)
= [ID_FIRST + ID_VPRN_];
char Vncode[MAXCCODE];
char Vnname[MAXCNAME];
char Vnstreet[MAXCSTREET];
char Vnstreet2[MAXCSTREET];
char Vncity[MAXCCITY];
char Vnstate[MAXCSTATE];
char Vnzip[MAXCZIP];
char VnTell1[MAXCTF1];
char VnFax1[MAXCTF1];
char Vnatt[MAXATT];
char VnPaytrm[MAXTERM];
char VnNote1[MAXNOTE];
char VnNote2[MAXNOTE];
TEdit *Edit1,*Edit2,*Edit3,*Edit4,*Edit5,*Edit6,*Edit7,*Edit8,*Edit9,*Edit10,
*Edit11,*Edit12,*Edit13;
TVendDlg(PTWindowsObject AParent, LPSTR name);
virtual BOOL CanClose();
};
| |
Next, you see the “ListBoxDialog” class that will be used to populate the list box and retrieve the user’s selection from it.
1 2 3 4 5 6 7 8 9 10 11 12
|
// declare “ListBoxDialog”, a TDialog descendant
class ListBoxDialog : public TDialog
{
public:
ListBoxDialog(PTWindowsObject AParent, LPSTR AName)
: TDialog(AParent, AName) {};
virtual void SetupWindow();
virtual void HandleListBoxMsg(RTMessage Msg)
= [ID_FIRST + ID_LISTBOX];
};
| |
CLICK THE “CHOOSE” BUTTON TO ACTIVATE THE LIST BOX
This member function of the “TVendDlg” class will be fired upon clicking the “Choose” button in the vendor data entry screen. The command under the “Choose” button, “GetApplication()->ExecDialog(new ListBoxDialog(this, "VENDORDIALOG"));”, will instantiate the “ListBoxDialog” class that is used to help populate the list box.
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
|
void TVendDlg::VendChs(RTMessage)
{
int a;
streambuf *inn = cin.rdbuf();
ifpstream ifile;
Globalvar = 0;
GetApplication()->ExecDialog(new ListBoxDialog(this, "VENDORDIALOG"));
// if the Global variable, “Globalvar” is set to 1 from the
// “ListBoxDialog::HandleListBoxMsg(RTMessage Msg)” member
// function, then proceed.
if( Globalvar == 1) {
// set the global flag, “hasbeenselected”, to signal a vendor
// has been selected from the list box.
hasbeenselected = 1;
// display the retrieved vendor data in the vendor data entry
// screen after the selection in the list box has been clicked
// by the user. the data for the selected vendor will be
// assigned to the edit controls in the vendor data entry
// screen.
ifile.open("vend.txt", ios::in | ios::binary);
inn = ifile.rdbuf();
// position the filestream ofthe binary vendor
// data file to the calculated filestream offset
// value of the selected list box item.
inn -> seekpos(offsetvar, ios::in);
for(a=0; a<MAXCCODE-1; a++) Vncode[a] = ifile.readByte();
Vncode[MAXCCODE-1] = 0;
Edit1->SetText(Vncode);
for(a=0; a<MAXCNAME-1; a++) Vnname[a] = ifile.readByte();
Vnname[MAXCNAME-1] = 0;
Edit2->SetText(Vnname);
for(a=0; a<MAXCSTREET-1; a++) Vnstreet[a] = ifile.readByte();
Vnstreet[MAXCSTREET-1] = 0;
Edit3->SetText(Vnstreet);
for(a=0; a<MAXCSTREET-1; a++) Vnstreet2[a] = ifile.readByte();
Vnstreet2[MAXCSTREET-1] = 0;
Edit4->SetText(Vnstreet2);
for(a=0; a<MAXCCITY-1; a++) Vncity[a] = ifile.readByte();
Vncity[MAXCCITY-1] = 0;
Edit5->SetText(Vncity);
for(a=0; a<MAXCSTATE-1; a++) Vnstate[a] = ifile.readByte();
Vnstate[MAXCSTATE-1] = 0;
Edit6->SetText(Vnstate);
for(a=0; a<MAXCZIP-1; a++) Vnzip[a] = ifile.readByte();
Vnzip[MAXCZIP-1] = 0;
Edit7->SetText(Vnzip);
for(a=0; a<3; a++) VnTell1[a] = ifile.readByte();
VnTell1[3] = '-';
for(a=0; a<3; a++) VnTell1[4+a] = ifile.readByte();
VnTell1[7] = '-';
for(a=0; a<4; a++) VnTell1[8+a] = ifile.readByte();
VnTell1[MAXCTF1-1] = 0;
Edit8->SetText(VnTell1);
for(a=0; a<3; a++) VnFax1[a] = ifile.readByte();
VnFax1[3] = '-';
for(a=0; a<3; a++) VnFax1[4+a] = ifile.readByte();
VnFax1[7] = '-';
for(a=0; a<4; a++) VnFax1[8+a] = ifile.readByte();
VnFax1[MAXCTF1-1] = 0;
Edit9->SetText(VnFax1);
for(a=0; a<MAXATT-1; a++) Vnatt[a] = ifile.readByte();
Vnatt[MAXATT-1] = 0;
Edit10->SetText(Vnatt);
for(a=0; a<MAXTERM-1; a++) VnPaytrm[a] = ifile.readByte();
VnPaytrm[MAXTERM-1] = 0;
Edit11->SetText(VnPaytrm);
for(a=0; a<MAXNOTE-1; a++) VnNote1[a] = ifile.readByte();
VnNote1[MAXNOTE-1] = 0;
Edit12->SetText(VnNote1);
for(a=0; a<MAXNOTE-1; a++) VnNote2[a] = ifile.readByte();
VnNote2[MAXNOTE-1] = 0;
Edit13->SetText(VnNote2);
ifile.close();
}
}
| |
CONSTRUCT THE LIST BOX AND POPULATE IT
This is from the project’s resource file, which constructs the layout for the vendors list box. The resource is named “VENDORDIALOG”. Notice that it uses the fixed width courier font, which will make the columns display nice and even.
1 2 3 4 5 6 7 8 9 10 11
|
VENDORDIALOG DIALOG DISCARDABLE LOADONCALL PURE MOVEABLE 30, 18, 208, 108
STYLE WS_POPUP | WS_DLGFRAME
FONT 10, "COURIER"
BEGIN
CONTROL "Vendor Name Vend. Code ", 10055, "static", SS_LEFT | WS_CHILD, 20, 3, 188, 8
CONTROL "&Exit" IDCANCEL, "BUTTON", WS_CHILD | WS_VISIBLE | WS_TABSTOP, 20, 93, 48, 12
CONTROL "Vendor Listing", 10056, "static", SS_LEFT | WS_CHILD, 75, 93, 200, 8
CONTROL "LISTBOX" ID_LISTBOX, "LISTBOX", WS_CHILD | WS_VISIBLE | WS_BORDER | WS_VSCROLL | 0x3L, 20, 15, 168, 73
END
| |
Next, I present the “SetupWindow” member function of the “ListBoxDialog” class that will populate the list box with data from the vendors binary data file, “vend.txt”. The command “SendDlgItemMsg(ID_LISTBOX, LB_ADDSTRING, 0, (LONG)char_array);”, adds each vendor name and vendor code pair as a row to the list box identified by the defined constant, “ID_LISTBOX”.
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
|
void ListBoxDialog::SetupWindow()
{
long int fileoffset, sizeofdatafile;
int a,fileinfo,t;
streambuf *inn = cin.rdbuf();
ifpstream ifile;
// this will loop around the “vend.txt” binary data file of
// vendors and add a data record to the list box, which includes
// vendor name and vendor code.
fileinfo = open("vend.txt", ios::in | ios::binary);
sizeofdatafile = filelength(fileinfo);
close(fileinfo);
ifile.open("vend.txt", ios::in | ios::binary);
inn = ifile.rdbuf();
fileoffset = 0;
do {
// initialize the char array, “char_array”, with space characters.
for(a=0; a<100; a++) char_array[a] = 32;
// read the vendor name and vendor code from the file stream.
inn -> seekpos(fileoffset, ios::in);
for(a=0; a<MAXCCODE-1; a++) char_array[32+a] = ifile.readByte();
inn -> seekpos(fileoffset+MAXCCODE-1, ios::in);
for(a=0; a<MAXCNAME-1; a++) char_array[a] = ifile.readByte();
// mask out white space characters.
for(a=0; a<100; a++) {
if(char_array[a]<33 || char_array[a]>126) char_array[a] = 32;
}
// read the sequential position of the record in the binary text file.
inn -> seekpos(fileoffset+VENDLEN-5, ios::in);
for(a=0; a<5; a++) char_array[70+a] = ifile.readByte();
// null space the end of the char array to suppress trailing random chars.
char_array[99] = 0;
// convert the char array to lower case.
strlwr(char_array);
// add the vendor name and vendor code pair to the list box control.
SendDlgItemMsg(ID_LISTBOX, LB_ADDSTRING, 0, (LONG) char_array);
// advance to the next record in the binary text file.
fileoffset = fileoffset + VENDLEN;
} while(fileoffset<sizeofdatafile);
ifile.close();
}
| |
Finally, the “HandleListBoxMsg” member function of the “ListBoxDialog” class will fire with a click by the user on the selected row in the list box. At this point, the list box will disappear and the file stream offset of the selected vendor record will be calculated with help from the selection’s “index” component . This offset will then be used in the data retrieval portion of the “VendChs” member function of the “TVendDlg” class mentioned previously.
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
|
void ListBoxDialog::HandleListBoxMsg(RTMessage Msg)
{
long int a, convert_to_number[5];
DWORD Idx;
// if the exit button is clicked, then exit and reset global variable to 0.
if ( Msg.LP.Hi == LBN_SELCANCEL ) Globalvar = 0;
// if a selection is made, then reset the global variable to 1 and proceed to calculate the
// filestream offset after getting the list box index of the selection, “Idx”.
if ( Msg.LP.Hi == LBN_SELCHANGE ) {
// initialize the char array, “char_array”, with space characters.
for(a=0; a<80; a++) char_array[a] = 32;
Globalvar = 1;
// get the index of the selected list box item.
Idx = SendDlgItemMsg(ID_LISTBOX, LB_GETCURSEL, 0, 0L);
char_array[79] = 0;
// use the index to retrieve the contents of the selected list box row into a char array, “char_array”.
SendDlgItemMsg(ID_LISTBOX, LB_GETTEXT, (WORD)Idx, (DWORD) char_array);
// close the list box window after retrieving info into the char array from above.
CloseWindow();
// this will take the auto-generated sequential
// position of the vendor record stored in each record of the binary text file, “vend.txt”
// from the char array and convert it to a numerical value to be multiplied by the defined constant,
// “VENDLEN”. this will produce the filestream offset I call “offsetvar”, which is used to locate the
// vendor data in the member function “VendChs” of the “TVendDlg” dialog class, which will populate
// the edit controls in the vendor data entry screen.
for(a=0; a<5; a++) {
convert_to_number[a] = 0;
if(char_array[70+a] == 48) convert_to_number[a] = 0;
if(char_array[70+a] == 49) convert_to_number[a] = 1;
if(char_array[70+a] == 50) convert_to_number[a] = 2;
if(char_array[70+a] == 51) convert_to_number[a] = 3;
if(char_array[70+a] == 52) convert_to_number[a] = 4;
if(char_array[70+a] == 53) convert_to_number[a] = 5;
if(char_array[70+a] == 54) convert_to_number[a] = 6;
if(char_array[70+a] == 55) convert_to_number[a] = 7;
if(char_array[70+a] == 56) convert_to_number[a] = 8;
if(char_array[70+a] == 57) convert_to_number[a] = 9;
}
offsetvar = ( (convert_to_number[0] * 10000) + (convert_to_number[1] * 1000) + (convert_to_number [2] * 100) + (convert_to_number [3] * 10 ) + (convert_to_number[4] * 1) ) * VENDLEN;
}
}
| |
THE ABOVE C++ CODE IN PICTURES
Here is the vendor data entry screen created from the “TVendDlg” class.
After clicking the "Choose" button, this list box appears with a vendor record I entered.
After clicking on the vendor in the list box, it will go away and the internal programming I made will populate the vendors data entry screen with the selected vendor as shown here.
CONCLUSION
As you can see, this can be challenging to follow if you don’t possess the
developer skills needed for object oriented programming. My
software design techniques can be a bit drawn out, but all this does function to quickly achieve its intended purpose without Windows exception screens, wide eyes, elevated blood pressure and the like. If anything, it makes one appreciate the modern coding platforms of today that are utilized for
custom software design.