0
votes

I have the ListBoxItem's style as follows and a problem with loosing 'Focused' visual effects when the listBoxItem remains in mentioned VisualState. How to force appropriate visual effects to appear on ‘Focused’ ListBoxItem?

The scenario to achieve a bug:

1) Click the ListBoxItem -> it receive the focus and styles(ok).

2) Move the pointer over this ListBoxItem -> it’s still focused and receive the styles for ‘MouseOver’ (it is within a separate VisualStateGroup ‘CommonStates’ as suggested here: https://msdn.microsoft.com/en-us/library/system.windows.controls.listboxitem%28v=vs.95%29.aspx )(ok).

3) Make the mouse leave the ListBoxItem’s bounds -> it loses the style for MouseOver(ok).

4) Now, the ListBoxItem is in ‘Normal’ state of ‘CommonStates’ and is also ‘Focused’, but doesn’t have the stylish for ‘Focused’(it is my problem).

5) Attampting to click this ListBoxItem has no effect

Could you give me some advice, how to deal with it?

I’m using Silverlight, so triggers are forbidden 4 me.

Here is my style:

    <Style x:Key="ListBoxItemStyle1" TargetType="ListBoxItem">
    <Setter Property="HorizontalContentAlignment" Value="Center" />
    <Setter Property="HorizontalAlignment" Value="Left" />
    <Setter Property="ContentControl.Foreground" Value="{StaticResource ViewModeButtonForeground}" />
    <Setter Property="Focusable" Value="True" />
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="ListBoxItem">
                <Border x:Name="RootElement">
                    <VisualStateManager.VisualStateGroups>
                        <VisualStateGroup x:Name="CommonStates">
                            <VisualState x:Name="Normal" />
                            <VisualState x:Name="MouseOver">
                                <Storyboard>
                                    <ObjectAnimationUsingKeyFrames Storyboard.TargetName="Content" Storyboard.TargetProperty="(ContentControl.Foreground)">
                                        <DiscreteObjectKeyFrame KeyTime="0:0:0" Value="{StaticResource ViewModeButtonForeground}" />
                                    </ObjectAnimationUsingKeyFrames>
                                    <ObjectAnimationUsingKeyFrames Storyboard.TargetName="CheckOuterBorder" Storyboard.TargetProperty="BorderBrush">
                                        <DiscreteObjectKeyFrame KeyTime="0:0:0" Value="{StaticResource ViewModeButtonOuterBorder_MouseOver}" />
                                    </ObjectAnimationUsingKeyFrames>
                                    <ObjectAnimationUsingKeyFrames Storyboard.TargetName="CheckOuterBorder" Storyboard.TargetProperty="Background">
                                        <DiscreteObjectKeyFrame KeyTime="0:0:0" Value="{StaticResource ViewModeButtonBackground_MouseOver}" />
                                    </ObjectAnimationUsingKeyFrames>
                                </Storyboard>
                            </VisualState>
                        </VisualStateGroup>
                        <VisualStateGroup x:Name="FocusStates">
                            <VisualState x:Name="Focused">
                                <Storyboard>
                                    <ObjectAnimationUsingKeyFrames Storyboard.TargetName="Content" Storyboard.TargetProperty="(ContentControl.Foreground)">
                                        <DiscreteObjectKeyFrame KeyTime="0:0:0" Value="{StaticResource ViewModeButtonForeground_Focused}" />
                                    </ObjectAnimationUsingKeyFrames>
                                    <ObjectAnimationUsingKeyFrames Storyboard.TargetName="CheckOuterBorder" Storyboard.TargetProperty="BorderBrush">
                                        <DiscreteObjectKeyFrame KeyTime="0:0:0" Value="{StaticResource ViewModeButtonOuterBorder_Focused}" />
                                    </ObjectAnimationUsingKeyFrames>
                                    <ObjectAnimationUsingKeyFrames Storyboard.TargetName="CheckOuterBorder" Storyboard.TargetProperty="Background">
                                        <DiscreteObjectKeyFrame KeyTime="0:0:0" Value="{StaticResource ViewModeButtonBackground_Normal}" />
                                    </ObjectAnimationUsingKeyFrames>
                                </Storyboard>
                            </VisualState>
                            <VisualState x:Name="Unfocused"></VisualState>
                        </VisualStateGroup>
                    </VisualStateManager.VisualStateGroups>
                    <Grid>
                        <Border x:Name="CheckOuterBorder" CornerRadius="2" BorderThickness="1" BorderBrush="Transparent" Background="Transparent">
                        </Border>
                        <Grid x:Name="GridContent" Margin="{TemplateBinding Padding}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" Width="Auto">
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition Width="Auto"></ColumnDefinition>
                                <ColumnDefinition Width="Auto"></ColumnDefinition>
                            </Grid.ColumnDefinitions>
                            <Image Grid.Column="0" Width="16" Height="16" VerticalAlignment="Center" HorizontalAlignment="Left" Source="{Binding ImgSource, Mode=OneWay}"/>
                            <ContentControl x:Name="Content" Grid.Column="1" Margin="3,0,0,0" Foreground="{TemplateBinding Foreground}" Content="{Binding Title}" ContentTemplate="{TemplateBinding ContentTemplate}"  VerticalAlignment="Center" Width="Auto"/>
                        </Grid>
                    </Grid>
                </Border>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>
