xamarin / Xamarin.Forms

Xamarin.Forms is no longer supported. Migrate your apps to .NET MAUI.
https://aka.ms/xamarin-upgrade
Other
5.62k stars 1.87k forks source link

[Bug] AdaptiveTrigger crashes when paired with a Setter with TargetName #10049

Open jimmyjxiao opened 4 years ago

jimmyjxiao commented 4 years ago

Description

When paired with a Setter that has the TargetName property, using AdaptiveTrigger can throw an exception that crashes the application if the trigger is hit when the page loads. The exception thrown is

System.InvalidOperationException: 'this element is not in a namescope'

Steps to Reproduce

  1. Open the VsmDemos solution from the Xamarin-Forms-Samples
  2. Go to VsmSetterTargetName.xaml and replace the triggers with
    <VisualStateGroup x:Name="CommonStates">
                    <VisualState x:Name="Normal" />
                    <VisualState x:Name="BigWindow">
                        <VisualState.Setters>
                            <Setter Property="Scale"
                                    Value="0.8" />
                            <Setter TargetName="entry"
                                    Property="Entry.Text"
                                    Value="Paris" />
                        </VisualState.Setters>
                        <VisualState.StateTriggers>
                            <AdaptiveTrigger MinWindowWidth="200"/>
                        </VisualState.StateTriggers>
                    </VisualState>
                </VisualStateGroup>
  3. Add Device.SetFlags(new string[] { "StateTriggers_Experimental" }); to App.xaml.cs
  4. Run the app, and before you click the "VSM with Setter TargetName", make sure the window (UWP) or device width is more than 200

Expected Behavior

The visual state is triggered, the effects from the Visual State are visible, because the width of the window/device is more than 200

Actual Behavior

The page crashes during loading, with the exception mentioned above.

Basic Information

This behavior is not present if the Setter doesn't have a TargetName property set. My guess is the cause of this issue is the Setter is getting triggered by the AdaptiveTrigger before InitializeComponent is finished running. The behavior also works as expected if the window is small when the page is opened, then the width goes above 200 after the page is loaded (easy to test on UWP).

jsuarezruiz commented 4 years ago

More information:

Xamarin.Forms.Element.FindByName(string)
    Xamarin.Forms.Setter.Apply(Xamarin.Forms.BindableObject, bool)
    Xamarin.Forms.VisualStateManager.GoToState(Xamarin.Forms.VisualElement, string)
    Xamarin.Forms.VisualStateGroup.UpdateStateTriggers()
    Xamarin.Forms.VisualStateGroupList.Validate(System.Collections.Generic.IList<Xamarin.Forms.VisualStateGroup>)
    Xamarin.Forms.VisualStateGroupList.ValidateAndNotify(System.Collections.Generic.IList<Xamarin.Forms.VisualStateGroup>)
    Xamarin.Forms.WatchAddList<T>.Add(T)
    Xamarin.Forms.VisualStateGroupList.Add(Xamarin.Forms.VisualStateGroup)
    VsmDemos.VsmSetterTargetNamePage.InitializeComponent() en VsmSetterTargetNamePage.xaml.g.cs
    VsmDemos.VsmSetterTargetNamePage.VsmSetterTargetNamePage() en VsmSetterTargetNamePage.xaml.cs
LoGA80 commented 4 years ago

Issue seems to come without using AdaptiveTrigger just by using VisualState with TargetName. We have a Style that contains a VisualState definition and when the page loads it crashes when the Disabled state is getting used (IsEnabled==False).

<VisualStateGroup x:Name="CommonStates">
    <VisualState x:Name="Normal">
        <VisualState.Setters>
            <Setter Property="BackgroundColor" Value="{DynamicResource EnterButtonBackgroundColor}" />
        </VisualState.Setters>
    </VisualState>
    <VisualState x:Name="Disabled">
        <VisualState.Setters>
            <Setter Property="BackgroundColor" Value="{DynamicResource DisabledEnterButtonBackgroundColor}" />
            <Setter TargetName="buttonLabel" Property="Label.TextColor" Value="{DynamicResource DisabledEnterButtonTextColor}" />
        </VisualState.Setters>
    </VisualState>
</VisualStateGroup>

The control this style is applied to is a Frame, the control with the name "buttonLabel" is a Label inside the Frame.

  at Xamarin.Forms.Element.FindByName (System.String name) [0x00009] in D:\a\1\s\Xamarin.Forms.Core\Element.cs:274 
  at Xamarin.Forms.Setter.Apply (Xamarin.Forms.BindableObject target, System.Boolean fromStyle) [0x00027] in D:\a\1\s\Xamarin.Forms.Core\Setter.cs:58 
  at Xamarin.Forms.VisualStateManager.GoToState (Xamarin.Forms.VisualElement visualElement, System.String name) [0x000be] in D:\a\1\s\Xamarin.Forms.Core\VisualStateManager.cs:90 
  at Xamarin.Forms.VisualElement.ChangeVisualState () [0x00008] in D:\a\1\s\Xamarin.Forms.Core\VisualElement.cs:946 
  at Xamarin.Forms.VisualElement.OnIsEnabledPropertyChanged (Xamarin.Forms.BindableObject bindable, System.Object oldValue, System.Object newValue) [0x00012] in D:\a\1\s\Xamarin.Forms.Core\VisualElement.cs:997
