MFC Collections: A List of Strings

 

Fundamentals of the CStringList Class

 

Introduction

The main class used to support strings in MFC is called CString. To support the ability to create, manage, and possibly save a list of strings, the MFC library provides a class called CStringList. CStringList is one of the MFC collection classes that don't the C++ concept of template to manage their items. Therefore, as its name indicates, this class is purposely made for strings.

Here is the dialog box we will use in this exercise:

Country ID Caption
Static Text   New Country
Edit Control IDC_NEWCOUNTRY  
Button IDC_ADD_COUNTRY  
List Box IDC_COUNTRIES_LIST  

Here are the member variables created and associated with each control:

ID Value Variable Control Variable
IDC_NEWCOUNTRY m_NewCountry m_ctlNewCountry
IDC_COUNTRIES_LIST   m_CountriesList
 

Starting a List of Strings

Although CString is an MFC class, it is build to easily integrate with C++ primitive data types, C/C++ strings, and even the STL's string class. Based on this, you can use the CStringList class to create a list of any built-in data type but if you are not planning to use CString objects directly, you may need to do some (but easy) conversions.

To start your list, you can first declare a CStringList variable. If you plan to create, use, and manage the list locally, you can declare the variable in a method or an event of your choice. If you want to access the variable from different methods of a project, you can declare the variable in the header file of the class that will use it. Here is an example:

// CountriesDlg.h : header file
//

#if !defined(AFX_COUNTRIESDLG_H__1A64B572_46D7_4B36_AB9F_131F5ECC1782__INCLUDED_)
#define AFX_COUNTRIESDLG_H__1A64B572_46D7_4B36_AB9F_131F5ECC1782__INCLUDED_

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000

/////////////////////////////////////////////////////////////////////////////
// CCountriesDlg dialog

class CCountriesDlg : public CDialog
{
// Construction
public:
	CCountriesDlg(CWnd* pParent = NULL);	// standard constructor

// Dialog Data
	//{{AFX_DATA(CCountriesDlg)
	enum { IDD = IDD_COUNTRIES_DIALOG };
		// NOTE: the ClassWizard will add data members here
	//}}AFX_DATA

	// ClassWizard generated virtual function overrides
	//{{AFX_VIRTUAL(CCountriesDlg)
	protected:
	virtual void DoDataExchange(CDataExchange* pDX);	// DDX/DDV support
	//}}AFX_VIRTUAL

// Implementation
protected:
	HICON m_hIcon;

	// Generated message map functions
	//{{AFX_MSG(CCountriesDlg)
	virtual BOOL OnInitDialog();
	afx_msg void OnPaint();
	afx_msg HCURSOR OnQueryDragIcon();
	//}}AFX_MSG
	DECLARE_MESSAGE_MAP()

private:
	CStringList ListOfCountries;
};

//{{AFX_INSERT_LOCATION}}
// Microsoft Visual C++ will insert additional 
declarations immediately before the previous line.

#endif
 // !defined(AFX_COUNTRIESDLG_H__1A64B572_46D7_4B36_AB9F_131F5ECC1782__INCLUDED_)

In reality, the constructor of the CStringList class takes one argument but the argument has a default value. The syntax of this constructor is:

CObList(INT_PTR nBlockSize = 10);

The nBlockSize argument represents the amount of memory to allocate to the list, when necessary. Most of the time, you can let the compiler take care of allocating and de-allocating memory for the list at it judges it necessary.

Introduction to List Maintenance

 

The Position of an Item in the List

In the next few sections, we will review various techniques of adding or deleting items from the list, which are some of the most regular maintenance operations performed on a list.

At any time, to know whether the list is empty or contains at least one item, you can call the CStringList::IsEmpty() method. Its syntax is:

BOOL IsEmpty() const;

If the list doesn't contain any item at all, this method returns TRUE. If the list contains at least one item, this method returns FALSE. Here is an example of calling it:

BOOL CCountriesDlg::OnInitDialog()
{
	CDialog::OnInitDialog();

	SetIcon(m_hIcon, TRUE);			// Set big icon
	SetIcon(m_hIcon, FALSE);		// Set small icon
	
	// TODO: Add extra initialization here
	if( this->ListOfCountries.IsEmpty() == TRUE )
		this->SetWindowText("The list is still empty");

	return TRUE;  // return TRUE  unless you set the focus to a control
}
 
 

The Position of an Item in the List

Probably the primary action you take on a list is to create one, as we will learn in the next few sections. An object or an item that belongs to a list is also referred to as a member of the list.

Once a list exists, each member occupies a position with regard to the other members. The position of an item in a CStringList list is represented by the POSITION, which is a data type Microsoft defined explicitly for list-based classes.

 

The Head Item of the List

As mentioned above, each string of a CStringList list occupies a specific position. This makes it possible to locate a particular item in the list or to look for an item.