<Style x:Key="ViewModeSelectionListBoxStyle" TargetType="ListBox">
    <Setter Property="BorderThickness" Value="0" />
    <Setter Property="IsTabStop" Value="True" />
    <Setter Property="ItemContainerStyle" Value="{StaticResource ListBoxItemStyle1}" />
    <Setter Property="Focusable" Value="True" />
    <Setter Property="ItemsPanel">
        <Setter.Value>
            <ItemsPanelTemplate>
                <StackPanel Orientation="Horizontal"/>
            </ItemsPanelTemplate>
        </Setter.Value>
    </Setter>
</Style>    

If I change my VisualStateGroup from ‘FocusStates’ into SelectionStates it behaves the same. I want it to be associated with focus rather than the selection, because there can be visual mismatch in the UI if user clicks somewhere and another control will have similar border too.

BTW: On another msdn site I’ve read: ‘The control is always in one state for each VisualStateGroup that is defined in its ControlTemplate and only leaves a state when it goes into another state from the same VisualStateGroup.’ From my code-behind I have ensured that the listBoxItem has still the focus on. Besides, I was trying to enforce going to that state after the mouse leaving my listBoxItem – with no results (simplifying: ActiveViewDefinition is an replacement for the actually focused item in the listbox):

        private void It_MouseLeave(object sender, MouseEventArgs e)
        {
            ListBoxItem lbItem = sender as System.Windows.Controls.ListBoxItem;
            if (lbItem != null && lbItem.Content == ActiveViewDefinition)
            {
                VisualStateManager.GoToState(lbItem, "Focused", true);
            }
        }
1

1 Answers

0
votes

Easy. Your VisualStates from different groups manipulate the same properties of the same elements. You always need to have separate elements (or use separate properties of shared elements) to indicate states from different groups, otherwise they will naturally clash and mess with each others settings (as you have observed).

An example in pseudo-xaml:

<VisualStateGroup x:Name="CommonStates">
    <VisualState x:Name="Normal"><NoChange/></VisualState>
    <VisualState x:Name="MouseOver"><Change What="TheBorder" Color="Red"/></VisualState>
</VisualStateGroup>
<VisualStateGroup x:Name="FocusStates">
    <VisualState x:Name="Focused"><Change What="TheBorder" Color="Blue"/></VisualState>
</VisualStateGroup>

<Border x:Name="TheBorder" Color="Transparent"/>

Initially the border is Transparent. Now what happens when we move the mouse over the Border? The mouseOver state changes it to red. Now we click our control (let's assume it gets selected and can be focused). The focusState changes it to blue. Now we move the mouse away from it. The normalState changes it to its initial state: Transparent. The focusState effect is lost.

Let's have a look at the modified example:

<VisualStateGroup x:Name="CommonStates">
    <VisualState x:Name="Normal"><NoChange/></VisualState>
    <VisualState x:Name="MouseOver"><Change What="TheMouseoverBorder" Color="Red"/></VisualState>
</VisualStateGroup>
<VisualStateGroup x:Name="FocusStates">
    <VisualState x:Name="Focused"><Change What="TheFocusBorder" Color="Blue"/></VisualState>
</VisualStateGroup>

<Border x:Name="TheFocusBorder" Color="Transparent"/>
<Border x:Name="TheMouseoverBorder" Color="Transparent"/>

Initially both borders are transparent. Now what happens when we move the mouse over them? The mouseOver state changes the TheMouseoverBorder to red. Now we click our control (let's assume it gets selected and can be focused). The focusState changes TheFocusBorder to blue. Now we move the mouse away from it. The normalState changes TheMouseoverBorder back to transparent. The focusState is still visible because the TheFocusBorder is uneffected by other state changes and still blue.

[Edit]

If you absolutely must modify the same property of the same element from separate VisualStateGroups, you can either use some kind of aggregator that has a resulting output property, or use some kind of fallthrough mechanism.

I can think of several possible solutions to modify the same Foreground property from separate VisualStateGroups. You can try those and see if they work.

Fallthrough

<VisualState x:Name="MouseOver"><Change What="Outer" ForegroundColor="Red"/></VisualState>
...
<VisualState x:Name="Focused"><Change What="Inner" ForegroundColor="Blue"/></VisualState>

<ContentControl x:Name="Outer">
    <ContentControl x:Name="Inner">
    ...
    </ContentControl>
</ContentControl>

In theory: if both states are active the inner state wins (focused: blue), if only the mouseover state is active the property is not explicitly set on the inner and will be inherited from the outer. Not tested. Try it.

Aggregator

<VisualState x:Name="MouseOver"><Change What="Aggregator" MouseOverColor="Red"/></VisualState>
...
<VisualState x:Name="Focused"><Change What="Aggregator" FocusedColor="Blue"/></VisualState>

<InvisibleAggregator x:Name="Aggregator"/>
<ContentControl Foreground="{Binding ElementName=Aggregator, Path=EffectiveColor}"/>