Code from Grocery List Problem

Listbox component

We want to list our grocery items in a listbox, so we add a listbox component to our main form. To do this simply click on the listbox icon in the standard component panel. Then click on the main form. Because we want to make the listbox cover the entire window except for the menu bar, we will select alClient in the Object Inspectors Align properties for the listbox.

Using the Listbox

There are two ways we will use the list box. First we want to transfer our list to the listbox as it exists in the array that holds our list. The text block is created by associating it with a strstream and then having the grocery list write itself to the stream. When this is finished the text block contains the list with each item listed on a separate line. When this is assigned to the Text property of our listbox, each line is a separate selection in the list box.

Using String Streams to Create Block of Text

To create a string stream, you first need a character buffer to write to (or read from). In this case we needed an output buffer, but the buffer is defined in the same way for an input stream.

char buff[500];
The variable buff (this can be anything, but buff indicating buffer is fairly standard) is an array of char, with 500 character positions. In this case since we are not going to do much with this program 500 should be large enough.

The output stream is created by a call to its constructor. The constructor requires the buffer and its size:

ostrstream out(buff,500);

Once the buffer open output is almost identical to that in the original program. One note about the print function for GroceryList (below), it has an extra line in bold. When working with strstreams you must make sure that the last item in the array is the null character '\0'. This marks the end of a c language string. Other than this function is the same as that used in console program.

void GroceryList::print(ostream & out)
{

	ArrayIterator< GroceryItem> next(_item);
	int count = 0;
	while (next)
	{
 		out<< ++count<< "  ";
		next().print(out);
		out << endl;
		next++;
	}
	 out<<'\0'; //needed since we are actually creating a String here
}

Updating the ListBox

Since we will frequently need to write our list into the listbox, we will make our own function paint to transfer the values. This is the WM_PAINT handler for windows.

void __fastcall TSDIAppForm::paint(TObject *Sender)
{
        char buff[32000];
        buff[0] = '\0';
        ostrstream out(buff,3200);
        _groceries.print(out);
        GroceryListBox->Items->Text = buff;
}

Anytime we alter the list we will call on paint to change the contents of the listbox.

File Management

File management in Builder is very simple. When you create a new file, you do not have a file name. If you save a new file for the first time, you must specify the file name. If you open a file the file name is specified at the time you open the file. Finally when you want to save a file with a different name you call Save As, and it allows the user to specify the file name. All of this requires that the program have a variable _fileName that is set to the empty string for a new file. When you save if the file name is not "" you need only call on the write function for GroceryList. If the file name is not specified, call on Save As where the user gets to choose the file name, then use the write function for Grocery List. Finally, to open a file, let the user pick the file and then use Grocery List read.

File Open and Save Dialogs

When you created your SDI application , in addtion to the menu icon there were two other icons in the window. These icons represent the open and save dialog boxes. They were added automatically as part of the SDI application. If you have a form that uses these dialog boxs, you need only select the icon of the dialog you need from the Dialogs component panel and click anywhere within the form and the dialog will be available in that form.

The file management functions are fairly straight-forward.

//---------------------------------------------------------------------
void __fastcall TSDIAppForm::OpenItemClick(TObject *Sender)
{
	if (OpenDialog->Execute()==IDOK)
    {
        _groceries.clearList();
        _fileName = OpenDialog->FileName; 
        Caption = ExtractFileName(_fileName);
        ifstream in (_fileName.c_str());
        _groceries.read(in);
       Invalidate(); //repaint the screen
    }
}
//---------------------------------------------------------------------
void __fastcall TSDIAppForm::SaveItemClick(TObject *Sender)
{
    if (_fileName !="")
    {
        ofstream out (_fileName.c_str());
        _groceries.write(out);
    }
    else
        SaveAs1Click(Sender);
}//---------------------------------------------------------------------------

void __fastcall TSDIAppForm::New1Click(TObject *Sender)
{
    _fileName = "";     
    Caption = "Untitled";
    _groceries.clearList();
   Invalidate();  //repaint the screen
}
//---------------------------------------------------------------------------

void __fastcall TSDIAppForm::SaveAs1Click(TObject *Sender)
{
    if (SaveDialog->Execute()==IDOK)
    {
        _fileName = SaveDialog->FileName; 
        Caption = ExtractFileName(_fileName);
        ofstream out (_fileName.c_str());
        _groceries.write(out);
    }
}
//---------------------------------------------------------------------------

Other Command Handlers

The add item handler requires a dialog box. It is given in the file describing that dialog box . Both of these handlers use the built-in dialog box MessageBox. It is a Borland override of the standard windows MessageBox dialog. The first parameter is the messge that appears in the box, the second is the title of the box and the last determines the buttons. Some of the buttons are MB_YESNO and MB_OKCANCEL. Notice that in builder the MessageBox is called from the Application.

The delete item function, uses the selection property of a listbox. To get the line selected, just use the ItemIndex property. This property is the subscript of the selected item. If the subscript is -1 then no item is chosen.

void __fastcall TSDIAppForm::DeleteItem1Click(TObject *Sender)
{
    int which = GroceryListBox->ItemIndex;
    if (which >=0)
    {
        char buff[255];
        ostrstream out(buff,255);
        _groceries.print(which,out);
       out<<'\0'; //complete the string
        if (Application->MessageBox(buff,"Delete This?",MB_YESNO)==IDYES)
        {
            _groceries.deleteItemAt(which);
            Invalidate();  //repaint the screen
        }
    }
}
//---------------------------------------------------------------------------

void __fastcall TSDIAppForm::TotalOrder1Click(TObject *Sender)
{
    double cost = _groceries.computeCost();
     char buff[255];
    ostrstream out(buff,255);
    out<<"The total for this order is $"<< cost<< '\0';
    Application->MessageBox(buff,"Total Cost",MB_OK);
}