After the strings have been created in the list, to get the position of the first string, you can call the CStringList::GetHeadPosition() method. Its syntax is:

POSITION GetHeadPosition( ) const;

This method simply returns the position of the first item in the list.

 

The Tail Item of the List

To get the position of the last item in the list, you can call the CStringList::GetTailPosition() method. Its syntax is:

POSITION GetTailPosition( ) const;

This method returns the position of the last item in the list.

 

List Creation

 

The Head of the List

A list based on the CStringList class behaves like a linked list: it allows you to add a new item at the beginning or at the end of the list. To add a new item at the beginning of the list, the class is equipped with the AddHead() method that is overloaded with three versions whose syntaxes are:

POSITION AddHead(const CString& newElement);
POSITION AddHead(LPCTSTR newElement);
void AddHead(CStringList* pNewList);

The first and the second versions of this method expect a string as argument. This string would then be added to the list. Here is an example:

void CCountriesList1Dlg::OnBnClickedAddCountry()
{
	// TODO: Add your control notification handler code here
	UpdateData();

	CString strNewCountry = this->m_NewCountry;
	this->ListOfCountries.AddHead(strNewCountry);

	this->m_CountriesList.AddString(strNewCountry);
	m_ctlNewCountry.SetWindowText("");
	m_ctlNewCountry.SetFocus();
}

The last version of this method allows you to add one CStringList list to another. Here is an example:

BOOL CCountriesDlg::OnInitDialog()
{
	CDialog::OnInitDialog();

	SetIcon(m_hIcon, TRUE);			// Set big icon
	SetIcon(m_hIcon, FALSE);		// Set small icon
	
	// TODO: Add extra initialization here
	
	CStringList lstCountries;

	lstCountries.AddHead("Ghana");
	lstCountries.AddHead("Sri Lanka");
	lstCountries.AddHead("Albania");
	lstCountries.AddHead("Holland");

	this->ListOfCountries.AddHead(&lstCountries);

	return TRUE;  // return TRUE  unless you set the focus to a control
}

When you call the CStringList::AddHead() method, if the list is empty, it (the list) would be created with the new item(s). If the list already contains at list one item, the new item(s) would be added before the existing one(s).

 

The Tail of the List

As a linked-list type, the CStringList class allows you to add the new item(s) after the existing one(s). It supports this through the AddTail() method, which is overloaded with three versions:

POSITION AddTail( const CString& newElement );
POSITION AddTail( LPCTSTR newElement );
void AddTail( CStringList* pNewList );

This method follows the same rules as the AddHead() method except that the item or the group of items is added to the end of the list if the list already contained at least one item.

 

Item Insertion

 

Item Insertion Ahead of an Existing Member

As discussed above, the AddHead() and  the AddTail() methods are used to add a new string or a group of strings at one end of the list. If you want to add a new string ahead of an existing string, you can call the CStringList::InsertBefore() method. It is overloaded with two versions whose syntaxes are:

POSITION InsertBefore(POSITION position, const CString& newElement);
POSITION InsertBefore(POSITION position, LPCTSTR newElement);

When calling this method, pass the position of an existing item as the position argument and pass the string of the new item as the newElement argument. If the position is valid, the new string would be added before the existing one that is located at position.

Here is an example of calling it:

void CCountriesDlg::OnAddCountry() 
{
	// TODO: Add your control notification handler code here

	ListOfCountries.AddHead("Ghana");
	ListOfCountries.AddHead("Sri Lanka");
	POSITION pos = ListOfCountries.AddHead("Albania");
	ListOfCountries.AddHead("Holland");

	this->ListOfCountries.InsertBefore(pos, "Venezuela");
}
 

Item Insertion After of an Existing Member

Instead of inserting a new string ahead of another string, you may want to insert the new string after an existing string. This operation can be performed using the CStringList::InsertAfter() method. This method is overloaded with two versions whose syntax are:

POSITION InsertAfter(POSITION position, const CString& newElement);
POSITION InsertAfter(POSITION position, LPCTSTR newElement);

This method follows the same logic as the InsertBefore() method except that the new string would be added before the string at position.

 

Adding a New Item at a Specific Position

Instead of adding a new string to the beginning or the end of the list, you may want to insert it at a specific position. One of the methods you can call to do this is CStringList::SetAt(). Its syntax is:

void SetAt(POSITION pos, LPCTSTR newElement);

When calling this method, specify the desired position with the pos argument and specify the new string with the newElement argument. If the position is valid, the item at that position would be deleted and replaced by the newElement. Here is an example of calling this method:

void CCountriesDlg::OnAddCountry() 
{
	// TODO: Add your control notification handler code here

	ListOfCountries.AddHead("Ghana");
	ListOfCountries.AddHead("Sri Lanka");
	POSITION pos = ListOfCountries.AddHead("Albania");
	ListOfCountries.AddHead("Holland");

	this->ListOfCountries.SetAt(pos, "Honduras");
}
 

