When you filter or sort the items in a ListBox, WPF will sometimes set the SelectedItem property to null. I’ll show you a behavior that you can attach to your ListBox to preserve that selection.
You’ve data bound the items source of a ListBox to an observable collection. Maybe you’ve also data bound the SelectedItem property to your view model. Then at some point you need to update that collection to sort or filter that collection.
It could be simply to move an item up or down in the collection. you would do this by calling
Move(oldIndex, newIndex), or by a
RemoveItem followed by a
InsertItem. Or it could be to apply a completely different sort order.
As you sort the collection, you will remove items from one place and insert them into another. But WPF jumps the gun. It will immediately react when you remove the item. If the item you just removed happens to be the selected item, it will set the
SelectedItem property of the ListBox to null.
Even if you prevent the null from coming into your view model, the ListBox will no longer display the item as selected.
If you subscribe to
SelectionChanged events on the ListBox, and
CollectionChanged events on the observable collection, you can observe the following sequence of events:
First, you will receive the
SelectionChanged event from the ListBox, indicating one item in the
RemovedItems collection. This is WPF responding to the second event, which you are just about to receive.
Second, the observable collection fires
CollectionChanged, with the action being Remove. There will be one item in the OldItems collection. WPF received this event first, and deselected the item. Now it’s your turn to see this event.
Third, the observable collection fires
CollectoinChanged, with the action being Add. There will be one item in the NewItems collection. What you want to do is set the
SelectedItem on the ListBox, fixing the mistake that WPF made in the first place.
To fix this problem, you need to keep track of the above events. Since you get the
CollectionChanged event after WPF does, the
SelectedItem has already been set to null. So you have to know what item used to be selected to see if you are in this situation.
The following code handles the
SelectionChanged event by storing the
_lastSelection. Then it sees the
CollectionChanged event with the
Remove action, and finds that the removed item is the one that was just selected. It moves this forward to the
_lastRemoved. Finally, when it sees the
CollectionChanged event with the
Add action, and determines that the item that was just removed has been re-added, it sets the
SelectedItem back again.
private void ListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
if (e.RemovedItems != null && (e.AddedItems == null || e.AddedItems.Count == 0))
_lastSelection = e.RemovedItems.OfType<object>().FirstOrDefault();
private void ItemsSource_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
if (e.Action == NotifyCollectionChangedAction.Remove)
var lostItem = e.OldItems.OfType<object>().FirstOrDefault();
if (lostItem != null && Object.Equals(_lastSelection, lostItem))
_lastRemoved = _lastSelection;
_lastSelection = null;
else if (e.Action == NotifyCollectionChangedAction.Add)
var newItem = e.NewItems.OfType<object>().FirstOrDefault();
if (newItem != null && Object.Equals(_lastRemoved, newItem))
AssociatedObject.SelectedItem = newItem;
_lastRemoved = null;
I’ve packaged the entire thing in a behavior so you can just attach it to a ListBox to fix this issue. Get it from NuGet.
Or download the source code from CodePlex.