Click here to Skip to main content
Click here to Skip to main content
Go to top

Preserve the Selected Item of a WPF List Box

, 1 Aug 2014
Rate this:
Please Sign up or sign in to vote.
A behavior to attach to a ListBox to keep WPF from clearing the selected item

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.

The problem

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.

The events

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.

The fix

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.

Install-Package Itzben

Or download the source code from CodePlex.

License

This article, along with any associated source code and files, is licensed under The Microsoft Public License (Ms-PL)

Share

About the Author

MichaelLPerry
Software Developer (Senior) Improving Enterprises
United States United States
Software is math.
 
Michael L Perry has built upon the works of mathematicians like Bertrand Meyer, James Rumbaugh, and Donald Knuth to develop a mathematical system for software development. He has captured this system in a set of open source projects, Update Controls and Correspondence. As a Principal Consultant at Improving Enterprises, he applies mathematical concepts to building scalable and robust enterprise systems.
 
You can find out more at qedcode.com.
Follow on   Twitter

Comments and Discussions

 
GeneralMy vote of 5 PinprofessionalVolynsky Alex8-Aug-14 21:55 
GeneralHappens all too often PinmemberDrakon0004-Aug-14 16:16 
QuestionIsn’t there a separation of concerns issue here? PinmemberGeorge Swan3-Aug-14 11:21 
AnswerRe: Isn’t there a separation of concerns issue here? PinmemberMichaelLPerry3-Aug-14 14:15 
GeneralRe: Isn’t there a separation of concerns issue here? PinmemberGeorge Swan3-Aug-14 20:47 
GeneralMy vote of 5 PinprofessionalVolynsky Alex2-Aug-14 5:05 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web01 | 2.8.140916.1 | Last Updated 1 Aug 2014
Article Copyright 2014 by MichaelLPerry
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid