In a previous article, I outlined the three main jobs of the Uno Platform in order to run a UWP app on iOS, Android, and in the browser:

  1. Parse XAML files;
  2. Implement data-binding;
  3. Implement the suite of views in the UWP framework for each platform.

In this article I want to focus on that last point. How does Uno implement UWP’s views? As a case study I’m going to use that humbly ubiquitous UI control, the button.

The number goes up

I present the simplest interactive application imaginable, one step above ‘Hello World’:

XAML:

<Page x:Class="UnoExtTestbed.MainPage" 
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
      xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
      xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
      mc:Ignorable="d">
    <StackPanel> 
        <TextBlock x:Name="ClickTextBlock" 
                   Text="Button wasn't clicked yet" /> 
        <Button Content="Click me" 
                Click="Button_Click" /> 
    </StackPanel> 
</Page>

Code behind :

using System; 
using Windows.UI.Xaml; 
using Windows.UI.Xaml.Controls;
namespace UnoExtTestbed 
{ 
    public sealed partial class MainPage : Page 
    { 
        public MainPage() 
        { 
            this.InitializeComponent(); 
        }
        private int _clickCount = 0; 
        private void Button_Click(object sender, RoutedEventArgs e) 
        { 
            _clickCount++; 
            ClickTextBlock.Text = $"Button was clicked {_clickCount} times."; 
        } 
    } 
}


I made a blank app using the Uno Solution template and put this code on the main page. Whenever the Button is clicked, the number goes up. Add a bit more chrome and we could have a viral hit.

Note that the XAML is useful here but not obligatory, either on UWP or Uno. We could have defined and created all our views in C#, had we really wanted to. That flexibility is handy to have.

Exhibit A — the visual tree

Visual-Tree-information-for-UWP

Visual tree information for UWP

Visual-Tree-information-for-Android

Visual tree information for Android

visualtree-ios

Visual tree information for iOS

visualtree-wasm

Visual tree information for WASM

So what does that get us? You can see the resulting visual trees on each target platform. They’re all substantially similar. The top-level wrapping varies a little, but inside we can see the MainPageStackPanelTextBlock and Button we defined in XAML. (The Frame is created in the stock App.xaml.cscode that comes with the project template.)

You might notice there are a couple of extra views inside the Button that weren’t explicitly defined in the XAML. These are part of Button’s default template. If you’re not familiar with UWP/WPF/Silverlight/etc’s concept of control templating, there’s a lot to say about the subject, but the gist of it is that any view which inherits from Control is a tabula rasa, an empty vessel which will be filled with inner views as defined in its template. (Some of these child views might be templated controls themselves.) It’s a powerful means to customise the look of a reusable control.

How do we go from a logical tree defined in XAML to a native visual tree?

Let’s keep the visual aids rolling. We’ll create an empty class inheriting from Button, and take a look at its inheritance hierarchy.

1_1ebed9dmdBOu6m3lEVl5sw

1_1ebed9dmdBOu6m3lEVl5sw

Inheritance chains for the Button class on UWP, Android, iOS, and WASM

Like the visual tree, the platform-specific inheritance chains are pretty similar (identical in fact) at the ‘leafward’ end, and diverge a bit at the root. Let’s focus on where they diverge: after the UIElement class, the base view type in UWP.

On UWP, UIElement inherits from DependencyObject, the base class for types which support data-binding using DependencyProperty values.

In Uno.Android and Uno.iOS, any UIElement is an instance of the native base view type (Android.Views.View and UIKit.UIView respectively, mapped to managed types via the magic of Xamarin). So views defined in XAML are also native views. This means, for example, that it’s possible to incorporate native views that know nothing about Uno directly into your app’s XAML. The following works on iOS:

<Page x:Class="UnoExtTestbed.MainPage"
            ...
            xmlns:uikit="using:UIKit">
    ...
    <StackPanel> 
            <uikit:UILabel Text="Native label"/> 
    </StackPanel>
</Page>

This is uniquely easy to do in Uno. We talk about ‘leaving an escape hatch’: the goal is 100% code reuse, but if you positively have to use a platform-specific feature or view library, the flexibility is there.

But wait, what about DependencyObject?

Since it’s an important part of the UWP contract, we didn’t want to leave DependencyObject out of the picture, but we also have to be able to support DependencyObjects that aren’t views at all. (Brushes and Transforms, to name just a few.) In Uno therefore DependencyObject is defined as an interface. It’s a ‘special’ interface however: Uno’s code generation automatically adds the backing methods when it finds a class like MyDependencyObject : DependencyObject, allowing code written for UWP to mostly ‘just work.’ I’ll talk more about it in a future article on code generation in Uno.

In WebAssembly, for now the inheritance hierarchy is a bit simpler and UIElement is at the root of the type tree. As you can see in the screenshot, Uno.WASM is generating <div> elements for each view in the visual tree.

Style points

The code above nets us a very plain, workaday button, but we could easily spice it up. We set the Content property to a text string, but Content can be anything, even another view. Our button could be an image, a shape, or a complex visual hierarchy. It could even have another button inside of it.

What if we want to go in the opposite direction? What if we want to abdicate control over our button’s appearance entirely?

A number of controls implemented by Uno, Button included, support the concept of a ‘native’ style. Instead of a button that’s consistent across all versions of your app, you get a button that looks the way a user of the target platform would expect it to look.

It’s supported by setting a pre-defined Style that puts an instance of the native control inside the XAML control. In our code above, we could have written:

<Button Content="Click me" 
                Click="Button_Click" 
      Style="{StaticResource NativeDefaultButton}" />

Since Android’s default button looks rather similar to UWP’s, I’m going with a more visually obvious illustration using a different control, ToggleSwitch. I’ll use the sample from the Uno Gallery app.

0_kX8Q2-mT-aSBw3US

0_iZq_tOqh8zf0fwES

Uno’s ToggleSwitch control on Android and iOS, using default and native styles.

The ToggleSwitch with the default style looks the same on all platforms, both statically and in motion. On Android and iOS, however, the ToggleSwitchwith the NativeDefaultToggleSwitch style replicates the native toggle control of each platform. Of course you can still bind to its properties in XAML as you normally would. This is another powerful option to have: for some apps it makes sense to look as ‘native’ as possible, for others its desirable to have a rich, customised UI. You may even want to mix and match different approaches for different screens in your app. With Uno it’s straightforward.

What if I don’t like buttons?

In fact UIElement implements primitive interaction events like PointerPressedPointerReleased, etc, so all views in Uno can handle touches/clicks, not just Button. (The big advantage of using Button, apart from the MVVM-friendly Command property, is that it implements visual statesto animate your button with.)

There’s plenty more to talk about, like the way that UWP’s API is hooked into each platform’s native input detection, or the way that layouting is done, but that’s all for now.

Try out Uno, and hit us up if you have any questions.

Leave a Reply

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

Tune in Today at 12 PM EST for our free Uno Platform 5.0 Live Webinar
Watch Here