Bindable Native Views in XAML – With Commands!?!

Back when I wrote the post on the awesomeness that is binding platform specific, aka native, views directly in Xamarin.Forms XAML one of the things missing were Commands. For example, no tying button clicks to a Command in a view model.

Using the UpdateSourceEventName attribute, you can introduce two way binding by having Forms listen for the event specified and then update the bindings when said event is fired.

However, that doesn’t help us when our app needs to react and take an action when an event occurs on the native view embedded within the XAML. So what’s a developer to do?

Adding commands isn't magic!

Adding commands isn’t magic!

One way around this is to embed the native control in the code behind file and then all the events will be available for use. Handle that event, then invoke whatever command you want in your view-model.

That works – but it isn’t optimal. You have to mix code-behind with XAML … AND … either use Shared Projects or make creative use of partial classes. There has to be a better way … and there is …

Commanding From the Native View! Or Subclassing to the Rescue!

The solution to the problem really is rather simple and it evolves subclassing. All the demo code and a working solution can be found on GitHub here.

In the previous post on native XAML embedding with data binding, I mentioned there was a new extension method called SetBinding on UIView and Android.View objects. It’s this method the Xamarin.Forms XAML parser calls when it comes across a native property definition specified in the XAML to provide data binding.

So … what would happen if the XAML parser would come across a property in the native control (which derives from either UIView or Android.View) that’s of a Command type? Why, it would call SetBinding on that property and then we would have data binding setup on a Command – thus enabling commanding to a view model!

All we have to do is subclass whatever view we want to provide commanding for, add a Command property, and then handle events appropriately to invoke the Execute method of that Command.

Let’s take a look at the code.

The View-Model

We’re going to build an app that pops a modal dialog onto the stack when a Droid FAB gets tapped, or an iOS look-alike FAB gets tapped.

That popping will happen when a Command gets invoked within a view model. If we take a look at a portion of the view model code, we’ll see that there’s a property which controls whether or not the modal should be shown, and then the Command which actually shows it.

Note that the Command also has a function to indicate whether the Command can execute or not.

Setting Up The Platform Code

As I mentioned above, the key to this all is to subclass whatever native/platform specific view you want to host the command, and then add a Command property to it.

For iOS, I created a simple class that inherits from UIView which looks like a FAB and raises simple events whenever a touch on it ends. (The implementation is very simplistic, but will demonstrate everything we need.)

Then I created a subclass of that FAB look-alike class. It’s in there that I created a new Command property. There’s nothing special about that property – no backing BindableProperty or anything like that – it’s a plain C# property.

Then in the constructor I handle the event raised by the underlying control, check if the Command property has been set, and if so, execute it.

The code looks like the following:

Yeah, that really is it. Granted this code isn’t doing anything if the Command shouldn’t be able to be executed … we’ll do that in the Droid version.

Speaking of the Droid version, here it is. This subclasses a FloatingActionButton. When it’s enabled and sending commands – it shows a plus sign. When it’s not, it shows an X.

There’s definitely more going on here however. It’s still handling the Clicked event and executing the Command . However, it also handles the CanExecuteChanged event. This way whenever the view model says the execution state has changed, this FAB can update its visual state (in this case, changing the image) and no longer executing the Command itself.

Consuming the Command

This is the easy part! It’s the same as it ever was – using the familiar XAML {Binding } syntax with properties on the class, map the Command property on our new subclasses to whatever command you need to in the view model.

That’s all there is!

Summary

There’s a little extra work involved – but it’s definitely worth it to subclass any native views that you want to access directly in XAML – to gain access to events that you want to tie Commands to.

It can be as easy as creating a public Command property – or with a little more work responding to CanExecuteChanged events to control the native view’s UI state. And it wouldn’t be much more work to pass the CommandParameter along as well.

All of this is made possible by the new extension methods on UIView and Android.View provided by the 2.3.3 version of Xamarin.Forms that the XAML parser automatically puts onto any exposed properties in the XAML definition.

Native view XAML embedding with data binding was already awesome – now it’s even more so with commanding!

All the demo code can be found on GitHub here.

Matthew Soucoup on sabemailMatthew Soucoup on sabgithubMatthew Soucoup on sablinkedinMatthew Soucoup on sabtwitter
Matthew Soucoup
Matthew Soucoup
Matthew is a Xamarin MVP and Certified Xamarin Developer from Madison, WI. He started Code Mill Technologies and is the founder of the Madison Mobile .Net Developer's Group. 

Matt regularly speaks on .Net and Xamarin development at user groups, code camps and conferences throughout the nation, including Xamarin Evolve, That Conference, Prairie.Code(), MKEdotNET, St. Louis Days of .Net, and CodeMash.

When he's not developing cross platform apps, writing blog posts, or creating the next great talk, Matt enjoys brewing beer with jalapeños from his garden and playing 80s tunes on the piano.

Submit a Comment

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

%d bloggers like this: