C# Markup

Overview

C# Markup is a declarative, fluent-style syntax for defining the layout of an application in C#. With C# Markup you can define both the layout and the logic of your application using the same language. C# Markup leverages the same underlying object model as XAML, meaning that it has all the same capabilities such as data binding, converters, and access to resources. You can use all the built-in controls, any custom controls you create, and any 3rd party controls all from C# Markup.

You will quickly discover why C# Markup is a developer favorite with:

  • A Declarative syntax
  • Strongly typed data binding
  • Intellisense and compile-time validation
  • Refactoring support
  • Custom Controls and 3rd party libraries

Let's jump in and take a look at a simple sample that displays 'Hello Uno Platform!' in the center of the screen:

public sealed partial class MainPage : Page
{
    public MainPage()
    {
        this.Content
        (
            new StackPanel()
                .VerticalAlignment(VerticalAlignment.Center)
                .HorizontalAlignment(HorizontalAlignment.Center)
                .Children
                (
                    new TextBlock()
                        .Text("Hello Uno Platform!")
                )
        );
    }
}

The first thing you will notice with C# Markup is that there is nothing special to learn. You can simply create a new instance of the controls you want to work with (eg new TextBlock()) and set properties using the generated extension method with the same name (eg .Text("Hello Uno Platform!')).

Unlike XAML which is littered with string constants, C# Markup provides a strongly typed API for setting properties. For example, in XAML you would set HorizontalAlignment with HorizontalAlignment="Center", but in C# Markup you would use the HorizontalAlignment enum with HorizontalAlignment.Center, ensuring that you get compile time validation and intellisense for all the available values.

Getting Started

Let's take a look at how to get started with C# Markup. We'll start with a simple sample that displays a counter and a button that increments the counter by a step size. The sample will have a ViewModel that has a Count and a Step property, as well as an IncrementCommand that increments the Count by the Step when executed.

Constructor and Properties

We'll start with creating a control and setting some properties, as shown in the following example that creates a TextBlock. The fluent API allows you to chain together multiple properties, as shown in the following example that sets the Margin, TextAlignment, and Text properties.

new TextBlock()
    .Margin(12)
    .TextAlignment(TextAlignment.Center)
    .Text("Counter: 0")

Similar to the HorizontalAlignment property mentioned earlier, the TextAlignment property is set using an enum rather than a string constant. This ensures that you get compile time validation and intellisense for all the available values.

In XAML you would set the Margin property using a single number (for example <TextBlock Margin="12" />), but the actual Margin property on the TextBlock class requires a Thickness type, resulting in the following C# Markup:

new TextBlock().Margin(new Thickness(12))

However, C# Markup provides automatic type conversion for common types such as Thickness, as well as a number of other types such as Brush, Color, CornerRadius, FontFamily, Geometry and ImageSource. This means that you can set the Margin property using a single number, as shown in the following example.

new TextBlock().Margin(12)

Data Binding

Since our counter example requires the value of the TextBlock to be updated each time the counter changes, we'll need to use data binding. C# Markup provides a strongly typed API for data binding, as shown in the following example that binds the Text property to the Count property of the ViewModel.

new TextBlock().Text(() => vm.Count)

At this point, you might be wondering what the vm is. The vm is a placeholder reference for the ViewModel type that you provide to the DataContext extension.

.DataContext(new MainViewModel(), (page, vm) => page
    .Content(
        new TextBlock().Text(() => vm.Count)
    )
);

We refer to vm as a placeholder because at this point it is not actually a reference to the ViewModel. It is simply a placeholder that allows us to provide a strongly typed API for data binding. Rather than invoking the code vm.Count when the TextBlock is created, the expression tree, () => vm.Count is used to create a binding expression that will be evaluated when the DataContext for the TextBlock is set.

In the above example, an instance of MainViewModel is provided to the DataContext extension method. If an instance of the ViewModel isn't available at the time the DataContext is set, you can provide the type of the ViewModel instead, as shown in the following example.

.DataContext<MainViewModel>((page, vm) => page
    .Content(
        new TextBlock().Text(() => vm.Count)
    )
);

Currently, the data binding is directly on the Count property meaning that the text shown in the TextBlock will just be the value of the Count property. However, we want to display the text "Counter: {value}" where {value} is the value of the Count property. To do this we can provide a delegate that will be invoked each time the value of the Count property changes, as shown in the following example.

new TextBlock().Text(() => vm.Count, count => $"Counter: {count}")

In some cases, you need more control over the binding, such as when you need to change the binding mode or provide a converter. In these cases, you can use the Bind extension method, as shown in the following example that two-way data binds the Step property to the Text property of a TextBox.

new TextBox().Text(x => x.Bind(() => vm.Step).TwoWay()),

Resources

C# Markup provides a strongly typed API to get, create, and add both static and theme resources. For example, to set the Background to the theme resource, get the ApplicationPageBackgroundThemeBrush from the default WinUI Fluent theme:

this.Background(ThemeResource.Get<Brush>("ApplicationPageBackgroundThemeBrush"));

Counter Sample

Putting all the pieces we've just covered together, we have the layout of a counter application that will increment the Count by the Step.

public sealed partial class MainPage : Page
{
    public MainPage()
    {
        this.DataContext(new MainViewModel(),(page, vm) => page
            .Background(ThemeResource.Get<Brush>("ApplicationPageBackgroundThemeBrush"))
            .Content(new StackPanel()
                .VerticalAlignment(VerticalAlignment.Center)
                .Children(
                    new Image()
                        .Width(150)
                        .Height(150)
                        .Margin(12)
                        .HorizontalAlignment(HorizontalAlignment.Center)
                        .Source("ms-appx:///Assets/logo.png"),
                    new TextBox()
                        .Margin(12)
                        .HorizontalAlignment(HorizontalAlignment.Center)
                        .PlaceholderText("Step Size")
                        .Text(x => x.Bind(() => vm.Step).TwoWay()),
                    new TextBlock()
                        .Margin(12)
                        .HorizontalAlignment(HorizontalAlignment.Center)
                        .TextAlignment(TextAlignment.Center)
                        .Text(() => vm.Count, txt => $"Counter: {txt}"),
                    new Button()
                        .Margin(12)
                        .HorizontalAlignment(HorizontalAlignment.Center)
                        .Command(() => vm.IncrementCommand)
                        .Content("Click me to increment Counter by Step Size")
                )
            )
        );
    }
}

Conclusion

This overview provides a brief introduction to C# Markup, including how to create controls, set properties, and use data binding and resources. For a comprehensive guide on using C# Markup to build a complete application, check out the full Counter tutorial. This tutorial offers two variants: C# Markup + MVUX and C# Markup + MVVM.

Next Steps

Learn more about: