Xamarin Forms Migration to Uno Platform: Data Binding Techniques

In the latest of our regular series on migration, we will delve into the glue which holds your UI and business logic together – data binding. 

The tutorial demonstrates how to leverage Uno Platform to expand the reach of your existing C# WinUI 3 application across native mobile, web, and desktop platforms while reusing the business logic and UI layer. By making minimal changes to your existing app, you can create a pixel-perfect copy of the app that runs on multiple platforms.

What is Data Binding

When Windows Presentation Foundation (WPF) was introduced, it offered a new approach to create user interfaces using XAML markup. This allowed you to more cleanly separate your UI design from your app logic. 

The Model-View-View Model (MVVM) paradigm was created to provide a structure for this. To support this separation the user interface uses data binding to connect controls to public properties and commands exposed by your view model. The view model has no knowledge of how the UI appears or works, just what functionality to expose and the ability to update it as required. 

All subsequent XAML based frameworks have largely supported the same functionality and so generally it is easy to move view models and other app logic between frameworks. There are, however, some important considerations to making this work in the most efficient way. 

Desireable Properties

Many UI controls have a key property which describes their main purpose – a TextBlock for example has a Text property containing a string, a Checkbox has a boolean value describing whether it is selected. However, data binding in XAML is not limited to these key properties – you can data bind any dependency property, changing not just the main data, but the colors, fonts, sizing etc. of the control. Dependency properties are accessed through the GetValue and SetValue methods on DependencyObject. Most implementations will add strongly typed properties to the class too to make it easier to work with these properties via code. 

On top of these properties there is a related concept – the attached property. This allows one class to define a property which can be applied to another type. You’ll likely have come across these when positioning controls in a grid and using the Grid.Row and Grid.Column attached properties. 

Binding Markup

The key to data binding any property in XAML is a special markup extension called Binding. Markup extensions are special markup that allow you to pass things other than explicit values to a property. Alongside  its use for data binding you’ve probably seen StaticResource used to reference a resource with a unique key defined in XAML. 

Some Context

Dependency objects in Xamarin Forms all have a BindingContext property – this is the code which provides the values for the data binding. Minimally it is an instance of a class which provides some public properties containing the values to display in the view. Usually it will also implement INotifyPropertyChanged – this interface defines a standard event which notifies the view when property values change. When you use an MVVM framework it will include a base-class such as ObservableObject which contains the plumbing for this, so you don’t have to implement a lot of boilerplate code. 

WinUI follows the WPF and UWP convention of using DataContext as this property, but it functions in the same way. 

A-la-mode

Bindings support multiple modes which determine how the binding behaves when values are changed. The default Mode is OneWay which means that UI controls will update any time a data bound value in the view model is changed. Other values include OneTime – where the value is set once and there is no change handling, and TwoWay where changes in the UI are sent back to the view model – this is commonly used for data entry controls. 

Converters

Converters are classes used to convert a value into an appropriate format for the UI control. They implement the IValueConverter interface, which defines two methods to convert to and from another type. There is no strong typing used so you must ensure you use a converter in the right place to return the expected type for the bound control. If you are using the converter for a one-way binding you don’t need to write your own code for the ConvertBack method. 

Common scenarios include converting ranges of values into colors and converting Enum values into formatted strings. Whereas the view model should not have any dependency on UI types, the converter may well require this to function. When migrating code from Xamarin Forms to Uno Platform, it is likely you’ll need to make changes to converters. For example, if your converter returns a Xamarin.Forms.Color, it will need reworking to return a Windows.UI.Color instead. 

Formatting Strings

Xamarin Forms, like WPF before it, supports the StringFormat property on bindings. This allows you to tell the runtime how to format the data bound value. For example, if you had a numerical value which you want to display as a percentage you can apply this in the string format rather than using converters or exposing a string property in the view model with the value already formatted. 

On UWP, and WinUI there isn’t an equivalent to StringFormat so you must either use a converter to return the formatted string, expose a view model property with the string already formatted, or use x:Bind which we’ll look into shortly. 

Commands

Commands are actions which have a method to perform a function but also optionally a method to indicate whether they can be executed. For example, some actions only work in a particular state. When the command reports that it cannot execute the bound control will be disabled. The ICommand interface includes an event which is fired when the state may have changed, and the function will be reevaluated. Because of its roots in WPF, the interface is defined in the System.Windows.Input namespace but it doesn’t have any dependencies on other WPF APIs. Because of this it is easy to migrate code using commands with no code changes. 

Compiled Bindings

