Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[C# Markup] VisualStateManager not working in Custom control (or I am missing something...) #2565

Open
3 of 16 tasks
kucint opened this issue Sep 25, 2024 · 3 comments · Fixed by #2580
Open
3 of 16 tasks
Labels
kind/bug Something isn't working triage/untriaged Indicates an issue requires triaging or verification.

Comments

@kucint
Copy link

kucint commented Sep 25, 2024

Current behavior

I implemented VisualStateManager as presented in example: VisualStateManager
In this example, VisualStateManager is a property of a topmost UIElement.
It is working fine.

I created couples of other scenarios where VisualStateManager is not working, don't know why.

  • VisualStateManager is a property of topmost element in the custom control
  • VisualStateManager is a property of a child of a Panel in the custom control

Have look on different scenarios bellow:

  • Content1 is fine
  • Content2 is not working. It is not allowed to attach VisualStateManager to the child?
  • Content3 is working. This is workaround for not working Content2.
  • Content4 is not working. How am I suppose to use VisualStateManager in custom Control?
  • Content5 is not working. How am I suppose to use VisualStateManager in custom Control?
    /// <summary>
    /// VisualStateManager attached to the topmost Grid
    /// working as expected
    /// </summary>
    private Grid Content1() => new Grid()
        .HorizontalAlignment(HorizontalAlignment.Center)
        .VerticalAlignment(VerticalAlignment.Center)
        .RowDefinitions(
            new RowDefinition().Height(GridLength.Auto),
            new RowDefinition().Height(GridLength.Auto),
            new RowDefinition().Height(GridLength.Auto))
        .Children(
            new TextBlock()
            .Grid(g => g.Row(0))
            .Text("Hello Uno Platform!")
            .Name(out var textBlock),
            new Button()
            .Grid(g => g.Row(1))
            .Content("A")
            .Margin(5)
            .Name(c => c.Click += ((s, e) => VisualStateManager.GoToState(this, Common.StateA, true))),
            new Button()
            .Grid(g => g.Row(2))
            .Margin(5)
            .Content("B")
            .Name(c => c.Click += ((s, e) => VisualStateManager.GoToState(this, Common.StateB, true)))
        )
        .VisualStateManager(vsm => vsm.Group(group => group
            .State(Common.StateA, state => state
                .Setters(grid => grid.Background(Colors.AliceBlue))
                .Setters(textBlock, tb => tb.Text(Common.StateA)))
            .State(Common.StateB, state => state
                .Setters(grid => grid.Background(Colors.Orange))
                .Setters(textBlock, tb => tb.Text(Common.StateB)))));

    /// <summary>
    /// VisualStateManager attached to the child: TextBlock
    /// not working as expected
    /// </summary>
    private StackPanel Content2() => new StackPanel()
        .VerticalAlignment(VerticalAlignment.Center)
        .HorizontalAlignment(HorizontalAlignment.Center)
        .Children(
            new TextBlock()
            .Text("Hello Uno Platform!")
            .VisualStateManager(vsm => vsm.Group(group => group
                .State(Common.StateA, state => state
                    .Setters(ctrl => ctrl.Text(Common.StateA))
                    .Setters(ctrl => ctrl.Foreground(Colors.DarkBlue))
                )
                .State(Common.StateB, state => state
                    .Setters(ctrl => ctrl.Text(Common.StateB))
                    .Setters(ctrl => ctrl.Foreground(Colors.DarkRed))
                ))),
            new Button()
            .Content("A")
            .Name(c => c.Click += ((s, e) => VisualStateManager.GoToState(this, Common.StateA, true))),
            new Button()
            .Content("B")
            .Name(c => c.Click += ((s, e) => VisualStateManager.GoToState(this, Common.StateB, true))));


    /// <summary>
    /// VisualStateManager attached to the topmost StackPanel
    /// working as expected
    /// </summary>
    private StackPanel Content3() => new StackPanel()
        .VerticalAlignment(VerticalAlignment.Center)
        .HorizontalAlignment(HorizontalAlignment.Center)
        .Children(
            new TextBlock()
            .Name(out var textBlock)
            .Text("Hello Uno Platform!"),
            new Button()
            .Content("A")
            .Name(c => c.Click += ((s, e) => VisualStateManager.GoToState(this, Common.StateA, true))),
            new Button()
            .Content("B")
            .Name(c => c.Click += ((s, e) => VisualStateManager.GoToState(this, Common.StateB, true)))
        )
        .VisualStateManager(vsm => vsm.Group(group => group
                .State(Common.StateA, state => state
                    .Setters(textBlock, ctrl => ctrl.Text(Common.StateA))
                    .Setters(textBlock, ctrl => ctrl.Foreground(Colors.DarkBlue))
                )
                .State(Common.StateB, state => state
                    .Setters(textBlock, ctrl => ctrl.Text(Common.StateB))
                    .Setters(textBlock, ctrl => ctrl.Foreground(Colors.DarkRed))
            )));

    /// <summary>
    /// VisualStateManager attached to the panel's child: TextBlock in Custom Control
    /// not working as expected
    /// </summary>
    private MyControl4 Content4() => new MyControl4();

    /// <summary>
    /// VisualStateManager attached to the topmost StackPanel in Custom Control
    /// not working as expected
    /// </summary>
    private MyControl5 Content5() => new MyControl5();





/// <summary>
/// VisualStateManager attached to the TextBlock
/// not working as expected
/// </summary>
internal class MyControl4 : Control
{
    public MyControl4()
    {
        DefaultStyleKey = typeof(MyControl4);

        this
        .Style(new Style<MyControl4>()
            .Setters(s => s.Template(ctrl => CreateTemplate(ctrl))));
    }

    private UIElement CreateTemplate(MyControl4 ctrl) => new StackPanel()
        .VerticalAlignment(VerticalAlignment.Center)
        .HorizontalAlignment(HorizontalAlignment.Center)
        .Children(
            new TextBlock()
            .Text("Hello Uno Platform!")
            .VisualStateManager(vsm => vsm.Group(group => group
                .State(Common.StateA, state => state
                    .Setters(ctrl => ctrl.Text(Common.StateA))
                    .Setters(ctrl => ctrl.Foreground(Colors.DarkBlue))
                )
                .State(Common.StateB, state => state
                    .Setters(ctrl => ctrl.Text(Common.StateB))
                    .Setters(ctrl => ctrl.Foreground(Colors.DarkRed))
                )))
            ,
            new Button()
            .Content("A")
            .Name(c => c.Click += ((s, e) => VisualStateManager.GoToState(this, Common.StateA, true))),
            new Button()
            .Content("B")
            .Name(c => c.Click += ((s, e) => VisualStateManager.GoToState(this, Common.StateB, true)))
        );
}



/// <summary>
/// VisualStateManager attached to the StackPanel
/// not working as expected
/// </summary>
internal class MyControl5 : Control
{
    public MyControl5()
    {
        DefaultStyleKey = typeof(MyControl5);

        this
        .Style(new Style<MyControl5>()
            .Setters(s => s.Template(ctrl => CreateTemplate(ctrl))));
    }

    private UIElement CreateTemplate(MyControl5 ctrl) => new StackPanel()
        .VerticalAlignment(VerticalAlignment.Center)
        .HorizontalAlignment(HorizontalAlignment.Center)
        .Children(
            new TextBlock()
            .Name(out var textBlock)
            .Text("Hello Uno Platform!"),
            new Button()
            .Content("A")
            .Name(c => c.Click += ((s, e) => VisualStateManager.GoToState(this, Common.StateA, true))),
            new Button()
            .Content("B")
            .Name(c => c.Click += ((s, e) => VisualStateManager.GoToState(this, Common.StateB, true)))

        .VisualStateManager(vsm => vsm.Group(group => group
            .State(Common.StateA, state => state
                .Setters(textBlock, ctrl => ctrl.Text(Common.StateA))
                .Setters(textBlock, ctrl => ctrl.Foreground(Colors.DarkBlue))
            )
            .State(Common.StateB, state => state
                .Setters(textBlock, ctrl => ctrl.Text(Common.StateB))
                .Setters(textBlock, ctrl => ctrl.Foreground(Colors.DarkRed))
            )))
        );
}

Expected behavior

VisualStateManager should work in all scenarios, I guess, or I miss something...

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

MINIMAL REPRO PROJECT: VisualStateManagerApp.zip

STEPS TO REPRODUCE

  • run the app
  • click on Buttons "A" and "B" that are supposed to switch to different Visual States.
  • modify content -> Content1(), Content2(), Content3(), ... to test different scenarios:
    public MainPage() => this
        .Background(ThemeResource.Get<Brush>("ApplicationPageBackgroundThemeBrush"))
        .Content(Content1()); // modify here

Environment

Nuget Package (s):

Package Version(s):

"Uno.Sdk": "5.3.108"

Issue is observed on both frameworks Desktop and WindowsSdk

Affected platform(s):

  • iOS
  • macOS (AppKit)
  • Mac Catalyst
  • Android
  • WebAssembly
  • Windows
  • Skia (WPF)
  • Skia (GTK on Linux/macOS/Windows)
  • Skia (Linux Framebuffer)
  • Build tasks

Visual Studio:

  • 2019 (version: )
  • 2022 (version: )
  • Visual Studio Code (version: )
  • Rider Windows (version: )
  • Rider macOS (version: )

Relevant plugins:

  • Resharper (version: )

Anything else we need to know?

@kucint kucint added kind/bug Something isn't working triage/untriaged Indicates an issue requires triaging or verification. labels Sep 25, 2024
@MartinZikmund MartinZikmund self-assigned this Sep 30, 2024
@MartinZikmund
Copy link
Member

MartinZikmund commented Oct 2, 2024

The problem here is that the VisualStateManager should only be set on the direct child of a templated control, as documentation says:

Conceptually, VisualStateManager.VisualStateGroups contains the visual states for a control, as an immediate child tag of the template root in a control template.

And this doc page says:

When you use StateTriggers, ensure that the VisualStateGroup is declared under the first child of the root in order for the triggers to take effect automatically.

This limitation is based on WinUI and we cannot easily circumvent it. However, you can create custom controls that are nested and that will have their own VisualStateManager on the root child

@kucint
Copy link
Author

kucint commented Oct 2, 2024

I got it: VisualStateManager must be set on the direct child of a templated control.

But this is a case in "MyControl5" and the VisualStateManager is lot working anyway....
@MartinZikmund , could you please reopen issue. I can't do it...

@MartinZikmund MartinZikmund reopened this Oct 2, 2024
@MartinZikmund
Copy link
Member

You are right, missed that one. It seems that it may be some C# markup issue.

@MartinZikmund MartinZikmund removed their assignment Oct 2, 2024
@MartinZikmund MartinZikmund self-assigned this Oct 9, 2024
@MartinZikmund MartinZikmund removed their assignment Nov 25, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
kind/bug Something isn't working triage/untriaged Indicates an issue requires triaging or verification.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants