Compiled DataBinding in Windows Universal Applications (UAP)

Compiled DataBinding in Windows Universal Applications (UAP)

This question on stackoverflow which references the Windows 10 jumpstart videos (http://www.microsoftvirtualacademy.com/training-courses/a-developers-guide-to-windows-10-preview?prid=ch9courselink) raises the topic of compiled data bindings. Currently there is no documentation about this feature and I doubt that we’ll see much out of Microsoft prior to Build. However, the x:Bind syntax is available in the insiders preview of the Windows 10 SDK so I figured I’d explore it a little.

Let’s start with a simple expression:

<TextBlock Text=”{x:Bind}” />

When you add this to a page and run it, you’ll see the Type name of the page displayed in the TextBlock (eg CompiledDataBindingSample.MainPage). In order to examine this further let’s create a simple custom control with a dependency property and corresponding change handler:

public class CustomControl : Control
{
    Windows.UI.Xaml.Markup.IComponentConnector x;

    public string Test
    {
        get { return (string)GetValue(TestProperty); }
        set { SetValue(TestProperty, value); }
    }

    // Using a DependencyProperty as the backing store for Test.  This enables animation, styling, binding, etc…
    public static readonly DependencyProperty TestProperty =
        DependencyProperty.Register(“Test”, typeof(string), typeof(CustomControl), new PropertyMetadata(null, TestChanged));

    private static void TestChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var newData = (d as CustomControl).Test;
        Debug.WriteLine(newData.GetType().Name);
    }
}

Now let’s add an instance of this control onto the page and data bind to the Test property:

<local:CustomControl Test=”{x:Bind}” />

When this is run we can set a breakpoint on the Debug.WriteLine and we can examine the newData object. What’s interesting is that the newData object is the instance of the page that is hosting the control. In other words, the value being passed into the binding expression is the page itself (in this case an instance of MainPage) – this explains why in the TextBlock showed the type name, as this is the default ToString() value. This is interesting in itself as it means that the context for data binding with x:Bind is the page itself, in contrast to the default DataContext of null when using Binding.

The other thing that’s worth looking at is the Call Stack window. The first two frames make sense, being the set and change handler for the dependency property but then it gets interesting as it references a class called MainPage_obj1_Bindings with methods such as SetValue_obj3_Test, Update_, InitializeCore, Initialize etc.

image

At this point I think it’s time to go take a look at what’s been generated when the application was compiled. In this case we’ll use ILSpy to get a list of the classes that make up the application. As you can see from this screenshot MainPage has a couple of nested types, MainPage.IMainPage_Bindings and MainPAge.MainPage_obj1_Bindings, along with a field/property combination that exposes an instance of MainPage.IMainPage_Bindings. There is also an additional method, Connect, which has been injected into the MainPage during compilation.

image

A further examination of the base types collection shows that in addition to inheriting from Page, MainPage implements Windows.UI.Xaml.Markup.IComponentConnector, which no surprises defines a method, Connect(int, object). So, what does this method do? Using ILSpy to inspect the contents on the Connect method we can see that it generates an instance of the MainPage_obj1_Bindings class and associates it with the elements on the page (in this case the TextBlock and the CustomControl). Clearly this method is going to be invoked during the initialization phase of a page.

Now, let’s turn our attention to the MainPage_obj1_Bindings class. It would appear that this class has instance variables for each of the elements that has an x:Bind expression. In this case it has variables obj2 (TextBlock) and obj3 (CustomControl). It also has some generated methods such as SetValue_obj2_Text which explicitly sets the Text value on the TextBlock. This makes me think back to a time before data binding where properties had to be explicitly set in code. Whilst data binding has been an effective way for developers to declaratively wire up properties to underlying data, it did add a significant overhead and subsequent performance hit – for large data sets this can be quite significant and hard to work around. By the looks of these generated methods, it would appear that by converting the data binding expression into compiled code, some of the performance overhead of data binding can be overcome.

Ok, so now that we’ve taken a bit of a look at some of the aspects of compiled data binding, let’s go back to XAML and take a look at extending the binding expression to include a path. In most cases we’ll want to bind attributes on the visual elements to properties on a view model. The question is how to do this using x:Bind. Let’s see what happens when we add ViewModel to the binding expression:

<TextBlock Text=”{x:Bind ViewModel}” />

Normally, if you add a path to a binding expression that isn’t valid it will compile and run without any issues. In this case, there is no ViewModel property on the MainPage, so when the application is compiled the following build error is generated:

Invalid binding expression ‘MyProperty’ : Unrecognized property ‘MyProperty’ at offset ‘0’

Let’s add a ViewModel property to the MainPage:

public MainViewModel ViewModel { get; set; }

public MainPage()
{
    ViewModel = new MainViewModel();
    this.InitializeComponent();

}

The MainViewModel class exposes a property, HelloText, and implements the INotifyPropertyChanged interface. Of course, normally this would be implemented in a base class with an OnPropertyChanged or RaisePropertyChanged helper method.

public class MainViewModel : INotifyPropertyChanged
{
    private string hello = “Hello World”;
    public string HelloText
    {
        get { return hello; }
        set
        {
            hello = value;
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(“HelloText”));
            }
        }
    }
    public event PropertyChangedEventHandler PropertyChanged;
}

In order to reference the HelloText property, we’ll extend the binding expression:

<TextBlock Text=”{x:Bind ViewModel.HelloText}” />

Running this appears to work with the text “Hello World” appearing in the TextBlock. Let’s try updating the HelloText. A simple Run method will update the HelloText property every second.

public async void Run()
{
    var i = 0;
    while (true)
    {
        await Task.Delay(1000);
        HelloText = “Hello ” + i++;
    }
}

Unfortunately this doesn’t seem to do anything, with the TextBlock not updating. This behaviour is consistent of a OneTime data binding. Let’s add Mode=OneWay to the binding expression.

<TextBlock Text=”{x:Bind ViewModel.HelloText, Mode=OneWay}” />

Now the TextBlock updates every second as we’d expect. So it appears that unlike a traditional Binding, the x:Bind expression defaults to a OneTime mode, instead of OneWay. ILSpy also indicates that the MainPage_obj_Bindings has a sub-class, MainPage_obj1_Listener, which is designed to hook up to the INotifyPropertyChanged.PropertyChanged event. This again will help ensure updates are passed to the appropriate elements without any need for Reflection based calls.

Leave a Reply

Your email address will not be published. Required fields are marked *