In the last post on the new features of Xamarin Forms 2.3.3 we took a look at Platform Specifics – both how to create one and how to consume one. This time around we’re going to turn our attention to bindable native views in XAML!

Again this feature seems to be taking away the need to create custom renderers by giving developers the ability to directly place native (as in 100% iOS or Android or UWP controls) into a Xamarin.Forms core project – and we’re talking putting them into XAML here! Not only that, they are bindable! (As we’ll see, they have to be bindable, because they are not accessible from the code behind file at all.)

So let’s dive right in!

Adding Native Bindable Views

Before we get started with how to add a native view/control – there is a quick gotcha (and isn’t it great when I have to start out with a warning first? This isn’t so bad.)

You cannot have XAML compilation turned on for the XAML pages that contain the native views. That’s not ideal. I would recommend you turn it on for every XAML page that does not contain a native view, but leave it off for the ones that do. So in other words – class level, not assembly level.

OK – adding one of these native views. A page that has a native iOS UILabel would look like the following:

<?xml version="1.0" encoding="utf-8"?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" 
        xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" 
        xmlns:local="clr-namespace:XAMLb" 
        xmlns:ios="clr-namespace:UIKit;assembly=Xamarin.iOS;targetPlatform=iOS"
        x:Class="XAMLb.XAMLbPage">
    <ContentPage.Content>
        <ios:UILabel Text="Hello from an iOS label" View.VerticalOptions="Center" View.HorizontalOptions="Center"  /> 
    </ContentPage.Content>
</ContentPage>

The first thing to look at is the page level XAML namespaces. In order for Xamarin.Forms to add native controls via XAML, we need to reference the iOS, Android or UWP “XAML namespace” – the familiar old xmlns attribute. However, there’s a new portion of that attribute which tells the Xamarin.Forms XAML parser which platform to go after and that’s called targetPlatform. Here it is so you can see what I’m talking about:

xmlns:ios="clr-namespace:UIKit;assembly=Xamarin.iOS;targetPlatform=iOS"

Most of the xmlns attribute looks the same as usual if one were bringing in controls defined in a different DLL from the core project. It first references the namespace where all the controls are then grabs the assembly name.

But here’s the new portion: targetPlatform. When the XAML parser runs across that, it checks if the current device the code is running on is the same as the value inside targetPlatform. If it is, then whenever the XAML parser comes across elements defined with that XML namespace in the XAML it will grab the native control and put it in.

If the targetPlatform doesn’t match, then the XAML node containing that XML namespace is ignored.

Android

Android being Android – there’s an extra step to go through in order to embed its controls. You have to pass the Context into the constructor of the native control. You can get the Context through the Xamarin.Forms.Forms.Context class.

A page with a Droid edit text box in it would look like the following:

<?xml version="1.0" encoding="utf-8"?>    
 
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" 
        xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" 
        xmlns:local="clr-namespace:XAMLb" 
        xmlns:droid="clr-namespace:Android.Widget;assembly=Mono.Android;targetPlatform=Android"
        xmlns:formsDroid="clr-namespace:Xamarin.Forms;assembly=Xamarin.Forms.Platform.Android;targetPlatform=Android"
        x:Class="XAMLb.XAMLbPage">
    
    <ContentPage.Content>
        <droid:EditText Text="hi" View.VerticalOptions="Center" View.HorizontalOptions="Fill" 
                x:Arguments="{x:Static formsDroid:Forms.Context}" />
    </ContentPage.Content>
</ContentPage>

Notice both of the XML namespaces for the pure Android and the Xamarin.Forms Android libraries.

(On a side note, if you ever really want to understand how and why your XAML code works … read through the unit tests and then the implementation code that does the XAML parsing in Xamarin.Forms. It’s pretty enlightening.)

Adding Data Binding

