xamluwpuwp-xamllistviewitemvisualstatemanager

UWP unfocused VisualState overriding selected VisualState on ListViewItem


I have a ListView that is using a custom style on its ItemContainerStyle property.

The problem is that when I use a keyboard to navigate through the items in the list, if I navigate to the currently-selected item, it gets the focused style (which is good), but then the moment I navigate away from that item it starts using the unfocused style instead of the selected style.

For example, let's say item 3 out of 5 is selected in the ListView. It'll look like this before the list gets focus, which is correct:

third item getting properly styled with selected unfocused state

Then I use my keyboard to navigate to the list, which applies the "focused" style to the selected item, which is fine...

third item getting properly styled with focused state

But then I navigate down by pressing the down arrow key, and suddenly the third item is no longer looks "selected." It looks like it's just a normal unselected item:

third item incorrectly getting unfocused style applied when I want selected style

This is happening because the "unfocused" state is overriding the "selected" visual state of the third item, but I want it to be the other way around.

Note: I have SingleSelectionFollowsFocus="False" on the ListView.

If I remove the "unselected" state, then moving the focus from the third item makes any item that I navigate to look like it's currently getting the focused state... if I could figure out why this is happening, then I could safely remove the "unselected" state, and it would work fine.

multiple items getting focus state for some reason

How can I retain the "selected" visual state of the selected item after unfocusing it?

Here's my code:

XAML:

<ListView x:Name="ThingieListView"
                                  ItemsSource="{x:Bind viewModel.ThingieDataList}"
                                  SingleSelectionFollowsFocus="False"
                                  SelectionMode="Single"
                                  IsItemClickEnabled="True"
                                  XYFocusRight="{x:Bind LocationListView}"
                                  Margin="8"
                                  Width="256"
                                  IsTabStop="True"
                                  TabIndex="0"
                                  LostFocus="ThingieListView_LostFocus"
                                  GotFocus="ThingieListView_GotFocus"
                                  ItemClick="ThingieListView_OnItemClick"
                                  ItemContainerStyle="{StaticResource SelectThingieItemStyle}">
                                <ListView.ItemTemplate>
                                    <DataTemplate>
                                        <ListViewItem Content="{Binding DisplayValue}"  />
                                    </DataTemplate>
                                </ListView.ItemTemplate>
                            </ListView> 

Custom Style:

