Click here to Skip to main content
15,879,474 members
Articles / Desktop Programming / XAML
Tip/Trick

Preserve the Selected Item of a WPF List Box

Rate me:
Please Sign up or sign in to vote.
5.00/5 (3 votes)
1 Aug 2014Ms-PL2 min read 46.4K   5   4   9
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)


Written By
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.

Comments and Discussions

 
QuestionSupport for Move events Pin
Johannes Unterguggenberger3-Sep-17 22:33
Johannes Unterguggenberger3-Sep-17 22:33 
QuestionHow to use? Pin
ashish.bathla16-Feb-16 20:15
ashish.bathla16-Feb-16 20:15 
AnswerRe: How to use? Pin
Johannes Unterguggenberger3-Sep-17 22:30
Johannes Unterguggenberger3-Sep-17 22:30 
GeneralMy vote of 5 Pin
Volynsky Alex8-Aug-14 21:55
professionalVolynsky Alex8-Aug-14 21:55 
GeneralHappens all too often Pin
Kris McGinnes4-Aug-14 16:16
Kris McGinnes4-Aug-14 16:16 
QuestionIsn’t there a separation of concerns issue here? Pin
George Swan3-Aug-14 11:21
mveGeorge Swan3-Aug-14 11:21 
AnswerRe: Isn’t there a separation of concerns issue here? Pin
MichaelLPerry3-Aug-14 14:15
MichaelLPerry3-Aug-14 14:15 
GeneralRe: Isn’t there a separation of concerns issue here? Pin
George Swan3-Aug-14 20:47
mveGeorge Swan3-Aug-14 20:47 
GeneralMy vote of 5 Pin
Volynsky Alex2-Aug-14 5:05
professionalVolynsky Alex2-Aug-14 5:05 

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

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