So we have a pure native, platform-specific control in the XAML … but how do we get it to display the data we need it to? We cannot use the x:Name attribute and get at it from straight C#. By doing so the XAML parser will try to create a variable in the code behind file (the one with the “g” in it that you only see when exceptions are thrown) with the fully qualified type. That won’t fly. And that happens to be the reason why XAML compilation cannot be turned on either. So in this case, the XAML view needs to be inflated at runtime so the native controls can be placed into it.

OK … so if we can’t reference the native control from the core project in any code behind files … how do we do it then? Data binding to the rescue!

There is now a SetBinding extension method on UIView and Android.View. Sweet! Now through the magic of the Xamarin.Forms binding engine we can update the display of our controls based on the view-models.

Two Way Binding

And the binding doesn’t need to only go one direction! There’s a new argument called UpdateSourceEventName. Pass in the platform-specific event name name on the control that you want to capture – and when it occurs – the binding, thus view model, will be updated!

Very, very cool!

The UpdateSourceEventName isn’t needed all the time on iOS controls … but it doesn’t hurt to put it in there… if nothing else to be explicit as to what event you’re after.

A page that has both Android and iOS binding between native text fields and a Forms label would look like the following (the iOS is the only one that has the two way binding setup):

<?xml version="1.0" encoding="utf-8"?>    
 
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" 
        xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" 
        xmlns:local="clr-namespace:XAMLb" 
        xmlns:ios="clr-namespace:UIKit;assembly=Xamarin.iOS;targetPlatform=iOS" 
        xmlns:droid="clr-namespace:Android.Widget;assembly=Mono.Android;targetPlatform=Android"
        xmlns:formsDroid="clr-namespace:Xamarin.Forms;assembly=Xamarin.Forms.Platform.Android;targetPlatform=Android"
        x:Class="XAMLb.XAMLbPage">
    
    <ContentPage.Content>
        <StackLayout VerticalOptions="Center">
            <Label Text="{Binding SomeGreatText}" TextColor="Red" />
            <droid:EditText Text="{Binding SomeGreatText}" View.HorizontalOptions="Fill" 
                x:Arguments="{x:Static formsDroid:Forms.Context}" />
            <ios:UITextField Text="{Binding SomeGreatText, Mode=TwoWay, UpdateSourceEventName=Ended}" View.HorizontalOptions="Fill" />
        </StackLayout>
    </ContentPage.Content>
</ContentPage>

The Downsides

As you can imagine, once you start to have a bunch of platform specific XAML nodes within a page, things could start to get messy very quickly! For example, you could have 10 lines for pure iOS then another 10 for pure Android … so some code applies in one place and not in another – it starts to become a mess! (and isn’t the whole point of the core project to be shared code? 🙂 )

One way around that is to start to create some custom controls that are specific to each OS. By that I mean – if your UI is laid out in a way that it’s possible for you to chunk out portions of your it where most of the native controls will be … pull them out into a custom user control. So you’ll have one user control for the Droid native views and one for the iOS native views. Then reference each of those user controls in your main page.

So instead of a ton of individual native control references – you’ll only have 2 user controls in your page.

iOS

Usually it’s Android that has special downsides all of it’s own! Here, it’s iOS. The API surface of a lot of iOS controls are designed in such a way that setting the display properties are done through functions. For example, to set a button’s title, you use – (void)setTitle:(NSString *)title forState:(UIControlState)state;. There’s not a way to set the title via properties alone. Thus you can’t do it through XAML.

The upshot of all that means is you’re going to end up subclassing a lot of iOS controls to work with XAML…

Finally, so far I’ve found I cannot create a native view in XAML that takes an array in its constructor. I’m sure it’s only a bug … but something to be aware of.

In Summary

Finally we can add 100% pure native views to Xamarin.Forms from XAML in PCL (and Shared) core projects! To top it all off, we can data bind them too!

So we’re taking another step away from having to write custom renderers for everything. However, there are some downsides here, namely that the XAML could become a big mess quickly … and you may have to subclass some of the native controls in order to expose the functionality you need them to within XAML.

But despite that … native controls … XAML … data binding. Wow!