Binding 101

Whether you've been around frameworks that use bindings for a while or you're brand new to the concept, this guide will help you understand the basics of Bindings.

public partial class MainPage : Page
{
    public MainPage()
    {
        this.DataContext<MyViewModel>((page, vm) => page
            .Content(
                new TextBlock()
                    .Text(vm.Message) // This will throw a Null Reference Exception
            )
        );
    }
}

At first glance the code sample above may look correct and it will compile. However it is important to consider that within the DataContext method the vm property will always be null. This method is provided to help you provide strongly typed bindings for your ViewModel. To fix this we need to provide a binding expression to the Text method.

public partial class MainPage : Page
{
    public MainPage()
    {
        this.DataContext<MyViewModel>((page, vm) => page
            .Content(
                new TextBlock()
                    .Text(() => vm.Message)
            )
        );
    }
}

Alternatively, we may want to reduce the overhead on the binding by only having it apply once to set the Text property and not be watched for changes. For example, if we're setting a title or something that doesn't change, we can use the Mode method to set the binding mode to OneTime.

public partial class MainPage : Page
{
    public MainPage()
    {
        this.DataContext<MyViewModel>((page, vm) => page
            .Content(
                new TextBlock()
                    .Text(x => x
                        .Binding(() => vm.Title)
                        .Mode(BindingMode.OneTime)
                    )
            )
        );
    }
}

The BindingMode additionally provides you with some shorthand methods to set the BindingMode to OneWay, TwoWay or OneTime.

public partial class MainPage : Page
{
    public MainPage()
    {
        this.DataContext<MyViewModel>((page, vm) => page
            .Content(
                new TextBlock()
                    .Text(x => x
                        .Binding(() => vm.Title)
                        .OneTime()
                    )
            )
        );
    }
}

Sometimes, you may want to update the format of the value before it is displayed. In this case, you can use the Convert method to provide a converter to the binding.

public partial class MainPage : Page
{
    public MainPage()
    {
        this.DataContext<MyViewModel>((page, vm) => page
            .Content(
                new TextBlock()
                    .Text(x => x
                        .Binding(() => vm.Query)
                        .Convert(query => $"Search: {query}")
                    )
            )
        );
    }
}

You can also use the shorthand version of this to simply provide the binding and a converter.

public partial class MainPage : Page
{
    public MainPage()
    {
        this.DataContext<MyViewModel>((page, vm) => page
            .Content(
                new TextBlock()
                    .Text(() => vm.Query, query => $"Search: {query}")
            )
        );
    }
}

Similarly, you may need to convert the value back to the original type when the value is updated. In this case you can use the ConvertBack method to provide a converter to the binding.

public partial class MainPage : Page
{
    public MainPage()
    {
        this.DataContext<MyViewModel>((page, vm) => page
            .Content(
                new TextBox()
                    .Text(x => x
                        .Binding(() => vm.Enabled)
                        .Convert(enabled => $"Enabled: {enabled}")
                        .ConvertBack(text => bool.TryParse(text.Replace("Search: ", ""), out var enabled) ? enabled : false)
                    )
            )
        );
    }
}

Binding to other sources

Sometimes you may want or need to bind to your ViewModel. The first case we'll take a look at is one in which we want to bind to a property of another element. In this case we will use the Source method to update our binding so that it will bind to a named element in the Visual Tree.

public partial class MainPage : Page
{
    public MainPage()
    {
        this.DataContext<MyViewModel>((page, vm) => page
            .Content(
                new TextBox()
                    .Name(out var searchBox)
                    .Text(() => vm.Query),
                new TextBox()
                    .Text(x => x
                        .Source(nameof(searchBox))
                        .Binding(() => searchBox.Text))
            )
        );
    }
}

Sometimes you may want to have a custom control that uses it's own ViewModel to help keep your ViewModel's clean and simple.

In the event that you need to grab some context to create the ViewModel for the Child control's DataContext you may put these various concepts together to create a DataContext for the custom control using a binding on the parent's DataContext while updating the Binding to use the current Page as the source.

public partial class MainPage : Page
{
    public MainPage()
    {
        this.DataContext<MyViewModel>((page, vm) => page
            .Content(
                new MyCustomControl()
                    .DataContext(x => x
                        .Source(this)
                        .Binding(() => this.DataContext)
                        .Convert(dataContext => {
                            if (dataContext is MyViewModel myViewModel)
                            {
                                return new MyCustomControlViewModel(myViewModel.SomeContext);
                            }
                            return null;
                        })
                        .OneTime()
                    )
            )
        );
    }
}

Next Steps

Learn more about: