Build together, debug together. Join the community on Discord.→

XAML Fundamentals for Web & Mobile: Advanced Binding Techniques

XAML platforms, from WPF and Silverlight to Uno and WinUI, support the MVVM pattern by separating the View described declaratively in a XML dialect with the application logic in code. The VM in the name represents the View Model; this is the code responsible for exposing functionality to which the View can bind without having any knowledge of the View itself. In addition, the platform provides data-binding functionality allowing the View to dynamically display data from the View Model and react to changes.  

By declaring namespaces in your XAML file, you can declaratively use a surprising amount of your code and logic beyond the platform’s controls and components. In addition, some limitations with the code can be referred to from XAML – any class must have a default constructor, controls bound to a DataContext must bind to Properties on that source, or in the case of action items like Buttons, an ICommand. 

However, in the modern XAML found in Uno and WinUI, there is an additional markup extension called x:Bind, which goes beyond the basic support for binding found in all previous XAML flavors. 

By the end of this tutorial or following the sample code, you will have learned when to use x:Bind and produced the following screens.

When to use x:Bind

In the WPF era, a utility class called ObjectDataProvider added functionality for more advanced data binding scenarios. 

ObjectDataProvider doesn’t exist in WinUI or Uno; however, many of its capabilities can be replicated with the x:Bind markup extension. Because x:Bind declarations are compiled, they are very efficient, and types are checked at build time. This provides a simple and reliable way to support more complex binding relationships. 

While x:Bind supports the same functionality as Binding and much more, you won’t necessarily want to replace every use of Binding with x:Bind. One of the significant differences is that Binding always refers to the DataContext of the View. However, x:Bind, by default, uses the scope of the View itself, so any property or method you reference with x:Bind must refer to an element in the View or exist within the code behind. This often means declaring a strongly typed property for your View Model in your View code. It allows you to specify, via namespaces, any other static method or property that can be pretty useful. 

Passing Parameters to the Constructor

There are many ways to instantiate a View Model, either from code behind or even in the XAML view itself. One of the limitations of the latter approach is that there is no mechanism in XAML to specify parameters for the constructor. 

Knowing that x:Bind allows us to specify functions, we are no longer limited to establishing “XAML safe” classes. For example, you cannot refer to a constructor directly as an x:Bind function, but you can call a static method that itself calls the constructor and returns an instance of the object. 

				
					public static PlanetViewModel Create(string planetName) 
        { 
            return new PlanetViewModel(planetName); 
        } 
				
			

Using Dependency Injection

While this provides a method to complex code any data source from your XAML, you will probably want a more structured approach to retrieving View Models in anything beyond a simple application. The standard pattern is to have an Interface representing each View Model and then assign the actual class instance at launch by registering it with a Dependency Injection container. This allows you to register your services and view models as long-lived singletons or single-use instances, which can be released once finished.  

Dependency injection frameworks support passing registered types into constructors. When you have a View Model constructor which requires another dependency, the framework will create an instance or pass an existing object when constructing the View Model. It’s beyond the scope of this article to go into Dependency Injection in total, but you could start by looking at the documentation here. 

Binding to a Method

Using the {Binding} syntax allows you to bind properties in your view to properties in your binding source (your View Model). However, it doesn’t allow for more complex scenarios, such as binding to a method that returns a value. In WPF, you could use the ObjectDataProvider helper class to create a binding to a method and specify parameters. As with the constructor scenario, you can use x:Bind to bind to any instance or static method in your code. In addition, you can specify multiple parameters for a function, which goes beyond the methods supported via ICommand for buttons, etc.  

By default, x:Bind uses the OneTime binding mode. This means that the value will be populated once when creating the view and will ignore any further changes. However, you can specify OneWay to allow the value to be recalculated when your View Model supports INotifyPropertyChanged. Where you would typically use the PropertyChanged event to signal that a property value has changed, you can also raise the event with the name of your method; this will cause the x:Bind function to be re-evaluated. 

Because the x:Bind function is evaluated at compilation time, you can use methods that return the expected type for the bound property and avoid the use of IValueConverters. In addition, because x:Bind supports instance and static methods, it can be used to call built-in framework methods to perform string formatting, for example, and to perform multi-binding. 

				
					<TextBlock Grid.Row="4" Grid.Column="1" Text="{x:Bind ViewModel.CalculateDensity(ViewModel.Planet.Mass, ViewModel.Planet.Diameter), Mode=OneWay}"/> 
				
			

Specifying a function that accepts two properties gives you an equivalent to multi-binding. Changes in either of these properties will cause the function to be re-evaluated with the new values. 

One function limitation is that x:Bind will only work with synchronous methods. This is because an asynchronous method will have a return type of Task rather than the T required for your View property. However, it is possible to work around this by writing an additional helper method to wrap an async method.  

				
					public string GetResultOfAsyncMethod() 
        { 
            return AsyncHelpers.RunSync(ViewModel.SimulateLongRunningMethodAsync); 
        } 
				
			

However, when binding to a property, such as TextBlock.Text, a long-running method, would cause a delay in rendering the view. In this case, you would need to use a property instead and have the OnPropertyChanged called when the value changes. 

Binding to functions can be used in both directions too. You specify the BindBack property in your x:Bind statement. This should specify a method that accepts a single argument of the same type that your “getter” method returns. 

Next Steps

You can read more about Uno’s support for the x:Bind syntax here, which follows the WinUI functionality. 

For your convenience, we’ve made the code sample available here. Explore our samples and discover how you can Get Started with Uno Platform to build your desktop, mobile, and web apps. 

Tags:

Share this post:
Related Posts

Uno Platform 5.2 LIVE Webinar – Today at 3 PM EST – Watch