ListViewItem.Remove() throws exception inside AfterLabelEdit

G

Guest

An interesting problem:
I have a ListView with LabelEdit set to TRUE. When I change the label, I
want to make some decisions as to whether the ListViewItem (that's just been
edited) should stay in the ListView or not.
The natural place to inspect this is in the AfterLabelEdit event...
If the decision is to take the item out, I used the ListViewItem.Remove().
However - check this out:
A) say that the item's index is N (indices are zero-based)
B) if there exists an item after it (i.e. with index N+1)
then this item gets quietly removed
C) if the item is the last in the list,an exception is thrown
saying that index N (requested to be removed) is non-existant.

It looks like:
D) when the item is being edited, it is taken OUT of the
ListView.Items collection. So - the original item N+1 moves up and
is temporarily assigned index N (that is how it gets removed - see
B) above)
E) if however, item N is the last in the collection and gets
temporarily taken out while being edited, then of course we get
and exception thrown as there isn't an item with index N

This can easily be proved:
1. create a new Windows Forms project
2. add a ListView to the form
3. Set LabelEdit to TRUE
4. Add 3 items to the list, with labels e.g. 'Item1', 'Item2' and 'Item3'
5. Create a handler for the AfterLabelEdit event
6. Inside this handler add this code:

ListViewItem item = listView1.Items[e.Item];
item.Remove();

7. Run the application
8. Edit the first item and press Enter -> the second item is removed
9. Next, edit the last item then press Enter -> exception is thrown

This is all very nice...

But - can someone suggest an elegant workaround , i.e. - where and
how can I remove items after they have been edited?

Many thanks in advance.
 
C

Claes Bergefall

Actually, what really happens is this:

1. You begin editing the first item (index 0), add characters and press
Enter
2. The AfterLabelEdit event fires for index 0. At this stage the change is
*not yet commited* to the actual item
3. You remove the item with index 0
4. Control returns to Windows that now tries to commit the change to index
0. Only problem is that you removed that item so it applies it to whatever
is at index 0 now, which happens to be your second item (i.e. the one that
previously had index 1). To you this looks like the second item is removed,
while in fact the first item is removed and the second item simply gets its
text changed

Imagine what happens when you edit the last item......
(btw, you can see that the correct item is being removed if you press Esc
instead of Enter)

To work around this use BeginInvoke to asynchronously invoke a method that
removes it, ensuring that it occurs after the event has completed.

private delegate void RemoveItemDelegate(ListViewItem item);
private void RemoveItem(ListViewItem item)
{
item.Remove();
}

private void listView1_AfterLabelEdit(object sender, LabelEditEventArgs e)
{
ListViewItem item = listView1.Items[e.Item];
listView1.BeginInvoke(new RemoveItemDelegate(RemoveItem), item);
}

/claes

Kela said:
An interesting problem:
I have a ListView with LabelEdit set to TRUE. When I change the label, I
want to make some decisions as to whether the ListViewItem (that's just
been
edited) should stay in the ListView or not.
The natural place to inspect this is in the AfterLabelEdit event...
If the decision is to take the item out, I used the ListViewItem.Remove().
However - check this out:
A) say that the item's index is N (indices are zero-based)
B) if there exists an item after it (i.e. with index N+1)
then this item gets quietly removed
C) if the item is the last in the list,an exception is thrown
saying that index N (requested to be removed) is non-existant.

It looks like:
D) when the item is being edited, it is taken OUT of the
ListView.Items collection. So - the original item N+1 moves up and
is temporarily assigned index N (that is how it gets removed - see
B) above)
E) if however, item N is the last in the collection and gets
temporarily taken out while being edited, then of course we get
and exception thrown as there isn't an item with index N

This can easily be proved:
1. create a new Windows Forms project
2. add a ListView to the form
3. Set LabelEdit to TRUE
4. Add 3 items to the list, with labels e.g. 'Item1', 'Item2' and 'Item3'
5. Create a handler for the AfterLabelEdit event
6. Inside this handler add this code:

ListViewItem item = listView1.Items[e.Item];
item.Remove();

7. Run the application
8. Edit the first item and press Enter -> the second item is removed
9. Next, edit the last item then press Enter -> exception is thrown

This is all very nice...

But - can someone suggest an elegant workaround , i.e. - where and
how can I remove items after they have been edited?

Many thanks in advance.
 
G

Guest

Claes,
Many thanks for this. It is indeed an elegant solution.
Best Regards
Kela

Claes Bergefall said:
Actually, what really happens is this:

1. You begin editing the first item (index 0), add characters and press
Enter
2. The AfterLabelEdit event fires for index 0. At this stage the change is
*not yet commited* to the actual item
3. You remove the item with index 0
4. Control returns to Windows that now tries to commit the change to index
0. Only problem is that you removed that item so it applies it to whatever
is at index 0 now, which happens to be your second item (i.e. the one that
previously had index 1). To you this looks like the second item is removed,
while in fact the first item is removed and the second item simply gets its
text changed

Imagine what happens when you edit the last item......
(btw, you can see that the correct item is being removed if you press Esc
instead of Enter)

To work around this use BeginInvoke to asynchronously invoke a method that
removes it, ensuring that it occurs after the event has completed.

private delegate void RemoveItemDelegate(ListViewItem item);
private void RemoveItem(ListViewItem item)
{
item.Remove();
}

private void listView1_AfterLabelEdit(object sender, LabelEditEventArgs e)
{
ListViewItem item = listView1.Items[e.Item];
listView1.BeginInvoke(new RemoveItemDelegate(RemoveItem), item);
}

/claes

Kela said:
An interesting problem:
I have a ListView with LabelEdit set to TRUE. When I change the label, I
want to make some decisions as to whether the ListViewItem (that's just
been
edited) should stay in the ListView or not.
The natural place to inspect this is in the AfterLabelEdit event...
If the decision is to take the item out, I used the ListViewItem.Remove().
However - check this out:
A) say that the item's index is N (indices are zero-based)
B) if there exists an item after it (i.e. with index N+1)
then this item gets quietly removed
C) if the item is the last in the list,an exception is thrown
saying that index N (requested to be removed) is non-existant.

It looks like:
D) when the item is being edited, it is taken OUT of the
ListView.Items collection. So - the original item N+1 moves up and
is temporarily assigned index N (that is how it gets removed - see
B) above)
E) if however, item N is the last in the collection and gets
temporarily taken out while being edited, then of course we get
and exception thrown as there isn't an item with index N

This can easily be proved:
1. create a new Windows Forms project
2. add a ListView to the form
3. Set LabelEdit to TRUE
4. Add 3 items to the list, with labels e.g. 'Item1', 'Item2' and 'Item3'
5. Create a handler for the AfterLabelEdit event
6. Inside this handler add this code:

ListViewItem item = listView1.Items[e.Item];
item.Remove();

7. Run the application
8. Edit the first item and press Enter -> the second item is removed
9. Next, edit the last item then press Enter -> exception is thrown

This is all very nice...

But - can someone suggest an elegant workaround , i.e. - where and
how can I remove items after they have been edited?

Many thanks in advance.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top