...
Only-Xam commented 3 years ago

@jsuarezruiz /Team, Issue still persist in XF 5.0.01558-pre3. Tested below code on UWP and Android. Necessary comments to reproduce the issue are mentioned in the sample code below. (If you run below code it will work but if you use commented sections, it will crash) Using Style Key causing issue so we cant apply Style to specific Grid. Also tried to apply Style at Control level (directly on MainGrid) but same issue.

<ContentPage x:Class="Test2.Views.AdaptiveTest" xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:viewmodels="clr-namespace:Test2.ViewModels">

<ContentPage.BindingContext>
    <viewmodels:MonkeysViewModel />
</ContentPage.BindingContext>

<ContentPage.Resources>

    <!--  If we use Key then it will not work and crash with exception - System.InvalidOperationException: this element is not in a namescope  -->
    <!--<Style TargetType="Grid" x:Key="MainGridStyle">   -->

    <Style TargetType="Grid">
        <Setter Property="VisualStateManager.VisualStateGroups">
            <VisualStateGroupList>
                <VisualStateGroup Name="ScreenStates">
                    <VisualState Name="NarrowState">
                        <VisualState.StateTriggers>
                            <AdaptiveTrigger MinWindowWidth="0" />
                        </VisualState.StateTriggers>
                        <VisualState.Setters>
                            <Setter Property="BackgroundColor" Value="LightCoral" />
                            <Setter Property="Padding" Value="0,10" />
                            <Setter TargetName="LeftOne" Property="ColumnDefinition.Width" Value="50" />
                            <Setter TargetName="RightOne" Property="ColumnDefinition.Width" Value="50" />
                        </VisualState.Setters>
                    </VisualState>

                    <VisualState Name="WideState">
                        <VisualState.StateTriggers>
                            <AdaptiveTrigger MinWindowWidth="1000" />
                        </VisualState.StateTriggers>
                        <VisualState.Setters>
                            <Setter Property="BackgroundColor" Value="LightGreen" />
                            <Setter Property="Padding" Value="40" />
                            <Setter TargetName="LeftOne" Property="ColumnDefinition.Width" Value="300" />
                            <Setter TargetName="RightOne" Property="ColumnDefinition.Width" Value="300" />
                        </VisualState.Setters>
                    </VisualState>
                </VisualStateGroup>
            </VisualStateGroupList>
        </Setter>
    </Style>
</ContentPage.Resources>

<!--  If we use this Style Key for below MainGrid, it will not work and crash with exception - System.InvalidOperationException: this element is not in a namescope  -->
<!--  <Grid x:Name="MainGrid" RowDefinitions="*, 8*,*" Style="{StaticResource MainGridStyle}"  -->

<Grid x:Name="MainGrid" RowDefinitions="*, 8*,*">
    <Grid.ColumnDefinitions>
        <ColumnDefinition x:Name="LeftOne" Width="2*" />
        <ColumnDefinition x:Name="Middle" Width="6*" />
        <ColumnDefinition x:Name="RightOne" Width="2*" />
    </Grid.ColumnDefinitions>

    <BoxView
        x:Name="Box1"
        Grid.Row="0"
        Grid.Column="0"
        BackgroundColor="SkyBlue" />

    <BoxView
        x:Name="Box2"
        Grid.Row="0"
        Grid.Column="2"
        BackgroundColor="Red" />

    <CollectionView
        Grid.Row="1"
        Grid.Column="1"
        BackgroundColor="Azure"
        HorizontalScrollBarVisibility="Always"
        ItemsSource="{Binding Monkeys}"
        VerticalScrollBarVisibility="Never">
        <CollectionView.ItemTemplate>
            <DataTemplate>
                <StackLayout>
                    <Label LineBreakMode="WordWrap">
                        <Label.FormattedText>
                            <FormattedString>
                                <Span
                                    FontAttributes="Bold"
                                    Text="If we use STACK, it is working fine."
                                    TextColor="Red" />
                                <Span
                                    FontAttributes="Bold"
                                    Text="If we removme STACK and use GRID in data template it doesn't work and app crashes."
                                    TextColor="Red" />
                                <Span
                                    FontAttributes="Bold"
                                    Text="Remove this STACK and try uncommenting GRID section."
                                    TextColor="Red" />
                            </FormattedString>
                        </Label.FormattedText>
                    </Label>
                </StackLayout>

                <!--  Using below child GRID in data teample will cause application to crash  -->

                <!--<Grid x:Name="ChildGrid" Padding="10">
                    <Grid.RowDefinitions>
                        <RowDefinition Height="Auto" />
                        <RowDefinition Height="Auto" />
                    </Grid.RowDefinitions>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="Auto" />
                        <ColumnDefinition Width="Auto" />
                    </Grid.ColumnDefinitions>
                    <Image
                        Grid.RowSpan="2"
                        Aspect="AspectFill"
                        HeightRequest="60"
                        Source="{Binding ImageUrl}"
                        WidthRequest="60" />
                    <Label
                        Grid.Column="1"
                        FontAttributes="Bold"
                        Text="{Binding Name}" />
                    <Label
                        Grid.Row="1"
                        Grid.Column="1"
                        FontAttributes="Italic"
                        Text="{Binding Location}"
                        VerticalOptions="End" />
                </Grid>-->
            </DataTemplate>

        </CollectionView.ItemTemplate>

    </CollectionView>
