Using Compiled Binding (x:Bind) with a ViewModel
The new compiled binding or x:Bind functionality in Universal Windows Platform (UWP) Apps for Windows 10 offers some real performance improvements over the classic binding approach. In most cases implementing this new feature is as simple as changing your Binding statements to x:Bind.
<!-- Change this --> <TextBox Header="Username" Text="{Binding Username, Mode=TwoWay}"/> <!-- to this --> <TextBox Header="Username" Text="{x:Bind Username, Mode=TwoWay}"/>
In my case I was using MVVM to bind properties to my view via a ViewModel. Because the property Username is on my MainPageViewModel and not my MainPage code behind class, the compiler wasn’t able to find the property Username.
In this case I received an error message at compile time stating “Invalid binding path ‘Username’ : Property ‘Username’ can’t be found on type ‘MainPage’”. This is because the compiled binding gets evaluated at compile-time and it cannot find any property on the partial class MainPage to match.
In module 4 of Jerry Nixon and Andy Wigley’s MVA Course, A Developers Guide to Windows 10, Andy mentioned a technique to automatically bind a strongly-typed property called ViewModel on the Page code-behind like this.
public sealed partial class MainPage : Page { public MainPage() { this.InitializeComponent(); DataContextChanged += (s, e) => { ViewModel = DataContext as MainPageViewModel; }; } public MainPageViewModel ViewModel { get; set; } }
Now every time the DataContext changes, the ViewModel property gets updated. You also have to change the Binding to point to the new strongly typed ViewModel property (note the new “ViewModel.” in the binding expression).
Text="{x:Bind ViewModel.Username}"
This fixed my problem, but I wanted a solution that I could apply to a base class and leverage across all pages. In order to maintain the strongly typed nature of the base class I had specify the ViewModel type somewhere in the code behind. I couldn’t see any way around that. At the same time, I wanted to minimize the occurences where this type had to be specified. After a couple variation using Lambdas and Expressions, which ultimately led to reflection of some type. I opted for full reflection approach based on convention. The convention I settled on required a property called ViewModel on the concrete page. Then on the BasePage I just look for this type and set it whenever the DataContext changes.
public sealed partial class MainPage : BasePage { public MainPage() { this.InitializeComponent(); } public MainPageViewModel ViewModel { get; set; } }
In order to achieve this, the BasePage logic needs to be a bit more complex, as it’s dealing with Reflection.
public class BasePage : Page { private const string ViewModelPropertyName = "ViewModel"; public BasePage() { DataContextChanged += (s, e) => { SyncVmWithDataContext(); }; } public void SyncVmWithDataContext() { var vmProperty = this.GetType().GetProperty(ViewModelPropertyName); if (vmProperty != null) { vmProperty.SetMethod.Invoke(this, new[] { DataContext }); } } }
Another alternative is to make ViewModelPropertyName a public property that can be set in the XAML and use that property name to override the convention.
I hope you like this approach. All the code can be found on my github page at http://github.com/bendewey/ViewModelCompiledBinding