Home

Visual C++/CLI Collections: Dictionary-Based Collection Classes

 

Adding Items to the Collection

To add an item to the list, you can call the Add() method. The syntax of the System::Collections::Hashtable::Add() and the System::Collections::SortedList::Add() methods is:

public:
    virtual void Add(Object^ key, Object^ value);

The syntax of the System::Collections::Generic::Dictionary::Add(), the System::Collections::Generic::SortedDictionary::Add(), and the System::Collections::Generic::SortedList::Add() method is:

public:
    virtual void Add(TKey key, TValue value) sealed;

As you can see, you must provide the key and the value as the first and second arguments to the method respectively. Here are examples of calling the Add() method:

void CExercise::Start(Object ^ sender, EventArgs ^ e)
{
    Hashtable ^ Students = gcnew Hashtable;

    Students->Add(L"Hermine", "Tolston");
    Students->Add(L"Patrick", "Donley");
}

When calling the Add() method, you must provide a valid Key argument: it cannot be nullptr. For example, the following code would produce an error:

void CExercise::Start(Object ^ sender, EventArgs ^ e)
{
    Hashtable ^ Students = gcnew Hashtable;

    Students->Add(L"Hermine", "Tolston");
    Students->Add(L"Patrick", "Donley");
    Students->Add(nullptr, L"Hannovers");
}

This would produce:

Error

When adding the items to the list, as mentioned in our introduction, each key must be unique: you cannot have two exact keys. If you try adding a key that exists already in the list, the compiler would throw an ArgumentException exception. Based on this, the following code would not work because, on the third call, a "Patrick" key exists already:

void CExercise::Start(Object ^ sender, EventArgs ^ e)
{
    Hashtable ^ Students = gcnew Hashtable;

    Students->Add(L"Hermine", L"Tolston");
    Students->Add(L"Patrick", L"Donley");
    Students->Add(L"Chrissie", L"Hannovers");
    Students->Add(L"Patrick", L"Herzog");
}

This would produce:

Error

This means that, when creating a dictionary type of list, you must define a scheme that would make sure that each key is unique among the other keys in the list.

Besides the Add() method, you can use the indexed property to add an item to the collection. To do this, enter the Key in the square brackets of the property and assign it the desired Value. Here is an example:

void CExercise::Start(Object ^ sender, EventArgs ^ e)
{
    Hashtable ^ Students = gcnew Hashtable;

    Students->Add(L"Hermine", "Tolston");
    Students->Add(L"Patrick", "Donley");
    Students->Add(L"Chrissie", "Hannovers");
    Students->Add(L"Patricia", "Herzog");
    Students[L"Michael"] = L"Herlander";
}

