MVVM event to command binding #6: Specifics of the UWP platform

As a follow-up to the series of articles targeting XAML event binding, I promised to give an overview of the new possibilities UWP’s {x:Bind} syntax brings us:

As I’ve mentioned before, the statement that direct binding of event handlers is not possible is not strictly true when using the Universal Windows Platform. Simply using SomeEvent="{Binding MyEventHandler}" still does not work, but instead we can rely on the {x:Bind} syntax. However, a few things are important to notice – especially if you’re used to bind commands in the traditional way:

  • To begin with, {x:Bind} bindings do not target a control’s DataContext (regardless of whether we’re binding to commands, events, or data). Instead, if we do not specify an explicit target, bindings are resolved in the current view’s code-behind. This means if we define an event-handler in code-behind:

    public sealed partial class MainPage : Page
    {
        private void Button_OnTapped(object sender, TappedRoutedEventArgs e)
        {
            // add event handler logic here
        }
    }
    

    …we can bind to it from in XAML:

    <Button Tapped="{x:Bind Button_OnTapped}">tap me</Button>
    

    While this precise example has no advantage over traditional event handler reference (<Button Tapped="Button_OnTapped">), the mechanism of binding to event handlers allows us to use the MVVM pattern: If you define a ViewModel somewhere in your View class (I presented one way of doing so in my blog post MVVM and compiled bindings), you can directly map View events to the ViewModel method:

    <Button Tapped="{x:Bind ViewModel.DoSomething}">tap me</Button>
    
  • The approach of just relocating event handler methods to ViewModels and binding to them will quickly get you into trouble: ViewModels typically reside in some PCL layer in order to be used on different platforms. This means different types of event args that are platform-specific may not be available. Fortunately, {x:Bind} allows us use methods with generalized signatures as binding target – this means, that method parameters can be of a more generic type than originally specified in the event, and the binding algorithm will still find them. For example, compare the following event handler method to the one shown above:

    public class MainViewModel
    {
        public void Button_OnTapped(object sender, RoutedEventArgs e)
        {
            // ...
        }
    }
    

    Alternatively, it’s also possible to use no method parameters at all – in an MVVM context, this might be the most useful approach, since both the event’s sender (which typically is a user control) and the event args are platform-specific and therefore can’t be accessed on the ViewModel layer anyway:

    public class MainViewModel
    {
        public void Button_OnTapped()
        {
            // ...
        }
    }
    
  • One more thing that you should notice is that if you’re binding to methods that are not located in the current View class (meaning, in its code-behind), they need to be declared as public.

  • In addition, it is possible to bind to asynchronous methods: Just add the async keyword to the target method, and you can invoke asynchronous staff using await and still bind to it:

    public async void Button_OnTapped(object sender, RoutedEventArgs e)
    {
        await Task.Delay(TimeSpan.FromSeconds(3));
        // ...
    }
    

    While the above example works, it’s not the best way to implement asynchronous bindings, since asynchronous methods should always have a return type of Task. As mentioned before, {x:Bind} is clever enough to even find methods with slightly different signature. Fortunately, this also applies to the target method’s return type, which means that you can use a clean async Task signature:

    public async Task Button_OnTapped(object sender, RoutedEventArgs e)
    {
        await Task.Delay(TimeSpan.FromSeconds(3));
        // ...
    }
    

    Theoretically, it’s even possible to use other return types (both with synchronous and asynchronous methods), such as public int Button_OnTapped(), public Task<bool> Button_OnTapped(), etc. – The UWP binding algorithm will still find your method and bind to it. However, in practice this won’t be useful since XAML can’t do anything with the return value anyway.

  • With all these nice features at hand, keep one thing in mind: {x:Bind} to methods is not the same thing as binding to Commands, as there are no real commands involved! This means primarily that you’ll need to open up your business logic methods (since they need to be public to be used as binding target), without the additional safety net of a Command wrapped around the actual method.

    In addition, methods in contrast to commands do not provide a CanExecute condition. This means you’ll need to bind the respective control’s IsEnabled, Visibility, etc. properties to some ViewModel flag and / or business logic by hand if you want to ensure that a certain functionality should not be invoked at the moment.