引言
在 20 世纪 90 年代,我还在使用旧的 Borland Turbo C++ 3.1 平台为 Windows 操作系统进行商业
编程 和
应用程序开发 时,我经常需要创建“列表框”。我用它们来存放各种东西,例如客户、库存项目、簿记交易、发票等等。
下面我将举例说明我是如何创建的。这个特别的列表框将用于存储供应商列表的单选列表框控件。是的,与 Microsoft Visual Studio 等
最佳开发平台相比,它很简陋,但它确实有效!
声明所需类
首先,这是项目 C++ 源文件中的“TVendDlg”类的类声明,该类将创建供应商的数据录入屏幕。
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();
};
|
接下来,您将看到“ListBoxDialog”类,它将用于填充列表框并从中检索用户的选择。
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];
};
|
点击“选择”按钮激活列表框
“TVendDlg”类的这个成员函数将在点击供应商数据录入屏幕上的“选择”按钮时触发。 “选择”按钮下的命令 “GetApplication()->ExecDialog(new ListBoxDialog(this, "VENDORDIALOG"));” 将实例化用于帮助填充列表框的“ListBoxDialog”类。
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();
}
}
|
构造列表框并填充它
这是来自项目的资源文件,它构造了供应商列表框的布局。该资源名为“VENDORDIALOG”。请注意,它使用了固定宽度的 Courier 字体,这将使列显示得整齐且均匀。
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
|
接下来,我将展示“ListBoxDialog”类的“SetupWindow”成员函数,它将使用来自供应商二进制数据文件“vend.txt”的数据填充列表框。命令 “SendDlgItemMsg(ID_LISTBOX, LB_ADDSTRING, 0, (LONG)char_array);” 将定义的常量“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();
}
|
最后,“ListBoxDialog”类的“HandleListBoxMsg”成员函数将在用户单击列表框中的选定行时触发。此时,列表框将消失,并且所选供应商记录的文件流偏移量将通过选定的“索引”组件计算出来。然后,此偏移量将在之前提到的“TVendDlg”类的“VendChs”成员函数的数据检索部分中使用。
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;
}
}
|
上面的 C++ 代码图解
这是从“TVendDlg”类创建的供应商数据录入屏幕。
点击“选择”按钮后,将出现此列表框,其中包含我录入的供应商记录。
点击列表框中的供应商后,列表框将消失,我内置的编程会将选定的供应商填充到供应商数据录入屏幕中,如下所示。
结论
您可以看到,如果您不具备面向对象编程所需的
开发技能,这可能会很困难。我的
软件设计技术可能有点冗长,但这一切都能正常工作,能够快速实现其预期目的,而不会出现 Windows 异常屏幕、惊恐的眼神、血压升高等等。如果说有什么的话,那就是它让人更加欣赏当今用于
定制软件设计的现代编码平台。