Practical LearningPractical Learning: Adding Items to the Collection

  1. Display the CarEditor form
  2. On the CarEditor form, double-click the Submit button and implement its Click event as follows:
     
    System::Void btnSubmit_Click(System::Object^  sender, System::EventArgs^  e)
    {
        CCar ^ vehicle = gcnew CCar;
        FileStream ^ stmCars = nullptr;
        BinaryFormatter ^ bfmCars = gcnew BinaryFormatter;
        Directory::CreateDirectory(L"C:\\Bethesda Car Rental");
        String ^ strFilename = L"C:\\Bethesda Car Rental\\Cars.crs";
    
        if (txtTagNumber->Text->Length == 0)
        {
            MessageBox::Show(L"You must enter the car's tag number");
            return;
        }
    
        if (txtMake->Text->Length == 0)
        {
            MessageBox::Show(L"You must specify the car's manufacturer");
            return;
        }
    
        if (txtModel->Text->Length == 0)
        {
            MessageBox::Show(L"You must enter the model of the car");
            return;
        }
    
        if (txtYear->Text->Length == 0)
        {
            MessageBox::Show(L"You must enter the year of the car");
            return;
        }
    
        // Create a car
        vehicle->Make = txtMake->Text;
        vehicle->Model = txtModel->Text;
        vehicle->Year = int::Parse(txtYear->Text);
        vehicle->Category = cbxCategories->Text;
        vehicle->HasCDPlayer = chkCDPlayer->Checked;
        vehicle->HasDVDPlayer = chkDVDPlayer->Checked;
        vehicle->IsAvailable = chkAvailable->Checked;
    
        // Call the Add method of our collection class to add the car
        lstCars->Add(txtTagNumber->Text, vehicle);
    
        // Save the list
        stmCars = gcnew FileStream(strFilename,
                                 FileMode::Create,
                                 FileAccess::Write,
                                 FileShare::Write);
    
        try
        {
            bfmCars->Serialize(stmCars, lstCars);
    
            if (lblPictureName->Text->Length != 0)
            {
                FileInfo ^ flePicture = gcnew FileInfo(lblPictureName->Text);
                flePicture->CopyTo(L"C:\\Bethesda Car Rental\\" +
                                  txtTagNumber->Text +
                                  flePicture->Extension);
            }
    
            txtTagNumber->Text = L"";
            txtMake->Text = L"";
            txtModel->Text = L"";
            txtYear->Text = L"";
            cbxCategories->Text = L"Economy";
            chkCDPlayer->Checked = false;
            chkDVDPlayer->Checked = false;
            chkAvailable->Checked = false;
            lblPictureName->Text = L".";
            pbxCar->Image = nullptr;
        }
        finally
        {
            stmCars->Close();
        }
    }
  3. In the Solution Explorer, right-click the Customers form and click View Code
  4. Change the file as follows:
     
    #pragma once
    
    #include "Customer.h"
    #include "CustomerEditor.h"
    
    using namespace System;
    using namespace System::ComponentModel;
    using namespace System::Collections;
    using namespace System::Collections::Generic;
    using namespace System::Windows::Forms;
    using namespace System::Data;
    using namespace System::Drawing;
    using namespace System::IO;
    using namespace System::Runtime::Serialization::Formatters::Binary;
    
    namespace BethesdaCarRental1 {
    
    	/// <summary>
    	/// Summary for Customers
    	///
    	/// WARNING: If you change the name of this class, you will need to change the
    	///          'Resource File Name' property for the managed resource compiler tool
    	///          associated with all .resx files this class depends on.  Otherwise,
    	///          the designers will not be able to interact properly with localized
    	///          resources associated with this form.
    	/// </summary>
    	public ref class Customers : public System::Windows::Forms::Form
    	{
    	private:
            Dictionary<String ^, CCustomer ^> ^ lstCustomers;
    
    	public:
    		Customers(void)
    		{
    			InitializeComponent();
    			//
    			//TODO: Add the constructor code here
    			//
    		}
    
            void ShowCustomers()
            {
            }
  5. Display the Customers form and double-click an unoccupied area of its body
  6. Change the file as follows:
     
    System::Void Customers_Load(System::Object^  sender, System::EventArgs^  e)
    {
        lstCustomers = gcnew Dictionary<String ^, CCustomer ^>;
        BinaryFormatter ^ bfmCustomers = gcnew BinaryFormatter;
    
        // This is the file that holds the list of parts
        String ^ strFilename = L"C:\\Bethesda Car Rental\\Customers.crc";
    
        if( File::Exists(strFilename))
        {
            FileStream ^ stmCustomers = gcnew FileStream(strFilename,
                                                         FileMode::Open,
                                                         FileAccess::Read,
                                                         FileShare::Read);
    
            try {
                // Retrieve the list of employees from file
                lstCustomers =
                    dynamic_cast<Dictionary<String ^, CCustomer ^> ^>
    		    (bfmCustomers->Deserialize(stmCustomers));
            }
            finally
            {
                stmCustomers->Close();
            }
        }
    
        ShowCustomers();
    }
  7. Return to the Customers form and double-click the New Customer button
  8. Implement its event as follows:
     
    System::Void btnNewCustomer_Click(System::Object^  sender, System::EventArgs^  e)
    {
        CustomerEditor ^ editor = gcnew CustomerEditor;
        Directory::CreateDirectory(L"C:\\Bethesda Car Rental");
    
        if (editor->ShowDialog() == System::Windows::Forms::DialogResult::OK)
        {
            if (editor->txtDrvLicNbr->Text == L"")
            {
                MessageBox::Show(L"You must provide the driver's " 
                                 L"license number of the customer");
                return;
            }
    
            if (editor->txtFullName->Text == L"")
            {
                MessageBox::Show(L"You must provide the employee's full name");
                return;
            }
    
            String ^ strFilename = L"C:\\Bethesda Car Rental\\Customers.crc";
    
            CCustomer ^ cust = gcnew CCustomer;
    
            cust->FullName = editor->txtFullName->Text;
            cust->Address = editor->txtAddress->Text;
            cust->City = editor->txtCity->Text;
            cust->State = editor->cbxStates->Text;
            cust->ZIPCode = editor->txtZIPCode->Text;
            lstCustomers->Add(editor->txtDrvLicNbr->Text, cust);
    
            FileStream ^ bcrStream = gcnew FileStream(strFilename,
                                                      FileMode::Create,
                                                      FileAccess::Write,
                                                      FileShare::Write);
            BinaryFormatter ^ bcrBinary = gcnew BinaryFormatter;
            bcrBinary->Serialize(bcrStream, lstCustomers);
            bcrStream->Close();
    
            ShowCustomers();
        }
    }
  9. In the Solution Explorer, right-click the Employees and click View Code
  10. Change the file as follows:
     
    #pragma once
    
    #include "Employee.h"
    #include "EmployeeEditor.h"
    
    using namespace System;
    using namespace System::ComponentModel;
    using namespace System::Collections;
    using namespace System::Collections::Generic;
    using namespace System::Windows::Forms;
    using namespace System::Data;
    using namespace System::Drawing;
    using namespace System::IO;
    using namespace System::Runtime::Serialization::Formatters::Binary;
    
    namespace BethesdaCarRental1 {
    
        /// <summary>
        /// Summary for Employees
        ///
        /// WARNING: If you change the name of this class, you will need to change the
        ///       'Resource File Name' property for the managed resource compiler tool
        ///        associated with all .resx files this class depends on.  Otherwise,
        ///        the designers will not be able to interact properly with localized
        ///        resources associated with this form.
        /// </summary>
        public ref class Employees : public System::Windows::Forms::Form
        {
        private:
            SortedDictionary<String ^, CEmployee ^> ^ lstEmployees;
    
        public:
    	Employees(void)
    	{
    		InitializeComponent();
    		//
    		//TODO: Add the constructor code here
    		//
    	}
    
    	void ShowEmployees()
            {
            }
  11. Display the Employees form and double-click an unoccupied area of its body
  12. Change the file as follows:
     
    System::Void Employees_Load(System::Object^  sender, System::EventArgs^  e)
    {
        lstEmployees = gcnew SortedDictionary<String ^, CEmployee ^>;
        BinaryFormatter ^ bfmEmployees = gcnew BinaryFormatter;
    
        // This is the file that holds the list of parts
        String ^ strFilename = L"C:\\Bethesda Car Rental\\Employees.cre";
    
        if( File::Exists(strFilename) )
        {
            FileStream ^ stmEmployees = gcnew FileStream(strFilename,
                                                         FileMode::Open,
                                                         FileAccess::Read,
                                                         FileShare::Read);
    
            try {
                // Retrieve the list of employees from file
                lstEmployees =
    		dynamic_cast<SortedDictionary<String ^, CEmployee ^> ^>
    		    (bfmEmployees->Deserialize(stmEmployees));
            }
            finally
            {
                stmEmployees->Close();
            }
        }
    
        ShowEmployees();
    }
  13. Return to the Employees form and double-click the New Employee button
  14. Implement its event as follows:
     
    System::Void btnNewEmployee_Click(System::Object^  sender, System::EventArgs^  e)
    {
        EmployeeEditor ^ editor = gcnew EmployeeEditor;
        Directory::CreateDirectory("C:\\Bethesda Car Rental");
    
        if (editor->ShowDialog() == System::Windows::Forms::DialogResult::OK)
        {
            if (editor->txtEmployeeNumber->Text == L"")
            {
                MessageBox::Show(L"You must provide an employee number");
                return;
            }
    
            if (editor->txtLastName->Text == L"")
            {
                MessageBox::Show(L"You must provide the employee's last name");
                return;
            }
    
            String ^ strFilename = "C:\\Bethesda Car Rental\\Employees.cre";
    
            CEmployee ^ empl = gcnew CEmployee;
    
            empl->FirstName = editor->txtFirstName->Text;
            empl->LastName = editor->txtLastName->Text;
            empl->Title = editor->txtTitle->Text;
            empl->HourlySalary = double::Parse(editor->txtHourlySalary->Text);
            lstEmployees->Add(editor->txtEmployeeNumber->Text, empl);
    
            FileStream ^ bcrStream = gcnew FileStream(strFilename,
                                                  FileMode::Create,
                                                  FileAccess::Write,
                                                  FileShare::Write);
            BinaryFormatter ^ bcrBinary = gcnew BinaryFormatter;
            bcrBinary->Serialize(bcrStream, lstEmployees);
            bcrStream->Close();
    
            ShowEmployees();
        }
    }
  15. Save all

 

 

 

 

Accessing the Items of a Dictionary-Type of List

Although, or because, the key and value are distinct, to consider their combination as a single object, if you are using either the System::Collections::Hasthtable or the System::Collections::SortedList class, the .NET Framework provides the DictionaryEntry structure. To access an item, you can use the for each loop to visit each item.

To support the for each loop, the System::Collections::Hashtable and the System::Collections::SortedList classes implement the IEnumerable::GetEnumerator() method. In this case, the item is of type DictionaryEntry: it contains a Key and a Value in combination.

The DictionaryEntry structure contains two properties named Key and Value to identify the components of a combination. Here is an example:

#pragma once

#include <windows.h>

#using <System.dll>
#using <System.Drawing.dll>
#using <System.Windows.Forms.dll>

using namespace System;
using namespace System::Drawing;
using namespace System::Collections;
using namespace System::Windows::Forms;

public ref class CExercise : public Form
{
private:
    ListBox ^ lbxStudents;
    void InitializeComponent();
    void Start(Object ^ sender, EventArgs ^ e);

public:
    CExercise();
};

CExercise::CExercise()
{
    InitializeComponent();
}

void CExercise::InitializeComponent()
{
    Text = L"Students";
    Size = Drawing::Size(150, 145);
    StartPosition = FormStartPosition::CenterScreen;
    Load += gcnew EventHandler(this, &CExercise::Start);
	
    lbxStudents = gcnew ListBox;
    lbxStudents->Location = Point(12, 12);
    Controls->Add(lbxStudents);
}

void CExercise::Start(Object ^ sender, EventArgs ^ e)
{
    Hashtable ^ Students = gcnew Hashtable;

    Students->Add(L"Hermine", "Tolston");
    Students->Add(L"Patrick", "Donley");
    Students->Add(L"Chrissie", "Hannovers");
    Students->Add(L"Patricia", "Herzog");
    Students[L"Michael"] = L"Herlander";

    for each(DictionaryEntry ^ entry in Students)
        lbxStudents->Items->Add(entry->Key + L" " + entry->Value);
}

int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
					 LPSTR lpCmdLine, int nCmdShow)
{
    Application::Run(gcnew CExercise());
    return 0;
}

This would produce:

Hash Table

If you are using a generic class, the .NET Framework provides the KeyValuePair structure that follows the same functionality as the System::Collections::DictionaryEntry structure, except that you must apply the rules of generic classes.

Practical LearningPractical Learning: Enumerating the Members of a Collection

  1. Display the Order Processing form
  2. On the form, click the Driver's Lic # text box
  3. In the Properties window, click the Events button and, in the Events section, double-click Leave
  4. Implement the event as follows:
     
    System::Void txtDrvLicNumber_Leave(System::Object^  sender, 
    				   System::EventArgs^  e)
    {
        CCustomer ^ renter = nullptr;
        String ^ strDrvLicNumber = txtDrvLicNumber->Text;
    
        if (strDrvLicNumber->Length == 0)
        {
            MessageBox::Show(L"You must enter the renter's " 
                             L"driver's license number");
            txtDrvLicNumber->Focus();
            return;
        }
    
        Dictionary<String ^, CCustomer ^> ^ lstCustomers = 
    	gcnew Dictionary<String ^, CCustomer ^>;
        BinaryFormatter ^ bfmCustomers = gcnew BinaryFormatter;
    
    	String ^ strFilename = L"C:\\Bethesda Car Rental\\Customers.crc";
    
        if( File::Exists(strFilename))
        {
             FileStream ^ stmCustomers = gcnew FileStream(strFilename,
                                                      FileMode::Open,
                                                      FileAccess::Read,
                                                      FileShare::Read);
    
            try
            {
                // Retrieve the list of customers
                lstCustomers = 
    		dynamic_cast<Dictionary<String ^, CCustomer ^> ^>
    		    (bfmCustomers->Deserialize(stmCustomers));
    
    			if (lstCustomers->ContainsKey(strDrvLicNumber) == true)
                {
               for each(KeyValuePair<String ^, CCustomer ^> ^ cust in lstCustomers)
                    {
    		    if (cust->Key == strDrvLicNumber)
                        {
                            renter = cust->Value;
                            txtCustomerName->Text = renter->FullName;
                            txtCustomerAddress->Text = renter->Address;
                            txtCustomerCity->Text = renter->City;
                            cbxCustomerStates->Text = renter->State;
                            txtCustomerZIPCode->Text = renter->ZIPCode;
                        }
    		}
                }
                else
                {
                    txtCustomerName->Text = L"";
                    txtCustomerAddress->Text = L"";
                    txtCustomerCity->Text = L"";
                    cbxCustomerStates->Text = L"";
                    txtCustomerZIPCode->Text = L"";
                    MessageBox::Show(L"There is no customer with that driver's " 
                                     L"license number in our database");
                    return;
                }
            }
            finally
            {
                stmCustomers->Close();
            }
        }
    }
  5. In the Class View, expand the BethesdaCarRental1 folder and sub-folder, then click Employees
  6. In the lower section of the Class View, double-click ShowEmployees and implement it as follows:
     
    void ShowEmployees()
    {
        if( lstEmployees->Count == 0)
            return;
    
        // Before displaying the employees, empty the list view
        lvwEmployees->Items->Clear();
        // This variable will allow us to identify the odd and even indexes
        int i = 1;
    
        // Use the KeyValuePair class to visit each key/value item
        for each(KeyValuePair<String ^, CEmployee ^> ^ kvp in lstEmployees )
        {
            ListViewItem ^ lviEmployee = gcnew ListViewItem(kvp->Key);
    
            CEmployee ^ empl = kvp->Value;
    
            lviEmployee->SubItems->Add(empl->FirstName);
            lviEmployee->SubItems->Add(empl->LastName);
            lviEmployee->SubItems->Add(empl->FullName);
            lviEmployee->SubItems->Add(empl->Title);
            lviEmployee->SubItems->Add(empl->HourlySalary.ToString(L"F"));
    
            if( i % 2 == 0 )
            {
    		lviEmployee->BackColor = Color::FromArgb(255, 128, 0);
    		lviEmployee->ForeColor = Color::White;
            }
            else
            {
    		lviEmployee->BackColor = Color::FromArgb(128, 64, 64);
    		lviEmployee->ForeColor = Color::White;
            }
    
            lvwEmployees->Items->Add(lviEmployee);
    
            i++;
        }
    }
  7. In the Class View, click Customers
  8. In the lower section of the Class View, double-click ShowCustomers and implement it as follows:
     
    void ShowCustomers()
    {
        if (lstCustomers->Count == 0)
            return;
    
        lvwCustomers->Items->Clear();
        int i = 1;
    
        for each( KeyValuePair<String ^, CCustomer ^> ^ kvp in lstCustomers)
        {
            ListViewItem ^ lviCustomer = gcnew ListViewItem(kvp->Key);
    
            CCustomer ^ cust = kvp->Value;
    
            lviCustomer->SubItems->Add(cust->FullName);
            lviCustomer->SubItems->Add(cust->Address);
            lviCustomer->SubItems->Add(cust->City);
            lviCustomer->SubItems->Add(cust->State);
            lviCustomer->SubItems->Add(cust->ZIPCode);
    
            if (i % 2 == 0)
            {
                lviCustomer->BackColor = Color::Navy;
                lviCustomer->ForeColor = Color::White;
            }
            else
            {
                lviCustomer->BackColor = Color::Blue;
                lviCustomer->ForeColor = Color::White;
            }
    
            lvwCustomers->Items->Add(lviCustomer);
    
            i++;
        }
    }
  9. Execute the application
  10. Click the Cars button and create the cars
     
    Bethesda Car Rental - Car Editor
  11. Close the Car Editor form
  12. Click the Customers button then click the New Customer button continually and create a few customers as follows:
     
    Driver's Lic. # State Full Name Address City ZIP Code
    M-505-862-575 MD Lynda Melman 4277 Jamison Avenue Silver Spring 20904
    379-82-7397 DC John Villard 108 Hacken Rd NE Washington 20012
    J-938-928-274 MD Chris Young 8522 Aulage Street Rockville 20852
    497-22-0614 PA Pamela Ulmreck 12075 Famina Rd Blain 17006
    922-71-8395 VA Helene Kapsco 806 Hyena Drive Alexandria 22231
    C-374-830-422 MD Hermine Crasson 6255 Old Georgia Ave Silver Spring 20910
    836-55-2279 NY Alan Pastore 4228 Talion Street Amherst 14228
    397-59-7487 TN Phillis Buster 724 Cranston Circle Knoxville 37919
    115-80-2957 FL Elmus Krazucki 808 Rasters Ave Orlando 32810
    294-90-7744 VA Helena Weniack 10448 Great Pollard Hwy Arlington 22232
  13. Close the Customers form
 
 
   
 

Previous Copyright © 2009-2010 FunctionX, Inc. Next