dotnet / maui

.NET MAUI is the .NET Multi-platform App UI, a framework for building native device applications spanning mobile, tablet, and desktop.
https://dot.net/maui
MIT License
22k stars 1.72k forks source link

AdaptiveTrigger in style for multiple elements does not work #14259

Open mstoll-kramp-it opened 1 year ago

mstoll-kramp-it commented 1 year ago

Description

A VisualStyle with an AdaptiveTrigger will not work correctly if the VisualStyle is bound to multiple elements via a style. Removing all but one matching elements for the style will result in the AdaptiveTrigger working correctly for that single style.

I have reproduced the issue in a unit test in the attached reproduction project repository (a fork of the maui project): https://github.com/mstoll-kramp-it/maui_adaptivetrigger_bug_repro/blob/main/src/Controls/tests/Core.UnitTests/AdaptiveTriggerTests.cs .

Edit: It appears that the very example given in the docs shows the same behaviour: https://learn.microsoft.com/en-us/dotnet/maui/fundamentals/triggers?view=net-maui-7.0#adaptive-trigger . This also stops working as soon as a second StackLayout is added to the page.

Steps to Reproduce

  1. Create a page with multiple labels.
  2. Add an implicit style for labels to the page's resources.
  3. Add a VisualStateGroup with two VisualStates to the style.
  4. Give each VisualState an AdaptiveTrigger, requiring minimum width of 0 and 300.

Expected behavior:

Actual behavior:

Additional observations:

  1. Remove all but one label

Actual behavior:

I tried debugging the issue with two labels differentiated by their text, and was able to find that in src\Controls\Core\AdaptiveTrigger.cs AttachEvents() (line 60) the element's window is null. Apparently the initial connection of the page to the window triggers a binding of elements to the window, which in turn triggers a re-binding of AdaptiveTriggers to elements, but after binding label 1 to the window the trigger tries to access label 2 before the window is connected to it.

My intent is to make my pages responsive by resizing certain elements and fonts based on screen size. I expected this to be simpler. Is there another way, or what is best practice to achieve this?

Link to public reproduction project repository

https://github.com/mstoll-kramp-it/maui_adaptivetrigger_bug_repro/blob/main/src/Controls/tests/Core.UnitTests/AdaptiveTriggerTests.cs

Version with bug

7.0 (current)

Last version that worked well

Unknown/Other

Affected platforms

Windows, I was not able test on other platforms

Affected platform versions

Windows SDK 10.0.22000.832

Did you find any workaround?

No response

Relevant log output

No response

ghost commented 1 year ago

We've added this issue to our backlog, and we will work to address it as time and resources allow. If you have any additional information or questions about this issue, please leave a comment. For additional info about issue management, please read our Triage Process.

mstoll-kramp-it commented 1 year ago

I have a cumbersome workaround: Defining a style per element type and state, naming all elements, and toggling styles on a single trigger:

<ContentView [...]>

    <ContentView.Resources>

        <Style TargetType="Label" x:Key="LabelStyleBase">
            [Setters I want to applied to my labels in any VisualState]
        </Style>

        <Style TargetType="Label" x:Key="LabelStyleState1" BasedOn="{StaticResource LabelStyleBase}">
            [Setters I want to applied to my labels in VisualState1]
        </Style>

        <Style TargetType="Label" x:Key="LabelStyleState2" BasedOn="{StaticResource LabelStyleBase}">
            [Setters I want to applied to my labels in VisualState2]
        </Style>

    </ContentView.Resources>

    <VisualStateManager.VisualStateGroups>
        <VisualStateGroup>
            <VisualState Name="State1">
                <VisualState.StateTriggers>
                    <AdaptiveTrigger MinWindowWidth="0" />
                </VisualState.StateTriggers>
                <VisualState.Setters>
                    <Setter TargetName="Label1" Property="Style" Value="{StaticResource LabelStyleState1}" />
                    <Setter TargetName="Label2" Property="Style" Value="{StaticResource LabelStyleState1}" />
                </VisualState.Setters>
            </VisualState>
            <VisualState Name="State2">
                <VisualState.StateTriggers>
                    <AdaptiveTrigger MinWindowWidth="800" />
                </VisualState.StateTriggers>
                <VisualState.Setters>
                    <Setter TargetName="Label1" Property="Style" Value="{StaticResource LabelStyleState2}" />
                    <Setter TargetName="Label2" Property="Style" Value="{StaticResource LabelStyleState2}" />
                </VisualState.Setters>
            </VisualState>
        </VisualStateGroup>
    </VisualStateManager.VisualStateGroups>

    <ContentPage.Content>
        <StackLayout>
            <Label x:Name="Label1" Text="Label 1" />
            <Label x:Name="Label2" Text="Label 2" />
        </StackLayout>
    </ContentPage.Content>

</ContentView>