<Style x:Key="SelectThingieItemStyle" TargetType="ListViewItem">
        <Setter Property="FontFamily" Value="{StaticResource Res-FontFamily}"/>
        <Setter Property="FontSize" Value="{StaticResource FontSizeText2}"/>
        <Setter Property="Background" Value="{ThemeResource ListViewItemBackground}"/>
        <Setter Property="Foreground" Value="{ThemeResource ListViewItemForeground}"/>
        <Setter Property="TabNavigation" Value="Local"/>
        <Setter Property="IsHoldingEnabled" Value="True"/>
        <Setter Property="Padding" Value="0,0,0,0"/>
        <Setter Property="HorizontalContentAlignment" Value="Left"/>
        <Setter Property="VerticalContentAlignment" Value="Center"/>
        <Setter Property="MinWidth" Value="{ThemeResource ListViewItemMinWidth}"/>
        <Setter Property="MinHeight" Value="{ThemeResource ListViewItemMinHeight}"/>
        <Setter Property="AllowDrop" Value="False"/>
        <Setter Property="UseSystemFocusVisuals" Value="{StaticResource UseSystemFocusVisuals}"/>
        <Setter Property="FocusVisualMargin" Value="0"/>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="ListViewItem">
                    <ListViewItemPresenter x:Name="Root"
                                           CheckBrush="{ThemeResource ListViewItemCheckBrush}"
                                           ContentMargin="15"
                                           Height="63"
                                           Margin="0 0 0 1"
                                           CheckBoxBrush="{ThemeResource ListViewItemCheckBoxBrush}"
                                           ContentTransitions="{TemplateBinding ContentTransitions}"
                                           CheckMode="{ThemeResource ListViewItemCheckMode}"
                                           DragOpacity="{ThemeResource ListViewItemDragThemeOpacity}"
                                           DisabledOpacity="{ThemeResource ListViewItemDisabledThemeOpacity}"
                                           DragBackground="{ThemeResource ListViewItemDragBackground}"
                                           DragForeground="{ThemeResource ListViewItemDragForeground}"
                                           FocusBorderBrush="{ThemeResource ListViewItemFocusBorderBrush}"
                                           FocusVisualMargin="{TemplateBinding FocusVisualMargin}"
                                           FocusVisualPrimaryThickness="0"
                                           FocusSecondaryBorderBrush="{ThemeResource ListViewItemFocusSecondaryBorderBrush}"
                                           HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
                                           Control.IsTemplateFocusTarget="True"
                                           PressedBackground="{ThemeResource ListViewItemBackgroundPressed}"
                                           PlaceholderBackground="{ThemeResource ListViewItemPlaceholderBackground}"
                                           PointerOverForeground="{ThemeResource ListViewItemForegroundPointerOver}"
                                           PointerOverBackground="{ThemeResource ListViewItemBackgroundPointerOver}"
                                           RevealBorderThickness="{ThemeResource ListViewItemRevealBorderThemeThickness}"
                                           ReorderHintOffset="{ThemeResource ListViewItemReorderHintThemeOffset}"
                                           RevealBorderBrush="{ThemeResource ListViewItemRevealBorderBrush}"
                                           RevealBackground="{ThemeResource ListViewItemRevealBackground}"
                                           SelectedForeground="{ThemeResource ListViewItemForegroundSelected}"
                                           SelectionCheckMarkVisualEnabled="{ThemeResource ListViewItemSelectionCheckMarkVisualEnabled}"
                                           SelectedBackground="{ThemeResource ListViewItemBackgroundSelected}"
                                           SelectedPressedBackground="{ThemeResource ListViewItemBackgroundSelectedPressed}"
                                           SelectedPointerOverBackground="{ThemeResource ListViewItemBackgroundSelectedPointerOver}"
                                           VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}">
                        <VisualStateManager.VisualStateGroups>
                            <VisualStateGroup x:Name="CommonStates">
                                <VisualState x:Name="Normal">
                                    <VisualState.Setters>
                                        <Setter Target="Root.Foreground" Value="{StaticResource Res-White}"/>
                                        <Setter Target="Root.RevealBackground" Value="{StaticResource Res-Gray_2}"/>
                                        <Setter Target="Root.RevealBorderBrush" Value="{StaticResource Res-Gray_1}"/>
                                        <Setter Target="Root.RevealBorderThickness" Value="0"/>
                                    </VisualState.Setters>
                                </VisualState>
                                <VisualState x:Name="Selected">
                                    <VisualState.Setters>
                                        <Setter Target="Root.Foreground" Value="{StaticResource Res-White}"/>
                                        <Setter Target="Root.RevealBorderBrush" Value="{StaticResource Res-Black}"/>
                                        <Setter Target="Root.FontWeight" Value="Bold"/>
                                        <Setter Target="Root.RevealBorderThickness" Value="0"/>
                                        <Setter Target="Root.RevealBackground" Value="{StaticResource Res-Gray_4}"/>
                                    </VisualState.Setters>
                                </VisualState>
                                <VisualState x:Name="PointerOver">
                                    <VisualState.Setters>
                                        <Setter Target="Root.RevealBackground" Value="{StaticResource Res-Gray_2}"/>
                                        <Setter Target="Root.RevealBorderThickness" Value="3"/>
                                        <Setter Target="Root.Foreground" Value="{StaticResource Res-White}"/>
                                        <Setter Target="Root.RevealBorderBrush" Value="{StaticResource Res-Orange}"/>
                                    </VisualState.Setters>
                                </VisualState>
                                <VisualState x:Name="PointerOverSelected">
                                    <VisualState.Setters>
                                        <Setter Target="Root.RevealBackground" Value="{StaticResource Res-Gray_4}"/>
                                        <Setter Target="Root.RevealBorderThickness" Value="3"/>
                                        <Setter Target="Root.Foreground" Value="{StaticResource Res-White}"/>
                                        <Setter Target="Root.RevealBorderBrush" Value="{StaticResource Res-Orange}"/>
                                    </VisualState.Setters>
                                </VisualState>
                                <VisualState x:Name="PointerOverPressed">
                                    <VisualState.Setters>
                                        <Setter Target="Root.RevealBackground" Value="{StaticResource Res-Gray_2}"/>
                                        <Setter Target="Root.RevealBorderThickness" Value="3"/>
                                        <Setter Target="Root.Foreground" Value="{StaticResource Res-Black}"/>
                                        <Setter Target="Root.RevealBorderBrush" Value="{StaticResource Res-Orange}"/>
                                    </VisualState.Setters>
                                </VisualState>
                                <VisualState x:Name="Pressed">
                                    <VisualState.Setters>
                                        <Setter Target="Root.RevealBorderThickness" Value="3"/>
                                        <Setter Target="Root.RevealBorderBrush" Value="{StaticResource Res-Orange}"/>
                                        <Setter Target="Root.FontWeight" Value="Bold"/>
                                        <Setter Target="Root.Foreground" Value="{StaticResource Res-White}"/>
                                        <Setter Target="Root.RevealBackground" Value="{StaticResource Res-Orange}"/>
                                    </VisualState.Setters>
                                </VisualState>
                                <VisualState x:Name="PressedSelected">
                                    <VisualState.Setters>
                                        <Setter Target="Root.RevealBorderThickness" Value="3"/>
                                        <Setter Target="Root.RevealBorderBrush" Value="{StaticResource Res-Orange}"/>
                                        <Setter Target="Root.FontWeight" Value="Bold"/>
                                        <Setter Target="Root.Foreground" Value="{StaticResource Res-White}"/>
                                        <Setter Target="Root.RevealBackground" Value="{StaticResource Res-Orange}"/>
                                    </VisualState.Setters>
                                </VisualState>
                            </VisualStateGroup>
                            <VisualStateGroup x:Name="FocusedStates">
                                <VisualState x:Name="Focused">
                                    <VisualState.Setters>
                                        <Setter Target="Root.FontWeight" Value="Bold"/>
                                        <Setter Target="Root.Foreground" Value="{StaticResource Res-Black}"/>
                                        <Setter Target="Root.RevealBackground" Value="{StaticResource Res-White}"/>
                                        <Setter Target="Root.RevealBorderThickness" Value="0 0 20 0"/>
                                        <Setter Target="Root.RevealBorderBrush" Value="{StaticResource Res-Orange}"/>
                                    </VisualState.Setters>
                                </VisualState>
                                <VisualState x:Name="Unfocused">
                                    <VisualState.Setters>
                                        <Setter Target="Root.Foreground" Value="{StaticResource Res-White}"/>
                                        <Setter Target="Root.RevealBackground" Value="{StaticResource Res-Gray_2}"/>
                                        <Setter Target="Root.RevealBorderBrush" Value="{StaticResource Res-Gray_1}"/>
                                        <Setter Target="Root.RevealBorderThickness" Value="0 1 0 1"/>
                                    </VisualState.Setters>
                                </VisualState>
                            </VisualStateGroup>
                            <VisualStateGroup x:Name="DisabledStates">
                                <VisualState x:Name="Enabled"/>
                                <VisualState x:Name="Disabled">
                                    <VisualState.Setters>
                                        <Setter Target="Root.RevealBorderThickness" Value="0"/>
                                    </VisualState.Setters>
                                </VisualState>
                            </VisualStateGroup>
                        </VisualStateManager.VisualStateGroups>
                    </ListViewItemPresenter>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
        
    </Style> 

Solution

  • How can I retain the "selected" visual state of the selected item after unfocusing it?

    As you mentioned, the "unfocused" state will override the "selected" state, if you want to display the "selected" state, you could try to trigger the "selected" visual state manually when the selected item loses focus in the VesselListView_LostFocus event. First determine if the item that loses focus is selected, if it is, you could trigger the "selected" state. For example:

    private void VesselListView_LostFocus(object sender, RoutedEventArgs e)
    {
        ListViewItem litem = e.OriginalSource as ListViewItem;
        if (litem != null && litem.IsSelected == true)
        {
            VisualStateManager.GoToState(litem, "Selected", false);
        }
    }