Creating and Using Lists |
|
The TList Class |
Introduction |
We have already introduced the TStrings and its child the TStringList classes to create a list of strings. The role of TStrings is to create a list of single items, such as a list of names or a list of numbers. Sometimes you will want a list that itself is made of objects, an object in this sense being made of its own variables that characterize the object. The Visual Component Library provides various classes adapted for creating a list of objects, such as a list of cars, a list of houses, or a list of countries. As you know, such objects are made internal parts; for example a car is made of doors, tires, and options (air condition, power steering, cruise control, etc) while a house is made of rooms, furniture, books, electronic devices, etc. |
The TList is used to create a list of objects, any type of object. In fact, the TList considers that an object is defined as void *ObjectOfTheList[Index]; As you can see from this definition, TList does not care what type of list you want to create as long as you can define it as an entity, an object. The second issue to keep in mind is that, because TList does not care about the kind of list you want to create and does not know in advance the number of objects in your list, it conveniently keeps the objects of your list in an array. This means that, as you create your list, TList adds your objects in an expanding list. To initiate a VCL list, the first thing you must do is to declare an instance of a TList. If the list will be used as a local object, you can declare it in a function or event. If the list will be accessed by more than one event or function, you should declare its instance in the class that will host the list. Because TList is a VCL object, it must be declared as a pointer: TList * ListOfObjects; In the constructor of the class that will host the list, initialize the pointer by letting the compiler know which class the list instance belongs to. This is done using the new operator. After using the class, you should (must) delete it and recover the memory it was using. This is done using the delete operator. If the list was created globally in a form's class, you can delete it in the OnDestroy event of the form.
Before creating the actual list of objects, you should first define the kind of object will make up the list. One way to do this is to create a class that holds the members or characteristics of the object. For example, for a list of countries, each object can include the country name, its area, its population, its capital, its internet code, etc. The object can be created as follows:
To create such an object, you have two options, you can create a fully functional C++ class:
Because your object will only be used as a "frame", it is a traditional shortcut to create it as a simple structure, listing only the members you will need. Such an object can be created as follows:
This option provides you with three possibilities to create an instance of a country:
Once you have an object whose list you want to create, you can create each instance of the object and add the instance to the list. Because the objects are stored in an array, you would repeat this process for each object you want to include in the list. The TList class provides all the necessary operations you would need to perform on a list. Before adding an object to a list, you must "fill" it up first. This can be done by providing a value for each member of the object. Because you are responsible for creating the object, you are also responsible for providing the right value for each member of the object. If you provide a wrong or invalid value, TList does not have any mechanism of checking the internal values of the object. Consequently, you can build the simplest object that contains only one or two members, or you can build the most complex object that resembles a chemical reaction. The choice is yours. Once the object is complete or at least acceptable as far as its structure is defined, you can add it to the list. This can be taken care of by using the TList::Add() method. Its syntax is: int __fastcall Add(void * Item); The Item to add must be a recognizable and defined object. TList has an internal mechanism of counting the objects that are added to its list. This count is stored in the Count member variable. Therefore, you can call it anytime to find out how many items exist in a list. When using the Add() method, if the Item argument is the first item to be added to the list, it would receive an index of 0 and the Count would be 1. When you add an object, the Count is incremented. The newly added object receives an index of Count-1. This means that there are two main actions the Add() method performs. First it adds an object at the end of the list because the additions of items are incremental. Second, it returns the index of the newly added object. Knowing this index, you can access a specific object in the list array. As you are adding objects to the list, the TList class takes care of incrementing the number of objects in the list. This is taken care of by the Capacity member variable. In fact, the Capacity serves two purposes. If you want to specify the number of items in a list, for example imagine you want to create a list of ten countries, you can specify this using the Capacity variable. In the same way, if you want to find out how many items the list can include, you can call the Capacity member variable.
List navigation consists of moving back and forth among items or locating an item on a list. Once you have added some objects, each one is stored in an array of Items[] defined as follows: void *Items[int Index]; As you can see the TList::Items member variable takes an index as an integer and returns the object that is stored at that index. You can also use the Items member variable in a for loop to scan the list. To get to the very first item in the list, you have two options. You can call the Items variable with an index of 0 as Items[0]. Alternatively, you can call the TList::First() member variable whose syntax is: void * __fastcall First(void); In the same way, to access the last item of the list, you can either call Items[TList::Count-1] or use the TList::Last() method. Its syntax is: void * __fastcall Last(void); On the other hand, if you know the item you want access to, you can call the TList::IndexOf() method. Its syntax is: int __fastcall IndexOf(void * Item); To use the IndexOf() method, you must provide the item that you want to locate. If the item exists, this function returns the index of the item.
|
Practical Learning: Navigating a List |
int CurrentRecord; |
//--------------------------------------------------------------------------- __fastcall TfrmMain::TfrmMain(TComponent* Owner) : TForm(Owner) { CurrentRecord = 0; ListOfContacts = new TList; ... |
void __fastcall UpdateButtons(); |
//--------------------------------------------------------------------------- void __fastcall TfrmMain::UpdateButtons() { if( CurrentRecord <= 1 ) { btnFirst->Enabled = False; btnPrevious->Enabled = False; btnNext->Enabled = True; btnLast->Enabled = True; } else if( CurrentRecord < ListOfContacts->Count ) { btnFirst->Enabled = True; btnPrevious->Enabled = True; btnNext->Enabled = True; btnLast->Enabled = True; } else if( CurrentRecord == ListOfContacts->Count ) { btnFirst->Enabled = True; btnPrevious->Enabled = True; btnNext->Enabled = False; btnLast->Enabled = False; } } //--------------------------------------------------------------------------- |
void __fastcall DisplayRecord(const TContact * Contact); |
//--------------------------------------------------------------------------- void __fastcall TfrmMain::DisplayRecord(const Contact * Contact) { // Display or update each Edit control edtFirstName->Text = Contact->FirstName; edtLastName->Text = Contact->LastName; edtAddress->Text = Contact->Address; edtCity->Text = Contact->City; edtState->Text = Contact->State; edtZIPCode->Text = Contact->ZIPCode; edtCountry->Text = Contact->Country; edtEmailAddress->Text = Contact->EmailAddress; edtHomePhone->Text = Contact->HomePhone; } //--------------------------------------------------------------------------- |
//--------------------------------------------------------------------------- void __fastcall TfrmMain::btnFirstClick(TObject *Sender) { // Set the CurrentRecord variable to the first record CurrentRecord = ListOfContacts->IndexOf( ListOfContacts->First() ) + 1; // Retrieve the first record and assign it to a TContact object Contact = reinterpret_cast<TContact *>(ListOfContacts->First()); DisplayRecord(Contact); // Display the current index in the bottom Edit control edtRecordNumber->Text = IntToStr(CurrentRecord); // Call the UpdateButtons() method to decide what to do with the buttons UpdateButtons(); } //--------------------------------------------------------------------------- |
//--------------------------------------------------------------------------- void __fastcall TfrmMain::btnPreviousClick(TObject *Sender) { // Decrease the CurrentRecord variable CurrentRecord--; // Get the current record and assign it to a TContact instance Contact = reinterpret_cast<TContact *>(ListOfContacts->Items[CurrentRecord-1]); DisplayRecord(Contact); edtRecordNumber->Text = IntToStr(CurrentRecord); UpdateButtons(); } //--------------------------------------------------------------------------- |
//--------------------------------------------------------------------------- void __fastcall TfrmMain::btnNextClick(TObject *Sender) { Contact = reinterpret_cast<TContact *>(ListOfContacts->Items[CurrentRecord]); DisplayRecord(Contact); edtRecordNumber->Text = IntToStr(CurrentRecord+1); // Decrease the record numbering CurrentRecord++; UpdateButtons(); } //--------------------------------------------------------------------------- |
//--------------------------------------------------------------------------- void __fastcall TfrmMain::btnLastClick(TObject *Sender) { CurrentRecord = ListOfContacts->Count; Contact = reinterpret_cast<TContact *>(ListOfContacts->Last()); DisplayRecord(Contact); edtRecordNumber->Text = IntToStr(ListOfContacts->Count); UpdateButtons(); } //--------------------------------------------------------------------------- |
//--------------------------------------------------------------------------- void __fastcall TfrmMain::FormCreate(TObject *Sender) { // When the form opens, act as if the Next button was clicked btnNextClick(Sender); // Display the total number of records on the navigation bar lblCount->Caption = ListOfContacts->Count; } //--------------------------------------------------------------------------- |
//--------------------------------------------------------------------------- void __fastcall TfrmMain::edtRecordNumberKeyDown(TObject *Sender, WORD &Key, TShiftState Shift) { // If the user press Enter while in the Edit control if( Key == VK_RETURN ) { // If the Edit control is empty if( edtRecordNumber->Text.IsEmpty() ) return; // Do nothing else if( (edtRecordNumber->Text.ToInt() > 0) && (edtRecordNumber->Text.ToInt() <= ListOfContacts->Count) ) { // Since the Edit control contains CurrentRecord = edtRecordNumber->Text.ToInt(); Contact = reinterpret_cast<TContact *>(ListOfContacts->Items[CurrentRecord-1]); DisplayRecord(Contact); UpdateButtons(); } else if( (edtRecordNumber->Text.ToInt() < 0) || (edtRecordNumber->Text.ToInt() > ListOfContacts->Count) ) { ShowMessage("The record number is not valid"); } } } //--------------------------------------------------------------------------- |
Operations on items of a list consist of adding, inserting, deleting, or moving items to or from a list. All these operation are already provided by the TList class. The most you have to do is to adapt the class to your goal. Although the operations on a list can be performed in the form layout we have used so far, they can better be viewed and appreciated in a view that displays all records at the same time. That is why sometimes spreadsheet views are used in databases. For this exercise, we will use, we will use a ListView control.
In the previous section of this lesson, we learned how to statically create a list. That is, we learned how you, the programmer, can create a list for the user. If you want the user to have more control on the list, you should give her the ability to add new items to a list or insert items in the order of her choice. We have already learned that, to add an item to the end of a list, you can use the TList::Add() method. To add a new record, we will provide the user with a dialog box that contains all the fields that compose a complete record. The user can provide the piece of information that are available. Upon clicking OK, we will retrieve the record and add it to the list. Once a few records exist in the list, it is time for maintenance. The ListView we have created will display only a few pieces of information, due to lack of space. That is why we will display only the name, the email address and the telephone number of a contact. If the user wants to see more details about a contact, we will allow her to select a record, either double-click it or click a button. A dialog box will display the whole record. To do this, when the user double-clicks a record, we will retrieve its index and display its details in the same dialog box used to create a new record. Upon closing the dialog box, if the user clicks OK after changing any piece of information on the contact, we will her if she wants to update the change.
|
Besides the TList::Add() method used to add an item to a list, the TList class also allows you to insert an item in any order inside the list. This operation is handled by the TList::Insert() method. Its syntax is: void __fastcall Insert(int Index, void *Item); The first argument of this function, Index, specifies the index that the new item will occupy after being added. Because the list of items is zero-based (since it is a C++ array), to add an item to the first position, specify Index as 0. In the same way, to add an item to the 3rd position, specify Index as 2. The Item argument is the item that you want to insert. There are two main ways you would use the Insert() method. If you know the exact position where you want to insert an object, then supply the known index. By contrast, you can use Insert() to insert a new item before or after one of your choice. To do this, you must first retrieve the index of the item that will succeed the one you want to add. This index would be used as a the Index argument.
If you find out that your list includes an item you don't need, you can remove such an item from your list. The deletion of an item from a TList list can be handled by the TList::Delete() method. Its syntax is: void __fastcall Delete(int Index); To process your request, this function needs the index of the item you want to delete. You can get this index using the TList::Items[] member variable. Besides the Delete() method, the TList class provides a method used to delete a record if you know the value of the record instead of its position. The method is TList::Remove() and its syntax is: int __fastcall Remove(void *Item); Instead of the index, the Remove method needs the item itself. If you supply an item that exists in the list, Remove() would delete it. If the operation is successful, Remove() returns the index the item had. There are two significant differences between the Delete() and the Remove() methods:
Clearing a list consists of deleting all of its records in one step. This operation can be taken care of by the TList::Clear() method. Its syntax is: void __fastcall Clear(); As you can see, there is no particular information this function needs. If the list is empty, the method would not do anything. If the list contains records, then all of them would be deleted. The only thing you might do is to warn the user especially if the records cannot be recovered.
Besides the TList class that we have studied in this lesson, the VCL provides a wide range of classes that can be used to create lists or maintain them. This means that, although you can create your lists from scratch using C++, there are already classes that can help you accomplish your scenario. TOrderedList: Like TList, the TOrderedList class is derived straight from TObject. The purpose of TOrderedList is to lay a foundation of creating a list that can be ordered. This class itself does not do anything. It gives birth to other classes that take care of the necessary function definitions. This is because C++ implements ordered lists differently. TQueue: A queue is a technique of arranging objects in an order so that items are aligned, like at the bank: the first person on the line is also the first person to be served and to get out of the bank. This technique is referred to as First-In First-Out (FIFO). To create such a list in a VCL application, you can use the TQueue class. It is derived from the TOrderedList class. TStack: A stack is a technique of arranging objects like when you put books in a box. If you decide to remove them, you would first remove the last book you had put in the box. This arrangement is referred to as Last-In First-Out (LIFO). To create such a list, you can use the TStack class which is derived from the TOrderedList. There are many other classes the VCL provides for lists. |
|
Copyright © 2003-2007 FunctionX, Inc. |
|