Getting Started with UserControl and VisualStatesManager

In the previous session we learn how to Custom your own C# Markup and Learn how to use Toolkit.

Now we will learn how to use the UserControl and the VisualStateManagers.

For this sample you can use same project we start on the how to Create your own C# Markup with Toolkit

UserControl

A UserControl is a reusable user interface component that allows you to group related visual elements and behavior into a single building block. It provides a way to create custom components that can be used in multiple parts of the application.

Changing UI to have the UserControl

  • In the Shared Project we need to add a UserControl.

  • The purpose of the new UserControl will be to have the Chips in a single place, so we can use the same code on the MainPage and the SecondPage.

    C# Markup

    Create a new Uno Platform UserControl. On the SharedProject Right click on the project name -> Add -> Class -> Inform the Name SampleUserControl and click Add.

    Now we can change the content of the SampleUserControl.cs to have the Chips, copy the ChipGroup from the MainPage.cs to this file..

    this.Background(ThemeResource.Get<Brush>("ApplicationPageBackgroundThemeBrush"))
        .Content(
                new ChipGroup()
                .Name(out var chipGroup)
                .Items(
                    new Chip()
                        .Margin(5)
                        .Content("First Chip")
                        .Background(new SolidColorBrush(Colors.LightBlue))
                        .Name(out var navigationChip),
                    new Chip()
                        .Margin(5)
                        .Content("Chip 2")
                        .Style(new Style<Chip>()
                                    .Setters(s => s.Foreground(new SolidColorBrush(Colors.Red)))
                            ),
                    new Chip()
                        .Margin(5)
                        .Content("Chip 3")
                        .Name(out var chipElement)
                )
            );
    

    And move the EventHandler from the MainPage.cs to the SampleUserControl.cs.

    
        chipElement.Checked += (sender, e) =>
        {
            if (sender is Chip chip)
            {
                chip.FontSize(18);
            }
        };
        chipElement.Unchecked += (sender, e) =>
        {
            if (sender is Chip chip)
            {
                chip.FontSize(14);
            }
        };
    

    And change the ChipGroup on the MainPage to the new UserControl.

    .Children(
        new Button()
            .Content("Go to Second Page")
            .Name(out var navigationButton),
        new SampleUserControl()
    )
    

    We can do the same on the SecondPage, check how will be the full code on the SecondPage

    namespace MySampleToolkitProject;
    
    public sealed partial class SecondPage : Page
    {
        public SecondPage()
        {
            this
                .Background(ThemeResource.Get<Brush>("ApplicationPageBackgroundThemeBrush"))
                .Content(
                    new StackPanel()
                        .Children(
                            new NavigationBar().Content("Title Second Page")
                                .VerticalAlignment(VerticalAlignment.Top)
                                .HorizontalAlignment(HorizontalAlignment.Left),
                            new StackPanel()
                                .Margin(0, 50, 0, 0)
                                .VerticalAlignment(VerticalAlignment.Center)
                                .HorizontalAlignment(HorizontalAlignment.Center)
                                .Children(
                                    new Button()
                                        .Content("Go to Main Page")
                                        .Name(out var navigationButton),
                                    new SampleUserControl()
                                )
                        )
                );
            navigationButton.Click += (s, e) =>
            {
                Frame.Navigate(typeof(MainPage));
            };
        }
    }
    

VisualStateManagers

The VisualStateManager is a class that allows you to define and manage visual states for controls or elements.

With VisualStateManager, we can define states such as "Normal", "Pressed","PointerEntered","PointerExited", "Focused," or any other custom states you want to handle. For each state, you can set various properties, such as background color, font size, visibility, or any other property relevant to the control's appearance or behavior.

Changing UI to have the VisualState

We need to add the VisualStateManager to the top element of the page, usually a Grid or StackPanel. With this we can control events and changes.

  • For this sample we will use the VisualStateManager for change the Background color and the Width of some element.

    C# Markup
    Add VisualStateManager to the Page

    In the Shared Project open the file MainPage.cs and change the content to have the VisualStateManager.

    .VisualStateManager(builder =>
                builder.Group("ButtonStates",
                    groupBuilder =>
                        groupBuilder
                        .State("PointerEntered",
                            stateBuilder => stateBuilder
                                .Setters(btn, e => e.Width(500))
                                .Setters(navigationButton, e => e.Background(new SolidColorBrush(Colors.Blue)))
                        ).State("PointerExited",
                            stateBuilder => stateBuilder
                                .Setters(btn, e => e.Width(100))
                                .Setters(navigationButton, e => e.Background(new SolidColorBrush(Colors.White)))
                        )
                    )
            )
    

    We will change the navigationButton (the same one that has been assigned for controlling the navigation) and change its background color now. And change the value of the Width of the Button that will be used on the EventHandler. You can add the Button before the new SampleUserControl().

    new Button()
        .Width(200)
        .Height(40)
        .Content("VisualStateManager Test")
        .Name(out var btn),
    

    And to handle the Event Handlers we need to create the Event Handlers so that the events are fired and the actions happen.

    
    btn.PointerEntered += (sender, e) =>
    {
        VisualStateManager.GoToState(this, "PointerEntered", true);
    };
    
    btn.PointerExited += (sender, e) =>
    {
        VisualStateManager.GoToState(this, "PointerExited", true);
    };
    
    

    Now Run the Project and test it.

    Add VisualStateManager to the UserControl

    In the Shared Project open the file SampleUserControl.cs and change the content to have the VisualStateManager. For the User Control, we will add some new Grid Element attach the VisualStateManager to it.

    new Grid()
    .RowDefinitions<Grid>("Auto, *")
    .Children(
    ...
    )
    .VisualStateManager(builder =>
                builder.Group("ButtonStates",
                    groupBuilder =>
                        groupBuilder
                        .State("PointerEntered",
                        stateBuilder => stateBuilder
                            .Setters(visualStateButtonChips, e => e.Width(500))
                        ).State("PointerExited",
                            stateBuilder => stateBuilder
                                .Setters(visualStateButtonChips, e => e.Width(200))
                        )
                    )
            )
    

    We will change the value of the Width of the Button that will be used on the EventHandler. You can add the Button in the Second Row of the new Grid.

    new Button()
        .Grid(row: 1)
        .Content("Visual State on UserControl")
        .Name(out var visualStateButtonChips)
    

    And to handle the Event Handlers we need to create the Event Handlers so that the events are fired and the actions happen.

    
    visualStateButtonChips.PointerEntered += (sender, e) =>
    {
        VisualStateManager.GoToState(this, "PointerEntered", true);
    };
    
    visualStateButtonChips.PointerExited += (sender, e) =>
    {
        VisualStateManager.GoToState(this, "PointerExited", true);
    };
    

Try it yourself

Now try to change your MainPage to have different layout and test other attributes and elements.

Next Steps