2

How can I get the SelectedItem of a ListBox to be highlighted after setting the SelectedItem in the ViewModel?

The ItemsSource is bound to an ObservableCollection of Bar (the collection is a member of a class Foo. A button is bound to a command that adds a new empty Bar instance to the collection and then also sets SelectedItem to the new empty instance.

After adding the instance to the collection, the ListBox is updated to show the new blank Bar. However, after setting the SelectedItem property in the ViewModel, the new instance is not highlighted in the ListBox but it is being set and the PropertyChanged event is raised (the SelectedItem is displayed elsewhere in the View).

Additional Details:

INotifyPropertyChanged is implemented in a base ViewModel class, and also implemented in the Foo and Bar classes.

The ListBox contains a custom ItemTemplate to display Bar members, and a custom ItemContainerStyle that modifies the Background for the IsMouseOver trigger.

simplified xaml:

<ListBox ItemsSource="{Binding Path=MyFoo.BarCollection}"
         SelectedItem="{Binding Path=SelectedItem, 
                       UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"/>

<Button Content="Add New Bar"
        Command="{Binding Path=AddBarCommand}"/>

simplified viewmodel:

private Foo _myFoo;
public Foo MyFoo
{
    get { return _myFoo; }
    set { _myFoo= value; OnPropertyChanged("MyFoo"); }
}

private Bar _selectedItem;
public Bar SelectedItem
{
    get { return _selectedItem; }
    set { _selectedItem = value; OnPropertyChanged("SelectedItem"); }
}

private void AddBar()
{
    Bar newBar = new Bar();

    MyFoo.BarCollection.Add(newBar);
    SelectedItem = newBar ;
    _unsavedChanges = true;
}
jonmicjam
  • 23
  • 4
  • What is the type of `BarCollection`? – 15ee8f99-57ff-4f92-890c-b56153 May 31 '17 at 18:44
  • @Ed `BarCollection` is an `ObservableCollection` – jonmicjam May 31 '17 at 19:02
  • I figured it had to be. With that assumption, your code worked fine for me. Is it possible that the new item is highlighted in a really pale gray color that's scarcely distinquishable from the window around it? The default non-focused highlight background color in modern versions of windows can be perversely hard to notice. If that's the case, it's easily fixable. – 15ee8f99-57ff-4f92-890c-b56153 May 31 '17 at 19:04
  • @Ed Ughh... Yes! Changed the ListBox background to white, and the newly added item is indeed highlighted LightGray. Guess I'll override the SystemColor brush? Thanks! – jonmicjam May 31 '17 at 19:08

1 Answers1

0

Your code worked perfectly for me.

enter image description here

Notice that "Bar 3" is selected, but when the listbox doesn't have focus, the default theme on Windows 7 works pretty hard to keep you from noticing. And Windows 7 isn't even the worst of the bunch. Our application uses WhiteSmoke as a default background, and we've had major issues with older users1 being unable to tell if listbox items are selected or not.

Fortunately, it's a trivial fix. These resources could just as easily be in Window.Resources, in a global ListBox style, or in App.xaml. It's up to you how widely you want to apply them.

<ListBox 
    ItemsSource="{Binding MyFoo.BarCollection}"
    SelectedItem="{Binding SelectedItem}"
    >
    <ListBox.Resources>
        <SolidColorBrush 
            x:Key="{x:Static SystemColors.InactiveSelectionHighlightBrushKey}" 
            Color="{x:Static SystemColors.HighlightColor}"
            Opacity="0.5"
            />
        <SolidColorBrush 
            x:Key="{x:Static SystemColors.InactiveSelectionHighlightTextBrushKey}" 
            Color="{x:Static SystemColors.HighlightTextColor}"
            />
    </ListBox.Resources>
</ListBox>

1 "Older" meaning old enough to vote.


And if your version of .NET predates the existence of SystemColors.InactiveSelectionHighlightTextBrushKey, this could probably use some refinement, but it works:

<ListBox
    >
    <ListBox.ItemContainerStyle>
        <Style 
            TargetType="ListBoxItem" 
            BasedOn="{StaticResource {x:Type ListBoxItem}}"
            >
            <Style.Setters>
                <Setter Property="Template">
                    <Setter.Value>
                        <ControlTemplate TargetType="ListBoxItem">
                            <Grid
                                Background="{TemplateBinding Background}"
                                >
                                <ContentPresenter />
                            </Grid>
                        </ControlTemplate>
                    </Setter.Value>
                </Setter>
            </Style.Setters>
            <Style.Triggers>
                <Trigger Property="IsSelected" Value="True">
                    <Setter 
                        Property="Background" 
                        Value="{StaticResource {x:Static SystemColors.HighlightBrushKey}}" 
                        />
                    <Setter 
                        Property="Foreground" 
                        Value="{StaticResource {x:Static SystemColors.HighlightTextBrushKey}}" 
                        />
                </Trigger>
                <MultiTrigger>
                    <MultiTrigger.Conditions>
                        <Condition Property="IsSelected" Value="True" />
                        <Condition Property="IsEnabled" Value="False" />
                    </MultiTrigger.Conditions>
                    <Setter 
                        Property="Background" 
                        Value="{StaticResource {x:Static SystemColors.InactiveCaptionBrushKey}}" 
                        />
                </MultiTrigger>
            </Style.Triggers>
        </Style>
    </ListBox.ItemContainerStyle>
</ListBox>
  • Exactly the issue I was running into. Thanks for the fix! Marked as answer. (sorry not known enough to +1) – jonmicjam May 31 '17 at 19:11
  • Overriding the `InactiveSelectionHighlightBrushKey` did the trick, even though it breaks the Designer in VS2010. [Microsoft docs](https://msdn.microsoft.com/en-us/library/system.windows.systemcolors.inactiveselectionhighlightbrushkey(v=vs.110).aspx) also say the, "brush's `IsFrozen` property is **true**, so it cannot be modified." Is this something to worry about? – jonmicjam May 31 '17 at 19:27
  • You're not altering the Brush resource; you're just creating a new one with the same key in a more local scope, so `FindResource()` (or whatever is actually doing the job) will find yours first and stop looking. That's unfortunate about breaking the designer, though. – 15ee8f99-57ff-4f92-890c-b56153 May 31 '17 at 19:30
  • For anyone else stuck on older .NET frameworks, I came across [this SO answer](https://stackoverflow.com/a/13827971/4681872) that states, "if the system has only .NET FW 4.0, an exception will be thrown when the system create User control with InactiveSelectionHighlightBrushKey." They recommend setting the color in handlers of LostFocus/LostKeyboardFocus/GotFocus. – jonmicjam May 31 '17 at 19:41
  • @jonmicjam Oh wow, I forgot about that. Yes, that's a relatively recent one. Should I toss together an ItemContainerStyle that does it the hard way? – 15ee8f99-57ff-4f92-890c-b56153 May 31 '17 at 19:42
  • An example may help any others with the same issue. But I'm going to fall back to code-behind, since I assume to do it via XAML would require `EventTriggers` with `Actions` and `Storyboards`? – jonmicjam May 31 '17 at 19:55
  • @jonmicjam I think it'd just be `` – 15ee8f99-57ff-4f92-890c-b56153 May 31 '17 at 19:57