How do you programmatically set focus to the SelectedItem in a WPF ListBox that already has focus?

listbox, selecteditem, wpf

Solution

It's a couple lines of code. If you didn't want it in code-behind, I sure it could be packaged in a attached behaviour.

private void Button_Click(object sender, RoutedEventArgs e)
{
    MainListBox.SelectedItem = MainListBox.Items[3];
    MainListBox.UpdateLayout(); // Pre-generates item containers 

    var listBoxItem = (ListBoxItem) MainListBox
        .ItemContainerGenerator
        .ContainerFromItem(MainListBox.SelectedItem);

    listBoxItem.Focus();
}

Problem

We want to set the `SelectedItem` of a `ListBox` programmatically and want that item to then have focus so the arrow keys work relative to that selected item. Seems simple enough. The problem however is if the `ListBox` already has keyboard focus when setting `SelectedItem` programmatically, while it does properly update the `IsSelected` property on the `ListBoxItem`, it doesn't set keyboard focus to it, and thus, the arrow keys move relative to the previously-focused item in the list and not the newly-selected item as one would expect. This is very confusing to the user as it makes the selection appear to jump around when using the keyboard as it snaps back to where it was before the programmatic selection took place. Note: As I said, this only happens if you programmatically set the `SelectedItem` property on a `ListBox` that already has keyboard focus itself. If it doesn't (or if it does but you leave, then come right back), when the keyboard focus returns to the `ListBox`, the correct item will now have the keyboard focus as expected. Here's some sample code showing this problem. To demo this, run the code, use the mouse to select 'Seven' in the list (thus putting the focus on the `ListBox`), then click the 'Test' button to programmatically select the fourth item. Finally, tap the 'Alt' key on your keyboard to reveal the focus rect. You will see it's still on 'Seven', not 'Four' as you may expect, and because of that, if you use the up and down arrows, they are relative row 'Seven', not 'Four' as well, further confusing the user since what they are visually seeing and what is actually focused are not in sync. Important: Note that I have `Focusable` set to `false` on the button. If I didn't, when you clicked it, it would gain focus and the `ListBox` would lose it, masking the issue because again, when a `ListBox` regains focus, it properly focuses the selected `ListBoxItem`. The issue is when a `ListBox` already has focus and you programmatically select a `ListBoxItem`. XAML file: ``` <Window x:Class="Test.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Width="525" Height="350" WindowStartupLocation="CenterScreen" Title="MainWindow" x:Name="Root"> <DockPanel> <Button Content="Test" DockPanel.Dock="Bottom" HorizontalAlignment="Left" Focusable="False" Click="Button_Click" /> <ListBox x:Name="MainListBox" /> </DockPanel> </Window> ``` Code-behind: ``` using System.Collections.ObjectModel; using System.Windows; namespace Test { public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); MainListBox.ItemsSource = new string[]{ "One", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight" }; } private void Button_Click(object sender, RoutedEventArgs e) { MainListBox.SelectedItem = MainListBox.Items[3]; } } } ``` Note: Some have suggested to use `IsSynchronizedWithCurrentItem`, but that property synchronizes the `SelectedItem` of the `ListBox` with the `Current` property of the associated view. It isn't related to focus as this problem still exists. Our work-around is to temporarily set the focus somewhere else, then set the selected item, then set the focus back to the `ListBox` but this has the undesireable effect of us having to make our `ViewModel` aware of the `ListBox` itself, then perform logic depending on whether or not it has the focus, etc. (i.e. you wouldn't want to simply say 'Focus elsewhere then come back here, if 'here' didn't have the focus already as you'd steal it from somewhere else.) Plus, you can't simply handle this through declarative bindings. Needless to say this is ugly. Then again, 'ugly' ships, so there's that.

Original source