</Grid>

Below error comes when we use Style Key. image

Below error comes when we comment stack section in CollectionView data template and use Grid. Then launch application and try to narrow down the application window.

image

MitchBomcanhao commented 3 years ago

any updates on this?

mlyrstad commented 3 years ago

I would like to know as well. Triggers and state managersare rather important, and they're currently quite hard to get to work.

Only-Xam commented 3 years ago

@jsuarezruiz @samhouts Issue still persist even in latest XF 5 release. Same error while trying to apply style to a child Grid inside the Main Grid as mentioned earlier.

MarkoJosipovicIDU commented 3 years ago

Problem is still there

Diesel1017 commented 3 years ago

The temporary solution to avoid the error. (Using code behind) `public partial class DetailPage : ContentPage { public DetailPage() { InitializeComponent();

        VisualStateGroup vsGroup = new VisualStateGroup();

        VisualState vsButtonDisabled = new VisualState()
        {
            Name = VisualStateManager.CommonStates.Disabled
        };

        vsButtonDisabled.Setters.Add(Button.BackgroundColorProperty, Color.DarkGray);

        VisualState vsButtonPressed = new VisualState()
        {
            Name = "Pressed"
        };

        vsButtonPressed.Setters.Add(Button.BackgroundColorProperty, Color.Green);
        vsButtonPressed.Setters.Add(Button.ScaleProperty, 0.90);

        VisualState vsButtonNormal = new VisualState()
        {
            Name = VisualStateManager.CommonStates.Normal
        };

        vsButtonNormal.Setters.Add(Button.BackgroundColorProperty, Color.Red);
        vsButtonNormal.Setters.Add(Button.ScaleProperty, 1);

        vsGroup.States.Add(vsButtonDisabled);
        vsGroup.States.Add(vsButtonPressed);
        vsGroup.States.Add(vsButtonNormal);

        VisualStateManager.SetVisualStateGroups(btnPause, new VisualStateGroupList() { vsGroup });
  }

}`

bondarenkod commented 2 years ago

I decided to use this 'adaptive state trigger' feature today. And quickly realized that it is not working properly

<Grid x:Name="PaneContainer" ColumnDefinitions="200, *">
        <Grid
            x:Name="LeftPane"
            Grid.Column="0"
            BackgroundColor="Green">
            <Label
                FontSize="22"
                HorizontalOptions="Center"
                Text="LEFT PANE"
                TextColor="White"
                VerticalOptions="Center" />
        </Grid>

        <Grid
            x:Name="RightPane"
            Grid.Column="1"
            BackgroundColor="Orange">

            <Label
                FontSize="22"
                HorizontalOptions="Center"
                Text="RIGHT PANE"
                TextColor="White"
                VerticalOptions="Center" />

        </Grid>

        <VisualStateManager.VisualStateGroups>
            <VisualStateGroupList>
                <VisualStateGroup>
                    <VisualState x:Name="Portrait">
                        <VisualState.StateTriggers>
                            <OrientationStateTrigger Orientation="Portrait" />
                        </VisualState.StateTriggers>
                        <VisualState.Setters>
                            <Setter Property="BackgroundColor" Value="Silver" /> 
                            <Setter TargetName="RightPane" Property="Opacity" Value="0" />
                            <Setter TargetName="RightPane" Property="IsVisible" Value="False" />
                        </VisualState.Setters>
                    </VisualState>
                    <VisualState x:Name="Landscape">
                        <VisualState.StateTriggers>
                            <OrientationStateTrigger Orientation="Landscape" />
                        </VisualState.StateTriggers>
                        <VisualState.Setters>
                            <Setter Property="BackgroundColor" Value="White" />
                        </VisualState.Setters>
                    </VisualState>
                </VisualStateGroup>
            </VisualStateGroupList>
        </VisualStateManager.VisualStateGroups>
    </Grid>