unoplatform / uno

Open-source platform for building cross-platform native Mobile, Web, Desktop and Embedded apps quickly. Create rich, C#/XAML, single-codebase apps from any IDE. Hot Reload included! 90m+ NuGet Downloads!!
https://platform.uno
Apache License 2.0
8.81k stars 708 forks source link

`PivotHeaderItem` not trigger Selected `VisualState` on Load control #4566

Open matizk144 opened 3 years ago

matizk144 commented 3 years ago

Current behavior

For customized PivotHeaderTemplate during Loading application (control) Selected VIsual state is not triggered (but first tab is selected)

invalidtabsonload

Expected behavior

validtabsonload

How to reproduce it (as minimally and precisely as possible)

<Setter Property="Template">
    <Setter.Value>
        <ControlTemplate TargetType="PivotHeaderItem">
            <Grid x:Name="RootGrid" Width="182" Height="38">
                <VisualStateManager.VisualStateGroups>
                    <VisualStateGroup x:Name="SelectionStates">
                        <VisualState x:Name="Selected">
                            <Storyboard>
                                <ObjectAnimationUsingKeyFrames Storyboard.TargetName="RootGrid" Storyboard.TargetProperty="Background">
                                    <DiscreteObjectKeyFrame KeyTime="0" Value="#FFFFFF"/>
                                </ObjectAnimationUsingKeyFrames>
                                <ObjectAnimationUsingKeyFrames Storyboard.TargetName="TextHeader" Storyboard.TargetProperty="Foreground">
                                    <DiscreteObjectKeyFrame KeyTime="0" Value="#000000"/>
                                </ObjectAnimationUsingKeyFrames>
                                <ObjectAnimationUsingKeyFrames Storyboard.TargetName="Underline" Storyboard.TargetProperty="Visibility">
                                    <DiscreteObjectKeyFrame KeyTime="0" Value="Visible"/>
                                </ObjectAnimationUsingKeyFrames>
                            </Storyboard>
                        </VisualState>
                        <VisualState x:Name="Unselected">
                            <Storyboard>
                                <ObjectAnimationUsingKeyFrames Storyboard.TargetName="RootGrid" Storyboard.TargetProperty="Background">
                                    <DiscreteObjectKeyFrame KeyTime="0" Value="#00000000"/>
                                </ObjectAnimationUsingKeyFrames>
                                <ObjectAnimationUsingKeyFrames Storyboard.TargetName="TextHeader" Storyboard.TargetProperty="Foreground">
                                    <DiscreteObjectKeyFrame KeyTime="0" Value="#80000000"/>
                                </ObjectAnimationUsingKeyFrames>
                                <ObjectAnimationUsingKeyFrames Storyboard.TargetName="Underline" Storyboard.TargetProperty="Visibility">
                                    <DiscreteObjectKeyFrame KeyTime="0" Value="Collapsed"/>
                                </ObjectAnimationUsingKeyFrames>
                            </Storyboard>
                        </VisualState>
                    </VisualStateGroup>
                </VisualStateManager.VisualStateGroups>
                <TextBlock x:Name="TextHeader" Text="{Binding Content, RelativeSource={RelativeSource Mode=TemplatedParent}}" FontSize="14" FontFamily="{StaticResource Roboto-Medium}" FontWeight="Medium" HorizontalAlignment="Center" VerticalAlignment="Center"/>
                <Rectangle x:Name="Underline" Fill="#002E56" Height="3" VerticalAlignment="Bottom" HorizontalAlignment="Stretch" Visibility="Collapsed" />
            </Grid>
        </ControlTemplate>
    </Setter.Value>
</Setter>
</Style>
<Pivot>
    <PivotItem Header="Reader Diagnostic">
        <TextBlock Text="Content of pivot item 1."/>
    </PivotItem>
    <PivotItem Header="Card Diagnostic">
        <TextBlock Text="Content of pivot item 2."/>
    </PivotItem>
</Pivot>

Workaround

Assign to Loaded event of Pivot control and reselect Items.. e.g:

private void Pivot_OnLoaded(object sender, RoutedEventArgs e)
{
      Pivot.SelectedIndex = -1;
      Pivot.SelectedIndex = 0;
}

Nuget Package: Uno.UI.Skia.Gtk: 3.3.0

Nuget Package Version(s):

Affected platform(s):

IDE:

Relevant plugins:

Anything else we need to know?

jeromelaban commented 3 years ago

Thanks for the report!

Note for contributors

This may be an issue related to the handling of IsSelected in Selector.

Youssef1313 commented 3 years ago

I have been looking at this, and I think I found the root cause, which may be affecting other controls (even user custom controls may be affected):

To explain what I'm suspecting to be the issue, I'll take a simple example.

Just add a button to a new empty project, and in MainPage constructor after InitializeComponent call, add var x = VisualTreeHelper.GetChild(btn, 0);

Note the variable x value using the debugger on both Android and UWP.

Why is this an issue?

Back to the PivotHeaderItem, when the control isn't loaded and GoToState is called, the CurrentState will change and be stored as "Selected". But the control isn't loaded and no visual changes occurred.

After a while, when the PivotHeaderItem is loaded, GoToState gets called again with the same state ("Selected"). This time, VisualStateManager will do nothing because the current state is the same as the requested new state. So it simply returns.

A workaround that's likely to work is to return in VisualStateManager.GoToState if control.IsLoaded is false.

NOTE: This could be the root cause of few other issues related to visual states.

@davidjohnoliver @jeromelaban @MartinZikmund Does my analysis make sense? Any ideas on how to move forward with the this? If it's going to be complicated, would you be okay with the workaround (I haven't actually tested the workaround - but likely will work)?

jeromelaban commented 3 years ago

This may be an issue related to #3519. That being said, you're saying but the control isn't loaded and no visual changes occurred. did you find the location that prevents this case ?

Youssef1313 commented 3 years ago

@jeromelaban I didn't, but I assumed this is expected? Will take another look and see if I can get deeper into how VisualStateManager.GoToState works deeply.