Normal bindings are resolved at runtime. This means they are not as performant as compiled code, and there is no validation at build time which can lead to unexpected errors. Xamarin Forms can use compiled bindings which give you this extra performance benefit and binding errors on build to let you fix issues sooner. There are two prerequisites to enable compiled bindings on Xamarin Forms – you must enable compiled XAML at the assembly level (this is enabled by default in the new project templates), you must also indicate the data type of the bound object in XAML. This is achieved via the x:DataType attribute which contains the namespace and class name. WinUI doesn’t have an equivalent option for Binding markup but instead introduces a new x:Bind markup extension which adds some other functionality as well as compiling the binding into code. 

x:Bind

x:Bind was introduced into UWP to support compiled binding as well as adding other more powerful options. You cannot simply replace every Binding with x:Bind to get this benefit though, as they behave differently. 

Firstly, x:Bind doesn’t use the DataContext to define the target but instead uses the page or control itself. This is fine for code-behind in the view, but we rarely want to do that. Instead, we need to provide a strongly typed property for the view model in the code behind. We can then use this in the x:Bind markup and we’ll get intellisense based on the public members of the type. 

				
					namespace DataBindingCodeBehind 
{ 
    public sealed partial class MainPage : Page 
    { 
        public MainPage() 
        { 
            this.InitializeComponent(); 
            this.ViewModel = new MyViewModel(); 
        } 
        public MyViewModel ViewModel { get; set; } 
    } 
} 
				
			

Therefore whereas previously you may have bound a control to a property on your view model like this:

				
					<TextBlock Text="{Binding Title}"/> 
				
			

You would now use:

				
					<TextBlock Text="{x:Bind ViewModel.Title}"/> 
				
			

The dotted notation is used to traverse the object hierarchy, so the following is valid notation

				
					<TextBlock Text="{x:Bind ViewModel.DateField.Month}"/> 
				
			

Secondly, because the OneWay and TwoWay binding modes require generating more code for change handling, the default binding mode with x:Bind is OneTime. Therefore if you find that your UI is not updating when values changed, the first place to check is that you’ve explicitly set the mode:

				
					<TextBlock Text="{x:Bind ViewModel.DateField.Month, Mode=OneWay}"/> 
				
			

x:Bind for String Formatting

Because x:Bind markup can contain functions you can include string formatting logic in your markup and not make any changes to your view model. Take the following example in Xamarin Forms: – 

				
					<TextBlock Text="{Binding Angle, StringFormat='The angle is {0:F0} degrees'}"/> 
				
			

The equivalent using x:Bind and formatting with a function would be

				
					<TextBlock Text="{x:Bind sys:String.Format('The angle is {0:F0} degrees', ViewModel.Angle)}"/> 
				
			

To use methods from the String type we have define the System namespace with the sys prefix in the page XAML:

				
					xmlns:sys="using:System" 
				
			

Alternatively, if you don’t need additional text and can rely on just the ToString() functionality of the value type you can use the even simpler notation: 

				
					<TextBlock Text="{x:Bind ViewModel.Value.ToString('F0')}"/> 
				
			

When you need to include quotes inside the markup you can use single quotes instead of the normal double quotes as these are used for the containing XAML attribute. 

x:Bind to Methods

x:Bind can be used to bind Commands to controls with a Command property. However, as we saw in the previous section the x:Bind syntax can contain methods. This brings up another interesting behavior – you can bind control events directly to methods in code, without using commands. There is also a lot of flexibility here in the methods you use because they don’t have to exactly match the signature of the specific method. The method can have no parameters or contain the same number of parameters as the event signature if they are types which the arguments can be cast to. 

There is a lot more to x:Bind than can be covered in this post. We’ve explorer further in our article on advanced binding techniques. 

Linking It All Together

We can see from the various options above that generally you can migrate code from Xamarin Forms to Uno Platform without significant changes to your logic in view models. Where you have used converters, you may need to make changes to UI specific types. To take advantage of compiled bindings you’ll need to move to the x:Bind markup and you may need to add a strongly typed property to your page to hold your view model. You can use x:Bind to remove converters and perform string formatting and take advantage of compiled code and build time binding errors. 

About Uno Platform

For those new to the Uno Platform, it allows for creating pixel-perfect, single-source C# and XAML apps that run natively on Windows, iOS, Android, macOS, Linux and Web via WebAssembly. In addition, it offers Figma integration for design-development handoff and a set of extensions to bootstrap your projects. Uno Platform is free, open-source (Apache 2.0), and available on GitHub.

Next Steps

To upgrade to the latest release of Uno Platform, please update your packages to 4.9 via your Visual Studio NuGet package manager! If you are new to Uno Platform, following our official getting started guide is the best way to get started. (5 min to complete)

Share this post: