MFC Collections: The CObArray Class |
|
An Array ob Objects |
Introduction |
The MFC is a library rich of collection classes that allow you to create and manage any type of list. In this tradition, it is equipped with the CObArray class. the particularity of this class is that it can be used to create a list of CObject values. This doesn't mean that the objects have to fit in any particular MFC scenario. Of course, there are a few rules you must follow but the main theory is that the class that would constitute the object must be derived from CObject. Normally, this is an advantage because such a class would be ready for serialization and MFC's exception handling. The CObArray class is part of MFC's collection classes and defined in the afxcoll.h library. Therefore, if you plan to use it to create and manage your list, make sure you include this header file where necessary. |
|
Setting Up an Array of Objects |
To create an object that you want to handle using a CObArray list, the first rule you must follow is that your class must be based on CObject. If you only plan to create an object and use it temporarily, this is all you need to do. If you want to be able to save the list, there are a few more steps you must follow. For example, you must include the DECLARE_SERIAL macro in the header file of your class and you must include the IMPLEMENT_SERIAL macro in the source file of your class. Also, if you plan to save your class, you must override the derived Serialize() method. This is necessary because you must specify what member variables would need to be persisted. |
Practical Learning: Setting Up a List |
Header File: Car.h |
#pragma once // CCar command target class CCar : public CObject { DECLARE_SERIAL(CCar) public: CCar(); CCar(CString tagnbr, CString mk, CString mdl, int yr); virtual ~CCar(); private: CString TagNumber; CString Make; CString Model; int Year; public: CString getTagNumber(void); void setTagNumber(CString tag); CString getMake(void); void setMake(CString mk); CString getModel(void); void setModel(CString mdl); int getYear(void); void setYear(int yr); virtual void Serialize(CArchive& ar); }; |
Source File: Car.cpp |
// Car.cpp : implementation file // #include "stdafx.h" #include "BCRSerial1.h" #include "Car.h" #include ".\car.h" IMPLEMENT_SERIAL(CCar, CObject, 1) // CCar CCar::CCar() : TagNumber(_T("")), Make(_T("")), Model(_T("")), Year(0) { } CCar::CCar(CString tagnbr, CString mk, CString mdl, int yr) { TagNumber = tagnbr; Make = mk; Model = mdl; Year = yr; } CCar::~CCar() { } // CCar member functions CString CCar::getTagNumber(void) { return TagNumber; } void CCar::setTagNumber(CString tag) { TagNumber = tag; } CString CCar::getMake(void) { return Make; } void CCar::setMake(CString mk) { Make = mk; } CString CCar::getModel(void) { return Model; } void CCar::setModel(CString mdl) { Model = mdl; } int CCar::getYear(void) { return Year; } void CCar::setYear(int yr) { Year = yr; } void CCar::Serialize(CArchive& ar) { CObject::Serialize(ar); if (ar.IsStoring()) { ar << TagNumber << Make << Model << Year; } else { ar >> TagNumber >> Make >> Model >> Year; } } |
List Setup |
When you are ready to start the list, like any other class, you must first declare a variable of type CObArray. To support this, the CObArray class is equipped with a simple default constructor. If you are planning to create and manage the list from one method or event, you can declare the variable locally. On the other hand, if you are planning to access the list from various sections of your code, you should declare the variable globally, for example, in the header file. |
Practical Learning: Setting Up a List |
// CarRental1Dlg.h : header file // #pragma once #include "afxcoll.h" // CCarRental1Dlg dialog class CCarRental1Dlg : public CDialog { // Construction public: CCarRental1Dlg(CWnd* pParent = NULL); // standard constructor // Dialog Data enum { IDD = IDD_CARRENTAL1_DIALOG }; protected: virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support // Implementation protected: HICON m_hIcon; CObArray ListOfCars; // Generated message map functions virtual BOOL OnInitDialog(); afx_msg void OnPaint(); afx_msg HCURSOR OnQueryDragIcon(); DECLARE_MESSAGE_MAP() public: CString m_TagNumber; CString m_Make; CString m_Model; int m_Year; }; |
List Serialization |
Introduction |
One of the advantages of using the CObArray class to create and manage a list is that it has a built-in mechanism for serialization. This allows you to easily save a list to a medium and be able to open it when necessary. As mentioned earlier, to prepare a class for serialization, you must make sure you override its Serialize() method. When such a class is used as a CObArray item, it can directly benefit from CObArray's own serialization. To serialize a CObArray list in your project, all of you have to do is to call the CObArray::Serialize() method. Everything else would be handle behind the scenes for you. |
Practical Learning: Serializing a List |
void CCarRental1Dlg::Serialize(CArchive& ar) { ListOfCars.Serialize(ar); } |
Saving the List |
The actions we have taken so far with regards to serialization allow our class to know how to save the list and what to save. The next action is to tell the project when to save the list. This is traditionally done by using the common Windows Save dialog box. To make our exercise simple, we will let the user create and manage the list while the application is opened. After using the application, when the user decides to close it, then we will automatically save the list. |
Practical Learning: Saving a List |
void CCarRental1Dlg::OnBnClickedCancel() { // TODO: Add your control notification handler code here char strFilename[] = "Cars.bcr"; CFile fleCars; fleCars.Open(strFilename, CFile::modeCreate | CFile::modeWrite); CArchive arc(&fleCars, CArchive::store); Serialize(arc); arc.Close(); fleCars.Close(); OnCancel(); } |
Opening the List |
Opening a list consists of retrieving it from an archive and making it available to an application. As done when saving, it is up to you to decide when this action must occur. For our example, when the user opens the application, we will automatically open the file that holds the list, retrieve the list and get ready to use it. We will take care of this in the OnCreate() event of the dialog box of our application. |
Practical Learning: Opening a List |
int CCarRental1Dlg::OnCreate(LPCREATESTRUCT lpCreateStruct) { if (CDialog::OnCreate(lpCreateStruct) == -1) return -1; // TODO: Add your specialized creation code here char strFilename[] = "Cars.bcr"; CFile fleCars; CFileFind fndFile; // Look for the Cars.bcr file BOOL exists = fndFile.FindFile(strFilename); if( exists == TRUE ) { // If the file exists, open it and fill the controls with it fleCars.Open(strFilename, CFile::modeRead); CArchive arc(&fleCars, CArchive::load); Serialize(arc); arc.Close(); fleCars.Close(); } return 0; } |
Introduction to List Management |
List Emptiness |
When you start a list, before adding any item in it, you would know that it is empty. If someone else had created it, you would not have this information. In some cases, before performing ann operation on a list, you will need to know whether it contains any item at all. This information can be given to you by calling the CObArray::IsEmpty() method. Its syntax is: BOOL IsEmpty() const; This method allows you to find out if the list already contains at least one member. |
Practical Learning: Checking List Emptiness |
void CCarRental1Dlg::OnBnClickedFirst() { // TODO: Add your control notification handler code here if( this->ListOfCars.IsEmpty() == TRUE ) return; } |
void CCarRental1Dlg::OnBnClickedPrevious() { // TODO: Add your control notification handler code here if( this->ListOfCars.IsEmpty() == TRUE ) return; } |
void CCarRental1Dlg::OnBnClickedNext() { // TODO: Add your control notification handler code here if( this->ListOfCars.IsEmpty() == TRUE ) return; } |
void CCarRental1Dlg::OnBnClickedLast() { // TODO: Add your control notification handler code here if( this->ListOfCars.IsEmpty() == TRUE ) return; } |
The Index of an Item in the List |
As we will see in the next sections of this exercise, there are various operations you can perform on a list, such as adding or inserting a new item. When an item is part of the list, that item occupies a specific position so you can locate it easily. The members of the CObArray list are stored at positions represented by the INT_PTR data type. |
List Creation |
Item Addition to the List |
Probably the first action to take with regards to a list consists of populating it, since an empty list may not be very useful. After creating an item, to add it to a CObArray list, you can call the Add() method. Its syntax is: INT_PTR Add(CObject* newElement); When calling this method, pass the item as argument. If this methods succeeds, which it usually does, it adds the new item to the list. If the list was empty, it would be started with this item. If the list already contained at least one item, the new item would be added to the end of the list. |
Practical Learning: Starting a List-Based Application |
void CCarRental1Dlg::OnBnClickedNewCar() { // TODO: Add your control notification handler code here UpdateData(); CString strCaption; this->m_BtnNewCar.GetWindowText(strCaption); if( strCaption == "New Car" ) { this->m_TagNumber = ""; this->m_Make = ""; this->m_Model = ""; this->m_Year = 2000; this->m_BtnNewCar.SetWindowText("Add"); this->m_edtTagNumber.SetFocus(); } else { CCar *car = new CCar(this->m_TagNumber, m_Make, m_Model, m_Year); this->ListOfCars.Add(car); this->m_BtnNewCar.SetWindowText("New Car"); this->OnBnClickedLastBtn(); } UpdateData(FALSE); } |
Tax Number | Make | Model | Year |
525708 | Dodge | Neon SE | 2004 |
DKO-739 | Ford | Focus SVT | 2002 |
M682468 | Ford | Expedition | 2004 |
DJK822 | Daewoo | Lanos | 2000 |
415-048 | Chevrolet | Monte Carlo LS | 2005 |
Item Inserting Within the List |
If a list already contains at least one item, when you call the CObArray::Add() method, the new item would added to the end of the list. In some cases, you can want to insert the new item somewhere within the list. To support this operation, the CObArray class provides the InsertAt() method which is overloaded with two versions. Their syntaxes are: void InsertAt(INT_PTR nIndex, CObject* newElement, INT_PTR nCount = 1); void InsertAt(INT_PTR nStartIndex, CObArray* pNewArray); The first version allows you to add a new item, newElement, at position nIndex. If you want o insert more than one sample of the same item, specify the number as the nCount argument. The second argument allows you to insert another CObArray list to this one, at position nStartIndex. |
Item Location |
Introduction |
After creating the list, you can start managing the items. Before taking an action on a member of the list, you may first need to locate it or event to find out if it exists. We previously mentioned that the index of each member is represented by an INT_PTR value. To allow you to scan the list and locate each item by its index using the same concept of the C++' arrays, the [] operator was overloaded in two versions that use the following syntaxes: CObject*& operator [](INT_PTR nIndex); CObject* operator [](INT_PTR nIndex) const; This operator allows you to locate an item by specifying its index between the square brackets of the CObArray variable. When a list is not empty, the index of the last item can be gotten by calling the CObArray::GetUpperBound() method. Its syntax is: INT_PTR GetUpperBound() const; This method returns the index of the last member of the array. |
Practical Learning: Locating an Item by its Index |
int CCarRental1Dlg::OnCreate(LPCREATESTRUCT lpCreateStruct) { if (CDialog::OnCreate(lpCreateStruct) == -1) return -1; // TODO: Add your specialized creation code here char strFilename[] = "Cars.bcr"; CFile fleCars; CFileFind fndFile; // Look for the Cars.bcr file BOOL exists = fndFile.FindFile(strFilename); if( exists == TRUE ) { // If the file exists, open it and fill the controls with it fleCars.Open(strFilename, CFile::modeRead); CArchive arc(&fleCars, CArchive::load); Serialize(arc); arc.Close(); fleCars.Close(); CCar *car = reinterpret_cast<CCar *>(this->ListOfCars[0]); this->m_TagNumber = car->getTagNumber(); this->m_Make = car->getMake(); this->m_Model = car->getModel(); this->m_Year = car->getYear(); } return 0; } |
// Implementation protected: HICON m_hIcon; CObArray ListOfCars; INT_PTR CurrentPosition; |
BOOL CCarRental1Dlg::OnInitDialog() { CDialog::OnInitDialog(); // Set the icon for this dialog. The framework does this automatically // when the application's main window is not a dialog SetIcon(m_hIcon, TRUE); // Set big icon SetIcon(m_hIcon, FALSE); // Set small icon // TODO: Add extra initialization here this->CurrentPosition = 0; return TRUE; // return TRUE unless you set the focus to a control } |
Accessing the Members of an Array |
To store all the members of the list in one pointer, you can call the CObArray::GetData() method that is overloaded with two versions whose syntaxes are: const CObject** GetData( ) const; CObject** GetData( ); This method can help you access all members of the list as one. |
The Size and Capacity of a List |
The Number of Items in the List |
To know the number of items in the list, you can call the CObArray::GetCount() method: INT_PTR GetCount() const; Since the list is zero-based, the actual number of items is the total - 1. Besides GetCount(), you can call the CArray::GetSize() method to know the size of the list. The syntax of the GetSize() method is: INT_PTR GetSize() const; This method returns the size of the list. Like GetCount(), the GetSize() method returns the total number of items - 1. |
Practical Learning: Accessing the Members of an Array |
void CCarRental1Dlg::OnBnClickedFirst() { // TODO: Add your control notification handler code here if( this->ListOfCars.IsEmpty() == TRUE ) return; CurrentPosition = 0; CCar *car = reinterpret_cast<CCar *>(this->ListOfCars[CurrentPosition]); this->m_TagNumber = car->getTagNumber(); this->m_Make = car->getMake(); this->m_Model = car->getModel(); this->m_Year = car->getYear(); UpdateData(FALSE); } |
void CCarRental1Dlg::OnBnClickedPrevious() { // TODO: Add your control notification handler code here if( this->ListOfCars.IsEmpty() == TRUE ) return; if( CurrentPosition != 0 ) CurrentPosition--; CCar *car = reinterpret_cast<CCar *>(this->ListOfCars[CurrentPosition]); this->m_TagNumber = car->getTagNumber(); this->m_Make = car->getMake(); this->m_Model = car->getModel(); this->m_Year = car->getYear(); UpdateData(FALSE); } |
void CCarRental1Dlg::OnBnClickedNext() { // TODO: Add your control notification handler code here if( this->ListOfCars.IsEmpty() == TRUE ) return; if( CurrentPosition != (this->ListOfCars.GetSize() - 1) ) CurrentPosition++; CCar *car = reinterpret_cast<CCar *>(this->ListOfCars[CurrentPosition]); this->m_TagNumber = car->getTagNumber(); this->m_Make = car->getMake(); this->m_Model = car->getModel(); this->m_Year = car->getYear(); UpdateData(FALSE); } |
void CCarRental1Dlg::OnBnClickedLast() { // TODO: Add your control notification handler code here if( this->ListOfCars.IsEmpty() == TRUE ) return; CurrentPosition = this->ListOfCars.GetSize() - 1; CCar *car = reinterpret_cast<CCar *>(this->ListOfCars[CurrentPosition]); this->m_TagNumber = car->getTagNumber(); this->m_Make = car->getMake(); this->m_Model = car->getModel(); this->m_Year = car->getYear(); UpdateData(FALSE); } |
The Extra Memory Unused by Items |
When a new item is being added to the list, the compiler increases the amount of memory allocated to the list. Some times, the amount increased may be more than was necessary. If you want to intervene in releasing any extra memory that is not used, you can call the CObArray::FreeExtra() method. Its syntax is: void FreeExtra(); This method can be used to release unused memory that was allocated when the list of items was increased. |
Deleting Items From a List |
Deleting Items |
If your CObArray list contains an item you don't need anymore, you can delete it. To delete an item from the list, you can call the CObArray: void RemoveAt(INT_PTR nIndex, INT_PTR nCount = 1); The first to this method is the index of the item you want to delete from the list. The value of this argument must be between the bounds of the list. If you pass only one argument, only the item at that position would be deleted. If you want to delete more than one argument, pass the desired number as the second argument. To delete all items from a CObArray list, call its RemoveAll() method. Its syntax is: void RemoveAll( ); When called, this method will clear the list of all members. |
Practical Learning: Deleting a Member of an Array |
void CDepartmentStore1Dlg::OnBnClickedDeleteBtn() { // TODO: Add your control notification handler code here if( CurrentPosition >= 0 ) { this->StoreItems.RemoveAt(CurrentPosition); this->OnBnClickedLastBtn(); } } |
Item Replacement |
Instead of simply deleting an item, you may want to replace it with another. This operation is supported by the CObArray::SetAt() method. Its syntax is: void SetAt(INT_PTR nIndex, CObject* newElement); The first argument of this method is the index that the new item will occupy in the list. |
|
||
Home | Copyright © 2005-2016, FunctionX | |
|