Deleting a Multiple Selection of Rows in a Listview Control



Click here for a larger image.

Environment: VC6

Optional Reading

With this article, I don't want to give you just nice code to copy and paste for your application's source code. I think that you do not learn very much from "sweet snippets" and many coders, including me, do sometimes only paste the code as mentioned above to get their application with the desired feature running. You can still just copy and paste the code out of this article, but I'll do my best to explain why I chose several ways of programming and show you some errors I come up with during my development of this function to help you to learn something.

Introduction

This article explains how it is possible to delete more than one selected row of a listview control in report mode. It is not as easy as it seems, but if you have some experience with programming for Windows and MFC, you should understand my source code and my explanation very quickly. At first, we will look at some simple basics—deleting one selected row.

void CMyListView::DeleteSelectedRow()
{
  int nIndex = GetListCtrl().GetSelectionMark();

  if(nIndex != -1)
  {
    GetListCtrl().DeleteItem(nIndex);
  }
}

As you can see, we are using a member function of the CListCtrl class to obtain the index of the selected row. After that, we check whether the result is not -1 because this would mean that no item is selected. If the result is zero or higher than zero, we delete the row with the member function DeleteItem of the CListCtrl class. This works fine for one-row selections, but if we select more than one item, the only row that's going to be deleted is the row we selected first.

First Attempt

Here is the our new function DeleteSelectedRows().

void CMyListView::DeleteSelectedRows()
{
  int nSelRows = GetListCtrl().GetSelectedCount();

  if(!nSelRows)
    return(0);

  POSITION pos = GetListCtrl().GetFirstSelectedItemPosition();

  int i = GetListCtrl().GetNextSelectedItem(pos);

  while (i != -1)
  {
    GetListCtrl().DeleteItem(i);
    i = GetListCtrl().GetNextSelectedItem(pos);
  }

  return(1);
}

In our new function, we first get the number of selected rows. If the result equals zero, we jump out of the code. But, if it's higher than zero, we go on with the code and retrieve the position of the first selected row. After that, we execute the loop as long as GetNextSelectedItem doesn't return -1, which means that there are no rows available that are selected, too. If you are using this code, you will notice that it does not work correctly because of the fact that the row indices in the listview control are changing if one or more items are deleted. As a result, not all rows or different rows than we have selected are going to be deleted. Take a short break and think about why this function does not work correctly... or just read on.

Here are some pictures that explain what happens if you are using the currently created function.

Image 1: The Listview control with several rows selected.

Image 2: This picture shows what happens if we delete the first row in our DeleteSelectedRows() method.

As you can see from Image 2, all the row indices are changing if the first row in the listview control is deleted. Due to this behaviour, the selected row indices are no longer valid. In this example, we want to delete row 1 after row 0, but the index of this row changed from 1 to 0, so row 2 will be deleted instead. During thinking about a solution for this problem, I came up with the idea to delete the rows with the highest index first. If we are deleting the rows with the highest indices first, the indices of the indices of other rows won't get changed.

Second Attempt

Here is our new function DeleteSelectedRowsFixed()

int CMultipleRowDeleteView::DeleteSelectedRowsFixed(void)
{
  int nSelRows = GetListCtrl().GetSelectedCount();

  if(!nSelRows)
    return(0);

  int* pnArrayOfSelRows = new int[nSelRows];

  if(!pnArrayOfSelRows)
    return(0);

  int nTemp = nSelRows;

  POSITION pos = GetListCtrl().GetFirstSelectedItemPosition();

  int i = GetListCtrl().GetNextSelectedItem(pos);

  while (i != -1)
  {
    nTemp = nTemp-1; 
    pnArrayOfSelRows[nTemp] = i;
    i = GetListCtrl().GetNextSelectedItem(pos);
  }


  for(int j=0; j < nSelRows; j++)
  {
    GetListCtrl().DeleteItem(pnArrayOfSelRows[j]);
  }

  delete(pnArrayOfSelRows);
  pnArrayOfSelRows = NULL;

  return(1);
}

We are using a dynamic allocated array to store the indices of the selected rows inverted. That means that the row indices will be stored from the highest index to the lowest index. Finally,we just have to loop through all row indices and delete them.

The End

This the end of the explanation how it's possible to delete more than one selected row at once. You can download the commented source code and the executable of a demo application that uses the routine at the bottom of the page. In the demo application, it's possible to delete rows by using the menu or by pressing the Delete key on the keyboard.

At the very end of my article, I want to thank the community of codeguru.com for providing a lot of cool coding stuff and codeguru.com for giving developers a platform to help other developers. I hope that some of you have learned something from my article.

I am looking forward to your comments, critiques, and questions about my article. If you want contact me, write a message to daniel-bartsch@arcor.de.

Downloads

Download demo executable - 8 Kb
Download demo sourcecode - 17 Kb


Comments

  • This is how I delete multiple selected items...

    Posted by Legacy on 06/09/2003 12:00am

    Originally posted by: Tom Kuzeja

    POSITION pos = m_cListCtrl.GetFirstSelectedItemPosition();
    
    while ( pos )
    {
    int nItem = m_cListCtrl.GetNextSelectedItem( pos );

    // Delete the item from the list
    m_cListCtrl.DeleteItem( nItem );

    // Start from the top again
    pos = m_cListCtrl.GetFirstSelectedItemPosition();
    }

    Reply
  • or try this

    Posted by Legacy on 03/05/2003 12:00am

    Originally posted by: Escher

    for (int i = GetListCtrl().GetItemCount(); i>=0;i--) 
    
    if ( GetListCtrl().GetItemState(i, TVIS_SELECTED ) & TVIS_SELECTED)
    GetListCtrl().DeleteItem(i);

    Reply
  • try this code

    Posted by Legacy on 03/04/2003 12:00am

    Originally posted by: nick

    void DeleteSelected(CListViewCtrl & lvc)
    
    {
    for (int i = 0;;)
    {
    i = ListView_GetNextItem(lvc, i - 1, LVNI_SELECTED);
    if (i < 0)
    break;
    ListView_DeleteItem(lvc, i);
    }
    }

    Reply
Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Although much publicity around computer security points to hackers and other outside attacks, insider threats can be particularly insidious and dangerous, whether caused by malice or employee negligence. In this report, you learn the eight most significant cybersecurity threats that could impact your organization (at any time), Forbes cited internal threats as No. 3, noting that internal attacks can be "the most devastating" due to the amount of damage privileged users can inflict and the type of data they can …

  • Genesys has been able to apply the best of all strategic approaches in moving to the cloud. It has acquired assets to bypass the long R&D process required to build from scratch, yet skillfully planned a phased integration to provide customers with functionality they need quickly. It also has followed an aggressive development plan to deliver multiple releases per quarter to continue to enrich its cloud offerings for customers. For these reasons, Frost & Sullivan is pleased to present Genesys with the 2014 …

Most Popular Programming Stories

More for Developers

RSS Feeds

Thanks for your registration, follow us on our social networks to keep up-to-date