Click here to Skip to main content
Click here to Skip to main content

Tagged as

Go to top

ListCtrl Operations

, 8 May 2012
Rate this:
Please Sign up or sign in to vote.
Add, Update, Delete operations on the ListCtrl.

Introduction

Hello Guys. This is my first ever article on CodeProject (or anywhere else) so I hope you don’t mind anything unusual.

This is a beginner’s article for adding, updating and deleting items froma ListCtrl in MFC Dialog based application.

Solution

First thing first, we make a standard MFC Dialog based application and make the GUI as you desire. Then we add the following variables to the controls.

  • Right click StudentId editbox and click Add Variable. In Category: select Value and name variable as: m_sStudentId. Type is CString.
  • Right click Name editbox and click Add Variable. In Category: select Value and name variable as: m_sName. Type is CString.
  • Right click ListCtrl and click Add Variable. In Category: select Control and name the variable as: m_ListOperations.
  • Go to the properties of ListCtrl and make sure that View property is set to: Report
  • Finally you can add three event handlers to the three buttons. Name them as you desire. I have OnAddItem(), OnUpdateItem(), OnDeleteItem().

Now starting with the solution, it has four main functions namely: AddColumns_ToList(), AddItem(), UpdateItem() and DeleteItem().

Their names describe what they are meant to do. Here is the function body of AddColumns_ToList(). It is called in OnInitDialog().

void CListCtrl_CommonActionsDlg::AddColumns_ToList()
{
        m_ListOperations.SetExtendedStyle(LVS_EX_GRIDLINES);

        LVCOLUMN col;
        int nCol;

        col.mask = LVCF_FMT | LVCF_TEXT | LVCF_WIDTH;
        col.fmt = LVCFMT_LEFT;
        col.cx = 70;
        col.pszText = _T("ID");
        nCol = m_ListOperations.InsertColumn(0, &col);

        col.mask = LVCF_FMT | LVCF_TEXT | LVCF_WIDTH;
        col.fmt = LVCFMT_LEFT;
        col.cx = 170;
        col.pszText = _T("Name");
        nCol = m_ListOperations.InsertColumn(1, &col);
}

Now here is the body for adding the items to the list. Remember that you are taking values from two editboxes.

void CListCtrl_CommonActionsDlg::AddItem()
{
          UpdateData();

          LVITEM item;
          int nItem;

          item.mask = LVIF_TEXT;
          item.iSubItem = 0;

          //If 'm_listListOperations.GetItemCount() <= 0' evaluates to TRUE then 0, 
          //else  value returned by 'm_listListOperations.GetItemCount()'

          item.iItem = (m_ListOperations.GetItemCount() <= 0) ? 0 : m_ListOperations.GetItemCount(); 
          item.pszText = m_sStudentId.GetBuffer(1024);    //insert the StudentId from editbox to item

          nItem = m_ListOperations.InsertItem(&item);

          m_ListOperations.SetItemText(nItem, 1, m_sName);          //second column of the ListCtrl
          GetDlgItem(IDC_STUDENTID)->SetWindowTextW(_T(""));
          GetDlgItem(IDC_NAME)->SetWindowTextW(_T(""));
          GetDlgItem(IDC_STUDENTID)->SetFocus();
}

And now we update the record. For this, we use a structure named LVFINDINFO. We use the ListCtrl’s FindItem() method to find the item using the Id we get from the editbox. Using this methos, if the item is found then its information will be stored in the LVFINDINFO structure and it then returns an index of the found item. Here is how it is done.

void CListCtrl_CommonActionsDlg::UpdateItem()
{
          UpdateData();
 
          LVITEM item;
          int nItem;
          CString str;
 
          GetDlgItem(IDC_STUDENTID)->GetWindowTextW(str);
 
          if (str != "")
          {
                   LVFINDINFO info;
                   info.flags = LVFI_PARTIAL|LVFI_STRING;
                   info.psz = str.GetBuffer(1024);
                   CString arr[3];
                   arr[0] = str;
                   arr[1] = m_sName;
 
                   int index = m_ListOperations.FindItem(&info);
                   if(index >= 0)
                   {
                             CHeaderCtrl* column = (CHeaderCtrl*)m_ListOperations.GetDlgItem(0);          
                   //OR:     CHeaderCtrl* column = m_listListOperations.GetHeaderCtrl();

                             int cols = column->GetItemCount();

                             item.mask = LVIF_TEXT;
                             item.iItem = index; 
                             item.pszText = m_sStudentId.GetBuffer(1024);
                             int nItem = index;
 
                             for (int i=0; i<cols; i++) //loop through the columns
                             {
                                if(i==0)
                                  continue;     //continue: because we do not want to update the first column
                                else
                                  m_ListOperations.SetItemText(nItem, i, arr[i]); //update the value
                             }
                             GetDlgItem(IDC_STUDENTID)->SetWindowTextW(_T(""));
                             GetDlgItem(IDC_NAME)->SetWindowTextW(_T(""));
                             GetDlgItem(IDC_STUDENTID)->SetFocus();
                   }
                   else
                   {
                             MessageBox(_T("No Item Found !!!"));
                             GetDlgItem(IDC_STUDENTID)->SetFocus();
                   }
          } 
          else
          {
                   MessageBox(_T("No Number Entered !!!"));
                   GetDlgItem(IDC_STUDENTID)->SetFocus();
          }
}