Item Location Within the List

 

The Head Item of the List

Probably the primary technique you can use to locate an item is by its position in the list. The MFC library makes this very convenient in its various list-based classes. To get a reference to the first item in the list, you can call the CStringList::GetHead() method which is overloaded in two versions whose syntaxes are:

const CString& GetHead( ) const;
CString& GetHead( );

When you call this method, it returns the item at the head of the list. Here is an example:

 
void CCountriesDlg::OnAddCountry() 
{
	// TODO: Add your control notification handler code here
	UpdateData();

	CStringList lstCountries;

	lstCountries.AddHead("Ghana");
	lstCountries.AddHead("Sri Lanka");
	lstCountries.AddHead("Albania");
	lstCountries.AddHead("Holland");

	this->ListOfCountries.AddHead(&lstCountries);

	POSITION pos = this->ListOfCountries.GetHeadPosition();

	for(int j = 0; j < 4; j++)
	{
		CString strCountry = this->ListOfCountries.GetNext(pos);
		this->m_CountriesList.AddString(strCountry);
	}
}

void CCountriesDlg::OnLocateBtn() 
{
	// TODO: Add your control notification handler code here
	CString strCountry = this->ListOfCountries.GetHead();

	this->m_LocateCountry = strCountry;
	UpdateData(FALSE);
}
 

The Tail Item of the List

To know what item is the last in the list, you can call the CStringList::GetTail() method which is overloaded in two versions whose syntaxes are:

const CString& GetTail( ) const;
CString& GetTail( );

This method returns last string of the list.

 

The List Items by their Position

Instead of getting the first or the last item in the list, you may want to locate a string somewhere within the list. Various methods allows you to do this. To locate an item whose position you know already, you can call the CStringList::GetAt() method which is overloaded with two versions. Their syntaxes are:

const CString& GetAt( POSITION position ) const;
CString& GetAt( POSITION position );

When called, this method returns the string located at the position.

 

The Next Item in the List

If you have already obtained the position of an item in the list, you can also easily locate the item that follows it. To get this information, you can call the CStringList::GetNext() method that is overloaded with two versions. Their syntaxes are:

void*& GetNext(POSITION& rPosition);
const void*& GetNext(POSITION& rPosition) const;

This method excepts as argument the position of an item used as reference. If an item is found at that position, the method returns the item that follows it. Here is an example:

void CCountriesDlg::OnAddCountry() 
{
	// TODO: Add your control notification handler code here
	UpdateData();

	CStringList lstCountries;

	CString strCountries[] = { "Ghana", "Sri Lanka", "Albania", "Holland" };
	for(int i = 0; i < 4; i++)
		lstCountries.AddHead(strCountries[i]);

	this->ListOfCountries.AddHead(&lstCountries);

	POSITION pos = this->ListOfCountries.GetHeadPosition();

	for(int j = 0; j < 4; j++)
	{
		CString strCountry = this->ListOfCountries.GetNext(pos);
		this->m_CountriesList.AddString(strCountry);
	}
}
 

Finding an Item in the List

To be able to find a string in the list, you can call the CStringList::Find() method. Its syntax is:

POSITION Find(LPCTSTR searchValue, POSITION startAfter = NULL ) const;

To look for a string value in the list, you can pass it as the searchValue argument to this method. Here is an example:

void CCountriesDlg::OnLocateBtn() 
{
	// TODO: Add your control notification handler code here
	UpdateData();
	CString strCountry = this->m_LocateCountry;
	POSITION pos = this->ListOfCountries.Find(strCountry);

	if( pos != NULL )
	{
		int strPos = this->m_CountriesList.FindString(0, strCountry);
		this->m_CountriesList.SetCurSel(strPos);
	}

	UpdateData(FALSE);
}
 

Deleting Items From the List

 

Removing an Item in the List

If the list contains an item you don't need anymore, you can delete it. There are various methods you can use.

To delete the top item in the list, you can call the CStringList::RemoveHead() method. Its syntax is:

CString RemoveHead();

To delete the last item in the list, you can call the CStringList::RemoveTail() method. Its syntax is:

CString RemoveTail();

To remove an item at a specific position, you can call the CStringList::RemoveAt(). Its syntax is:

void RemoveAt(POSITION position);

When calling this method, make sure you pass the position of an existing item.

 

Clearing the List

To remove all items from the list, you can call the CStringList::RemoveAll() method. Its syntax is:

void RemoveAll();
 
 

The Size and Capacity of a List

 

The Number of Items in the List

As the items are added to, or removed from, the list, the size of the list grows or shrinks. To get the number of items in the list at one particular time, you can call the CStringList::GetCount() method. Its syntax is:

INT_PTR GetCount() const;

You can also get the same information by calling the CStringList::GetSize() method. Its syntax is:

INT_PTR GetSize() const;

In both cases, the method returns an integer.

 

Home Copyright 2005 FunctionX, Inc.