Deleting a Multiple Selection of Rows in a Listview Control

CodeGuru content and product recommendations are editorially independent. We may make money when you click on links to our partners. Learn More.



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 [email protected].

Downloads

Download demo executable – 8 Kb


Download demo sourcecode – 17 Kb

More by Author

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Must Read