And now we delete the record. This is almost same as the previous function, the functionality being a different one. We have to find the item first and once found, we can delete it. Here is it

void CListCtrl_CommonActionsDlg::DeleteItem()
{
          CString str;
          GetDlgItem(IDC_STUDENTID)->GetWindowTextW(str);
          if (str != "")
          {
                   LVFINDINFO info;
                   info.flags = LVFI_PARTIAL|LVFI_STRING;
                   info.psz = str.GetBuffer(1024);
 
                   int index = m_ListOperations.FindItem(&info);

                   if(index >= 0)
                   {
                             m_ListOperations.DeleteItem(index);
                             GetDlgItem(IDC_STUDENTID)->SetWindowTextW(_T(""));
                             GetDlgItem(IDC_STUDENTID)->SetFocus();
                   }
                   else
                   {
                             MessageBox(_T("No Item Found !!!"));
                             GetDlgItem(IDC_STUDENTID)->SetFocus();
                   }
          } 
          else
          {
                   MessageBox(_T("No Number Entered !!!"));
                   GetDlgItem(IDC_STUDENTID)->SetFocus();
          }
}

So this is how we add, update and delete items from a ListCtrl. Simple, isn’t it?

Points to Remember

  1. There could have been a lot of checks before doing these operations. For example: checking whether an ID already exists before we can add it to ListCtrl. But since the motive was to keep this sample as simple as possible, I think other such actions should not be a problem
  2. Please be aware that I do not know any drawbacks that this solution has. It just does the job for me perfectly. If there are any drawbacks, please share so that others may know whether to use this or not.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Django_Untaken

Pakistan Pakistan
No Biography provided

Comments and Discussions

 
QuestionDubious loop PinmemberMarius Bancila14-Jan-14 10:05 
GeneralHelped PinmemberMember 999500214-May-13 9:29 
SuggestionComments/Improvements PinmemberMike Appleby14-May-12 23:22 
Overall, clean code but not sure on what you're trying to demonstrate (as code is specific to your application) and this is pretty much boiler-plate functionality that is provided by the CListCtrl object.
 
::AddItem()
You don't check if the student id already exists?
 
::UpdateItem()
You can only update an item if the student id is set. What happens if you don't know the id and just the name?
 
Also searching for a string match is slower that matching a number, user the lParam member of the LVITEM and search against that.
 
::DeleteItem
You can only delete via the student id, what if I want to delete what's selected? what if I want to delete more than one item?
 

When you're using structures, I always prefer to have them initialized, i.e. you can do:
 
LVCOLUMN col = {0}; // Sets all members to be 0
::AddColumns_ToList()
No need to set structure members to what they already are, i.e. col.fmt = LVCFMT_LEFT only needs to be set once.
 
Also I normally create an enum for columns so as not to hard code column positions:
 
enum {
 E_COL_STUDENT_ID,
 E_COL_NAME,
 E_TOTAL_COLUMNS  // Must be last item
};
 
So when adding items to the listview, I just add a single item then populate the cells. Slightly in-efficient that the first cell is in-effect populated twice (once when adding the item), but worthy compromise for clarity/maintenance.
 
If I need to add more columns or re-organize them I just modify the enum to suit and no searching through code updating various numbers.
 
I have a utility class with static methods that perform common functions for various controls, and for a listview control, I have:
 
InitColumns( ...)
AddColumn( ...)
EnableSorting( ...)
Sort( ...)
AutoFitColumns( ...)
AutoFitColumnsProportionally( ...)
AutoLabelTips( ...)
ShowGridLines( ...)
SetFullRowSelect( ...)
 
Some of the methods are very simple and are only a few lines, but makes the code a bit more readable and hopefully more maintainable.
 

If this was a learning exercise for yourself, what you could do is to improve it, and write a part 2 to show the differences/improvements. Improvement is not just about efficiency it's also about readability and maintainability of code.
 
Mike.
GeneralMy vote of 1 Pinmembersupergyc13-May-12 1:36 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web01 | 2.8.140916.1 | Last Updated 8 May 2012
Article Copyright 2012 by Django_Untaken
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid