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

  • Hybrid cloud platforms need to think in terms of sweet spots when it comes to application platform interface (API) integration. Cloud Velocity has taken a unique approach to tight integration with the API sweet spot; enough to support the agility of physical and virtual apps, including multi-tier environments and databases, while reducing capital and operating costs. Read this case study to learn how a global-level Fortune 1000 company was able to deploy an entire 6+ TB Oracle eCommerce stack in Amazon Web …

  • Protecting business operations means shifting the priorities around availability from disaster recovery to business continuity. Enterprises are shifting their focus from recovery from a disaster to preventing the disaster in the first place. With this change in mindset, disaster recovery is no longer the first line of defense; the organizations with a smarter business continuity practice are less impacted when disasters strike. This SmartSelect will provide insight to help guide your enterprise toward better …

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds