Tutorial: How to Create a XAML Templated Control

In this post we’re going to cover creating a custom control that uses a control template to define how it looks, aka a Templated Control. The principles of templated, or lookless, controls have been adopted by most of the XAML based technologies but for the purpose of this post we’re going to start by building for Windows (ie UWP) and then we’re going to leverage Uno to deliver the same control across iOS, Android and even the web using Web Assembly (WASM).

Full source code available on GitHub

Disclaimer: The purpose of this post is to walk through the process of creating a Templated Control. To do this we’re going to create a multi-switch control (i.e. a switch that has multiple positions). However, I haven’t attempted to win any design awards with this control. In fact the entire point of a Templated Control is that it’s possible to restyle the control and add animation etc without changing the basic functionality of the control.

Getting Started – Uno Project Templates

To get started a bit of house keeping – let’s make sure we have our project setup so that we can build a Templated Control in its own library (so we can reuse it) and that we have a set of head projects where we can test out our control. Since we’re going to use Uno to take our control cross platform, we’ll use the Uno project templates to get us started.

We’ll start by creating a new project based on the Cross-Platform App (Uno Platform). If you don’t have the Uno project templates installed you can grab the Uno Platform Solution Templates Visual Studio extension from the marketplace.

Set some basic project information – in this case our head projects are just for the purpose of testing our Templated Control so we’ve named it XAMLControlSample.

Once you’ve completed creating the new project you should have a solution with four head projects (iOS, Android, UWP and WASM) as well as a shared project. The XAML for the MainPage is in the shared project, which is where we’ll be adding an instance of our Templated Control to test it out after we’ve created it.

Speaking of which, we need to create a library for our Templated Control to reside in. We’ll add another project, this time based on the Cross-Platform Library (Uno Platform) project template. If you’re not interested in taking your Templated Control cross platform (i.e. you’re just building for UWP) you can simply create a class library based on the Class Library (Universal Windows) project template. The big difference with the Uno template is that it creates a project that is setup with multi-targeting, meaning that it will create a library that will have an iOS, Android, Windows and WASM binaries.

We’ll give our class library a name, in this case MyCustomControls.

The next step is to create our Templated Control. Unfortunately due to the limited support for multi-targeting within Visual Studio, if you attempt to add a new item directly to the class library, you won’t see any of the Windows Universal item templates. Instead what we need to do is to create the Template Control in the UWP head project and move the relevant files across to the class library. Right-click on the UWP head project and select Add, New Item. In the Add New Item dialog, select the Templated Control item template and give the control a name, in this case MultiSwitchControl.

After adding the Templated Control you should see two files added to the UWP head project: Generic.xaml (in the Themes folder) and MultiSwitchControl.cs (you Templated Control). Note that there’s no XAML file for the Templated Control (i.e. there’s no MultiSwitchControl.xaml), which you would get if you were creating a UserControl. This is because the XAML that defines how the Templated Control looks is all contained in the Style and the associated ControlTemplate.

The final piece of setup is just to move these two files, including the Themes folder, into the class library. After moving the files, you should make sure that you update the namespace of your Templated Control to reflect the correct project. In my case I had to change the namespace from XAMLControlSample to MyCustomControls.

After moving the Templated Control to its correct location, let’s make sure that it can be consumed by each of our head projects:

  • Update NuGet packages, importantly the Uno packages
  • For each head project add a reference to the MyCustomControls project.
  • Build and run each head project to make sure no compile errors (Note for WASM use the “Start without Debugging” option to launch the browser)

Once we’ve confirmed that each platform works without our Templated Control, it’s time to add an instance to the MainPage. Update the MainPage code to the following:

<Page x:Class="XAMLControlSample.MainPage"
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      xmlns:ctrls="using:MyCustomControls">
  <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
    <StackPanel>
      <TextBlock Margin="20"
                 HorizontalAlignment="Center"
                 Text="XAML Templated Control"
                 FontSize="30" />
      <ctrls:MultiSwitchControl 
                                Width="400"
                                Height="400"
                                Background="Blue" />
    </StackPanel>
  </Grid>
</Page>

Run each of the head projects and verify that the MultiSwitchControl appears as a blue square.

Breaking Down the Templated Control

In the previous section we walked through creating a very simple Templated Control and demonstrated that through the power of Uno the same control can be used across iOS, Android, Windows and Web. Let’s take a look at how the Templated Control works, before we move on to building out our multi-switch control.

DefaultStyleKey for Implicit Style Lookup

The MultiSwitchControl.cs code file contains very little code. In fact, the only code it contains by default is a parameterless constructor that sets the DefaultStyleKey property.

public MultiSwitchControl()
{
    this.DefaultStyleKey = typeof(MultiSwitchControl);
}

What’s not apparent here is that setting the DefaultStyleKey is critical to the loading of the control. When an instance of the MultiSwitchControl is created, unless the Style attribute has been set, the framework looks for a corresponding implicit style. An implicit style is one that doesn’t have an explicit Key defined. Instead, the Key for an implicit style is essentially the TargetType of the Style. For example in the Generic.xaml you’ll see that there is a Style defined with TargetType set to MultiSwitchControl.

<Style TargetType="local:MultiSwitchControl" >
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="local:MultiSwitchControl">
                <Border
                    Background="{TemplateBinding Background}"
                    BorderBrush="{TemplateBinding BorderBrush}"
                    BorderThickness="{TemplateBinding BorderThickness}">
                </Border>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

What’s important to note is that when the framework looks for the implicit Style, it doesn’t just assume that it should look for a Style with the TargetType matching that of the control. Instead it looks at the Type returned by the DefaultStyleKey property. Whilst this property is often just set to the Type of the control, there are cases where this isn’t the case.

Making your Implicit Style Explicit

One thing that annoys me about the item template that is used to generate the Templated Control is that it only defines an implicit Style for the control. The weakness of this is that it means that any developer wanting to override the Style has to copy the entire Style into their application. A better alternative is to make your Style explicit by giving it a Key, thus making it possible for other developers to inherit from your Style using the BasedOn attribute.

Of course, if you make your Style explicit, your Templated Control will no longer be able to find the Style without you explicitly referencing it. This is simple to overcome by defining an implicit style that inherits from your explicit Style.

If this all sounds a little complex, check out the amended Styles for the MultiSwitchControl below (there’s no code changes required to the MultiSwitchControl itself since it still relies on the implicit Style).

<Style x:Key="MultiSwitchControlDefaultStyle"
     TargetType="local:MultiSwitchControl">
<Setter Property="Template">
  <Setter.Value>
    <ControlTemplate TargetType="local:MultiSwitchControl">
      <Border Background="{TemplateBinding Background}"
              BorderBrush="{TemplateBinding BorderBrush}"
              BorderThickness="{TemplateBinding BorderThickness}" />
    </ControlTemplate>
  </Setter.Value>
</Setter>
</Style>
<Style TargetType="local:MultiSwitchControl"
     BasedOn="{StaticResource MultiSwitchControlDefaultStyle}" />

Designing the Template Control

At this point we have a lot of the infrastructure in place so we can get on with actually building our Templated Control. In this case we’re building a four-way switch control. It actually has five states: Off (Center), Up, Right, Down, Left, and as mentioned earlier we’re going to put minimal effort into the default design/layout of the control. We’ll show at the end of the process how easy it is for a developer consuming the control to override the Style and provide their own design without having to re-code the operation of the control (i.e. a true lookless control).

Simple Box Layout for the Template Control

To keep things simple the layout for the multi-switch that we’ll add to the MultiSwitchControlDefaultStyle will be a cross based on a 5×5 grid. There will be a box defined in the middle of the top row (Up), the center of the fifth column (Right), the middle of the bottom row (Down), the center of the first column (Left) and at the intersection of the third row and third column (Off). We’ve used a 5×5 layout to give a bit of spacing between the boxes, as you can see from the following image.

The updates Style defines each box using a Grid. At this stage a Border element would have sufficed. However, as you’ll see in the next step we’ll be nesting a couple of elements in the box to provide the visual context for when the user moves the mouse over the box, presses or clicks on the box, and when the box is selected.

<Style x:Key="MultiSwitchControlDefaultStyle"
     TargetType="local:MultiSwitchControl">
<Setter Property="BorderBrush" Value="SteelBlue" />
<Setter Property="BorderThickness" Value="1" />
<Setter Property="Template">
  <Setter.Value>
    <ControlTemplate TargetType="local:MultiSwitchControl">
      <Grid>
        <Grid.ColumnDefinitions>
          <ColumnDefinition />
          <ColumnDefinition />
          <ColumnDefinition />
          <ColumnDefinition />
          <ColumnDefinition />
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
          <RowDefinition />
          <RowDefinition />
          <RowDefinition />
          <RowDefinition />
          <RowDefinition />
        </Grid.RowDefinitions>
        <Grid x:Name="PART_Off"
              Grid.Row="2"
              Grid.Column="2"
              Background="Transparent"
              BorderBrush="{TemplateBinding BorderBrush}"
              BorderThickness="{TemplateBinding BorderThickness}" />
        <Grid x:Name="PART_Up"
              Grid.Row="0"
              Grid.Column="2"
              Background="Transparent"
              BorderBrush="{TemplateBinding BorderBrush}"
              BorderThickness="{TemplateBinding BorderThickness}" />
        <Grid x:Name="PART_Right"
              Grid.Row="2"
              Grid.Column="4"
              Background="Transparent"
              BorderBrush="{TemplateBinding BorderBrush}"
              BorderThickness="{TemplateBinding BorderThickness}" />
        <Grid x:Name="PART_Down"
              Grid.Row="4"
              Grid.Column="2"
              Background="Transparent"
              BorderBrush="{TemplateBinding BorderBrush}"
              BorderThickness="{TemplateBinding BorderThickness}" />
        <Grid x:Name="PART_Left"
              Grid.Row="2"
              Grid.Column="0"
              Background="Transparent"
              BorderBrush="{TemplateBinding BorderBrush}"
              BorderThickness="{TemplateBinding BorderThickness}" />
      </Grid>
    </ControlTemplate>
  </Setter.Value>
</Setter>
</Style>

Note that in most cases where there is repeated XAML (for example setting the properties of Background, BorderBrush and BorderThickness) it pays to extract these into a Style that can simply be applied to all elements. However, in practice this both adds to the overhead of loading the control and you immediately run into limitations on the TemplateBinding markup extension. Attempting to extract these elements to a Style will result in a runtime exception that doesn’t seem to have a clear work around.

The next thing to note about the Style is that we’ve added a Setter for both BorderBrush and BorderThickness. The Setters define the default values for these properties, meaning that if the developer doesn’t explicitly set them on their instance of the MultiSwitchControl they’ll still have a value. If we didn’t do this, the default appearance of the MultiSwitchControl wouldn’t show the boxes since there would be no brush, and thickness would be 0.

The last thing to note about the Style is that each of the Grid elements have a Name attribute. In each case the value has the prefix “PART_” followed by the corresponding switch state eg PART_Off. This prefix was a convention adopted by WPF but subsequently dropped for Silverlight (see this post for some commentary on this topic), Windows Phone, UWP etc. Whilst you don’t have to adopt this prefix (you’ll see why in a minute) I still find it quite a clean way to identify parts of the Style that have to be there in order for the control to function correctly.

Visual States for the Templated Control

As mentioned earlier we want our Templated Control to be able to provide contextual feedback to the user. There are three things that we want to be able to do:

  • Indicate when the user moves the mouse (UWP & WASM) over a box
  • Indicate when the user clicks, presses, touches into a box
  • Indicate when the user has selected a box

The first two of these we’ll pair together as they can represent the current state of the input device (aka pointer). This will be our CommonStates VisualStateGroup, to be consistent with other Windows controls, and will contain the following Visual States:

  • Normal – pointer isn’t over any element or pressed down on any element
  • PointerOverXXX – pointer has entered the area of element XXX
  • PressedXXX – pointer has been pressed down on element XXX

Element XXX will be one of the Grid elements named in our Style, so our states will be PointerOverOff and PressedOff for the PART_Off Grid.

To track which box is currently selected we’ll create a second VisualStateGroup called SelectionStates, which will include Visual States with the naming convention SelectionXXX. So for the PART_Off Grid there will be a corresponding VisualState called SelectionOff. Additionally there will be one extra VisualState, SelectionNone, which represents the default state where no box has focus.

You might be asking at this point – why the need for two VisualStateGroups? or why not three? The answer to this is that VisualStateGroups should define mutually exclusive VisualStates; and that VisualStates from one group should not set the same properties as VisualStates from a different group. If we look at the scenarios above it’s very clear that we’d want to be able to specify which box is currently selected whilst being able to highlight a different box that the user may have moused over. What’s not immediately clear is why we’ve combined the PointerOver and the Pressed states into the one group. The reality is that we could have separated these into a third group. However, in this case we’re going to keep the implementation simple by assuming that the state of the pointer will either be PointerOver or Pressed and not both at the same time.

I mentioned earlier that each of the Grids we created for the different switch states were going to contain multiple elements. In fact we’re going to add three Border elements to each, with the resulting Grids all being similar to the following Part_Off Grid, where the element names have the switch state as their prefix eg OffPointerOver, OffPressed, OffSelection.

<Grid x:Name="PART_Off"
      Grid.Row="2"
      Grid.Column="2"
      Background="Transparent"
      BorderBrush="{TemplateBinding BorderBrush}"
      BorderThickness="{TemplateBinding BorderThickness}">
  <Border x:Name="OffPointerOver"
          Background="{TemplateBinding Background}"
          Visibility="Collapsed" />
  <Border x:Name="OffPressed"
          Opacity="{TemplateBinding PressedOpacity}"
          Background="{TemplateBinding Foreground}"
          Visibility="Collapsed" />
  <Border x:Name="OffSelection"
          Background="{TemplateBinding Foreground}"
          Visibility="Collapsed" />
</Grid>

Each Border has its Visibility property set to Collapsed. The OffPointerOver Border will be set to Visible when a Pointer enters the region of PART_Off. The OffPressed will be set to Visible when a Pointer is pressed inside the PART_Off. Lastly, the OffSelection will be set to Visible when the PART_Off is selected (i.e. the state of the switch is set to Off). All this of course has to be done with the corresponding visual states, as follows:

<VisualStateManager.VisualStateGroups>
  <VisualStateGroup x:Name="CommonStates">
    <VisualState x:Name="Normal" />
    <VisualState x:Name="PointerOverOff">
      <VisualState.Setters>
        <Setter Target="OffPointerOver.Visibility" Value="Visible" />
      </VisualState.Setters>
    </VisualState>
    ...
    <VisualState x:Name="PressedOff">
      <VisualState.Setters>
        <Setter Target="OffPressed.Visibility" Value="Visible" />
      </VisualState.Setters>
    </VisualState>
    ...
  </VisualStateGroup>
  <VisualStateGroup x:Name="SelectionStates">
    <VisualState x:Name="SelectionNone" />
    <VisualState x:Name="SelectionOff">
      <VisualState.Setters>
        <Setter Target="OffSelection.Visibility" Value="Visible" />
      </VisualState.Setters>
    </VisualState>
    ...
  </VisualStateGroup>
</VisualStateManager.VisualStateGroups>

The visual states for the other parts are all similar, just with different names in the Target of the Setter.

Building the Functionality of the Templated Control

So far we’ve focused on getting the basic layout of the Templated Control sorted. This has included specifying the different visual states that map to both user interaction (i.e. pointer over and pressed) as well as the switch states (i.e. selection). What’s missing is that actual functionality of the MultiSwitchControl which will trigger the changes in the visual states and track what the current switch state is.

Current Switch State

To track the current state of the switch I’m going to define an enum called SwitchState, which will include the values Off, Up, Right, Down and Left. For completion I’ve added a None state to represent an invalid or non-set state. I’ll then add a Value dependency property which will track the current state of the switch. when the Value does change, the ValuePropertyChanged method will be invoked, which subsequently calls the UpdateSwitchState that is responsible for calling GoToState on the VisualStateManager. The name of the new VisualState is specified by concatenating the prefix “Selection” with the current switch Value. For example if the current Value is SwitchState.Off, the visual state name would be SelectionOff.

public enum SwitchState
{
    None,
    Off,
    Up,
    Right,
    Down,
    Left
}

private const string SelectionStatePrefix = "Selection";

public static readonly DependencyProperty ValueProperty =
    DependencyProperty.Register(nameof(Value), typeof(SwitchState), 
                                typeof(MultiSwitchControl), 
                                new PropertyMetadata(SwitchState.None, ValuePropertyChanged));

public SwitchState Value
{
    get => (SwitchState)GetValue(ValueProperty);
    set => SetValue(ValueProperty, value);
}

private static void ValuePropertyChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs args)
{
    var switchControl = dependencyObject as MultiSwitchControl;
    switchControl?.UpdateSwitchState();
}

private void UpdateSwitchState()
{
    VisualStateManager.GoToState(this, SelectionStatePrefix + this.Value, true);
}

Pointer Events in the Templated Control

A lot of the visual state changes are conditional on intercepting pointer activity entering, exiting, pressing and release on the Templated Control. To attach the correct event handlers we need to override the OnApplyTemplate method – this method is called to apply the template to the control, afterwhich the various parts of the template are available to interact with.

private IDictionary<UIElement, (SwitchState state, bool isInside, bool isPressed)> Parts { get; } = new Dictionary<UIElement, (SwitchState state, bool isInside, bool isPressed)>();

protected override void OnApplyTemplate()
{
    base.OnApplyTemplate();

    var switchStates = new[] { SwitchState.Off, SwitchState.Up, SwitchState.Right, SwitchState.Down, SwitchState.Left };
    foreach (var s in switchStates)
    {
        SetupPart(s);
    }

    Value = SwitchState.Off;
}

private void SetupPart(SwitchState state)
{
    var partName = PartPrefix + state;
    var partOff = GetTemplateChild(partName) as UIElement;
    if (partOff == null) throw new NullReferenceException($"{partName} expected in control template");
    Parts[partOff] = (state: state, isInside: false, isPressed: false);
    partOff.PointerPressed += PartPointerPressed;
    partOff.PointerReleased += PartPointerReleased;
    partOff.PointerEntered += PartPointerEntered;
    partOff.PointerExited += PartPointerExited;
}

As the code above illustrates, the OnApplyTemplate method iterates through a list of switch states, invoking the SetupPart method, afterwhich it sets the default value of the switch to Off. The SetupPart method calls GetTemplateChild to retrieve the element generated by the corresponding template part. For example for the SwitchState.Off, the partName is “PART_Off”. Calling GetTemplateChild doesn’t retrieve the Grid from the ControlTemplate, it retrieves the Grid that was created as part of applying the ControlTemplate to the instance of the MultiSwitchControl.

The Parts dictionary is used to track the current state of each part of the MultiSwitchControl. More specifically it tracks whether a pointer is inside the part and whether the pointer has been pressed. As you’ll see in the next code snippet, these values are used to determine when different visual state changes are applied.

At this point we also wire up the event handlers for each of the pointer events. The expected flow is that a pointer will enter the part, it may then be pressed (which will capture the pointer), the pointer may then exit and/or release at some point in the future. If the pointer is released whilst still within the part, this will select the part and change the state of the MultiSwitchControl.

private void PartPointerEntered(object sender, PointerRoutedEventArgs e)
{
    var partElement = sender as UIElement;
    if (partElement == null)
    {
        return;
    }

    var part = Parts[partElement];
    Parts[partElement] = (part.state, true, part.isPressed);
    if (!part.isPressed)
    {
        VisualStateManager.GoToState(this, PointerOverStatePrefix + part.state, true);
    }
}

private void PartPointerExited(object sender, PointerRoutedEventArgs e)
{
    var partElement = sender as UIElement;
    if (partElement == null)
    {
        return;
    }

    var part = Parts[partElement];
    Parts[partElement] = (part.state, false, part.isPressed);
    if (!part.isPressed)
    {
        VisualStateManager.GoToState(this, NormalState, true);
    }
}

private void PartPointerPressed(object sender, PointerRoutedEventArgs e)
{
    var partElement = sender as UIElement;
    if (partElement == null)
    {
        return;
    }

    var part = Parts[partElement];
    if (!part.isInside && !part.isPressed)
    {
        // Hack to deal with Android not firing events correctly
        //VisualStateManager.GoToState(this, "Selection" + part.state, true);
        Value = part.state;
        VisualStateManager.GoToState(this, NormalState, true);
        return;
    }
    Parts[partElement] = (part.state, part.isInside, true);
    VisualStateManager.GoToState(this, PressedStatePrefix + part.state, true);
    partElement.CapturePointer(e.Pointer);
}

private void PartPointerReleased(object sender, PointerRoutedEventArgs e)
{
    var partElement = sender as UIElement;
    if (partElement == null)
    {
        return;
    }

    partElement.ReleasePointerCaptures();
    var part = Parts[partElement];
    Parts[partElement] = (part.state, part.isInside, false);
    if (part.isInside)
    {
        Value = part.state;
    }
    VisualStateManager.GoToState(this, NormalState, true);
}

What’s a TemplatePart?

Earlier in this post I mentioned that WPF had a pseudo standard for the naming of parts of the template that needed to exist. The more precise name for these elements are template parts and the reason that the naming convention is no longer widely adopted is that there is a more prescriptive way to communicate to developers the required parts of a control.

The TemplatePartAttribute should be used to define the name and, if necessary, the type of the content template that need to exist in order for the control to operate correctly. In the case of the MultiSwitchControl there are five template parts, so we add five instances of the TemplatePartAttribute to the MultiSwitchControl class.

[TemplatePart(Name = "PART_Off")]
[TemplatePart(Name = "PART_Up")]
[TemplatePart(Name = "PART_Right")]
[TemplatePart(Name = "PART_Down")]
[TemplatePart(Name = "PART_Left")]
public partial class MultiSwitchControl : Control

I’d love to stay that these attributes showed up in the visual designer in Visual Studio or Blend but the reality is that both designers are in a pretty messed up state right now, so I would count on getting any useful prompts. The best advice I’d give is that if you’re going to start messing with the template of a control, inspect the class for yourself and see what template parts are required.

Are We There Yet?

Yes, the good news is that we’ve got to a point where we have a functioning control. We’ve used all the power of UWP to separate the visuals (i.e. the ControlTemplate coupled with Visual States) from the underlying control functionality. The only real connection is via the named parts of the template.

The following GIFs illustrate the control running on Windows, Android and WASM:

Overriding the Style of a Templated Control

The last thing I wanted to illustrate is how it’s possible to adjust the layout and visual appearance of the switch control without impacting the way it works. In the App.xaml file in the shared project (i.e. not in the class library) I’ve copied across the Style for the MultiSwitchControl. I’ve subsequently modified the ControlTemplate as follows:

  • Instead of multiple rows, all the boxes are now placed in 1 row
  • Each box now has a rounded corner, effectively causing them to be circular in shape (this was admittedly a lazy way and I should really have made them ellipses).
<ControlTemplate TargetType="myCustomControls:MultiSwitchControl">
<Grid Background="Transparent"
      DataContext="{Binding RelativeSource={RelativeSource TemplatedParent}}">
  <Grid.ColumnDefinitions>
    <ColumnDefinition />
    <ColumnDefinition />
    <ColumnDefinition />
    <ColumnDefinition />
    <ColumnDefinition />
    <ColumnDefinition />
    <ColumnDefinition />
    <ColumnDefinition />
    <ColumnDefinition />
  </Grid.ColumnDefinitions>
  <Grid x:Name="PART_Left"
        Grid.Column="0"
        Background="Transparent"
        CornerRadius="30"
        BorderBrush="{TemplateBinding BorderBrush}"
        BorderThickness="{TemplateBinding BorderThickness}">
    <Border x:Name="PART_Left_PointerOver"
            Background="{TemplateBinding Background}"
            Visibility="Collapsed" />
    <Border x:Name="PART_Left_Pressed"
            Opacity="{TemplateBinding PressedOpacity}"
            Background="{TemplateBinding Foreground}"
            Visibility="Collapsed" />
    <Border x:Name="PART_Left_Selection"
            Background="{TemplateBinding Foreground}"
            Visibility="Collapsed" />
  </Grid>
  <Grid x:Name="PART_Down"
        Grid.Column="2"
        Background="Transparent"
        CornerRadius="30"
        BorderBrush="{TemplateBinding BorderBrush}"
        BorderThickness="{TemplateBinding BorderThickness}">
    <Border x:Name="PART_Down_PointerOver"
            Background="{TemplateBinding Background}"
            Visibility="Collapsed" />
    <Border x:Name="PART_Down_Pressed"
            Opacity="{TemplateBinding PressedOpacity}"
            Background="{TemplateBinding Foreground}"
            Visibility="Collapsed" />
    <Border x:Name="PART_Down_Selection"
            Background="{TemplateBinding Foreground}"
            Visibility="Collapsed" />
  </Grid>
  <Grid x:Name="PART_Off"
        Grid.Column="4"
        Background="Transparent"
        CornerRadius="30"
        BorderBrush="{TemplateBinding BorderBrush}"
        BorderThickness="{TemplateBinding BorderThickness}">
    <Border x:Name="PART_Off_PointerOver"
            Background="{TemplateBinding Background}"
            Visibility="Collapsed" />
    <Border x:Name="PART_Off_Pressed"
            Opacity="{TemplateBinding PressedOpacity}"
            Background="{TemplateBinding Foreground}"
            Visibility="Collapsed" />
    <Border x:Name="PART_Off_Selection"
            Background="{TemplateBinding Foreground}"
            Visibility="Collapsed" />
  </Grid>
  <Grid x:Name="PART_Up"
        Grid.Column="6"
        Background="Transparent"
        CornerRadius="30"
        BorderBrush="{TemplateBinding BorderBrush}"
        BorderThickness="{TemplateBinding BorderThickness}">
    <Border x:Name="PART_Up_PointerOver"
            Background="{TemplateBinding Background}"
            Visibility="Collapsed" />
    <Border x:Name="PART_Up_Pressed"
            Opacity="{TemplateBinding PressedOpacity}"
            Background="{TemplateBinding Foreground}"
            Visibility="Collapsed" />
    <Border x:Name="PART_Up_Selection"
            Background="{TemplateBinding Foreground}"
            Visibility="Collapsed" />
  </Grid>
  <Grid x:Name="PART_Right"
        Grid.Column="8"
        Background="Transparent"
        CornerRadius="30"
        BorderBrush="{TemplateBinding BorderBrush}"
        BorderThickness="{TemplateBinding BorderThickness}">
    <Border x:Name="PART_Right_PointerOver"
            Background="{TemplateBinding Background}"
            Visibility="Collapsed" />
    <Border x:Name="PART_Right_Pressed"
            Opacity="{TemplateBinding PressedOpacity}"
            Background="{TemplateBinding Foreground}"
            Visibility="Collapsed" />
    <Border x:Name="PART_Right_Selection"
            Background="{TemplateBinding Foreground}"
            Visibility="Collapsed" />
  </Grid>
  <VisualStateManager.VisualStateGroups>
    ...
  </VisualStateManager.VisualStateGroups>
</Grid>
</ControlTemplate>

The only other change I needed to make was in MainPage I needed to change the instance of the MultiSwitchControl to reference the Style that I’d added. Now when I run my sample application I can see that the MultiSwitchControl looks dramatically different, and yet still functions the same way.

Wrapping up the Templated Control

As you’ve hopefully seen in this post there’s huge potential with a Templated Control to build a component that can be heavily reused and more importantly restyled. The point of Templated Controls, or lookless controls, is that the restyling shouldn’t change the core functionality.

What excites me about the Uno platform is that this stuff just works. The entire Templated Control I’ve walked through works on Android, iOS, Windows and WASM – what other technology allows you to do that, with the same ability to retemplate a control.

Don’t forget the full source code is available on GitHub

Deploying Uno Wasm using Blob Storage

Deploying Uno Wasm using Blob Storage

Earlier today I posted about deploying Uno on Wasm to an Azure App Service (to which the Uno team replied on Twitter with an updated web.config). I was thinking a bit more about how I would deploy a real Uno Wasm app and I realised that of course, there’s no server side logic, so I could just go host it off Blob Storage.

I ran up the Azure Storage Explorer and created a new container within an existing Blob Storage account

image

As I’m going to be serving up content directly from Blob Storage I need to make sure that I enable public read access on the individual blobs (the default is that there’s no public read access on the container or blobs). Right-click on the container and select Set Public Access Level

image

Set access to Public read access for blobs only

image

Now you can simply copy the Uno Wasm application into the container by dragging the files from File Explorer into the right pane of the Azure Storage Explorer. I found that the best folder to copy is the folder that Visual Studio uses to deploy files when you do a Publish. For my project this is found at FirstUnoProject.WasmobjReleasenetstandard2.0PubTmpOutFirstUnoProject.WasmdistbinReleasenetstandard2.0dist.

And that’s it – now I can run my Uno Wasm application by clicking the Copy URL from the tool bar and then switching to my browser and launching the corresponding URL.

image

The Uno Wasm application runs without any further configuration required.

image

Publishing Uno WebAssembly (Wasm) to Azure App Service

Publishing Uno WebAssembly (Wasm) to Azure App Service

I figured that since I had Uno working in WebAssembly locally on my machine that I’d try publishing it out to an Azure App Service. I mean, how hard could it be since Visual Studio recognises that the Wasm project is a web project (nice job there team Uno!) and even gives me the option to Publish.

image

Stepping through the publish wizard I decided to create a new Azure App Service

image

I made use of an existing Resource Group and App Service Plan that I had lying around.

image

After hitting Create and publish Visual Studio went off thinking for what seemed like a long time with nothing happening. I knew it was probably busy packaging and deploying but I didn’t see anything appear in the Output window…… not surprisingly because Visual Studio pushes all the logging for the publish operation to the Web Publish Activity window.

image

Once it was done Visual Studio launches a browser window displaying the root of the newly created App Service. Unfortunately, this is not my Uno project.

image

After investigating a little I realised that the publish operation was uploading the Wasm project to a sub folder of the wwwroot folder within the App Service (eg  wwwrootFirstUnoProject.WasmbinReleasenetstandard2.0distindex.html). I validated this by using the Advanced Tools from the Azure portal.

image

From the Advanced Tools, select Files

image

Browsing the files you can locate the index.html that gets published with the Wasm project. This is the file that hosts the wasm

image

Unfortunately just adding the appropriate path to the index.html to the site url doesn’t seem to work (ie this doesn’t work: https://firstunoproject.azurewebsites.net/FirstUnoProject.Wasm/dist/bin/Release/netstandard2.0/dist/index.html). However, you can easily set up a new application to point to the dist folder. Go to Application Settings and under the section Virtual  applications and directories, create a new mapping. In this case I’ve mapped /uno to the sitewwwrootFirstUnoProject.WasmdistbinReleasenetstandard2.0dist folder (you can get the folder from the “path” shown in the Kudo – Files explorer) and I’ve made it an Application.

image

If you now attempt to go to the index.html page in your new mapped folder (eg https://firstunoproject.azurewebsites.net/uno/index.html) you’ll find that you’ll see the “Loading…” text that comes with the Uno project template but your application won’t load. If you spin up the Chrome debugging tool you’ll see that it’s not able to find the mono.wasm file with a 404 being raised. Don’t bother trying to work out whether the file exists or not because the issue is that whilst the file exists, the Azure App Service isn’t going to serve it because it’s not a known file type. Luckily there’s a simple solution. Add the following Web.config to your Wasm project and publish your application again.

<?xml version=”1.0″ encoding=”UTF-8″?>
<configuration>
   <system.webServer>
     <staticContent>
       <remove fileExtension=”.clr” />
       <remove fileExtension=”.dll” />
       <remove fileExtension=”.json” />
       <remove fileExtension=”.wasm” />
       <remove fileExtension=”.woff” />
       <remove fileExtension=”.woff2″ />
       <mimeMap fileExtension=”.dll” mimeType=”application/octet-stream” />
       <mimeMap fileExtension=”.clr” mimeType=”application/octet-stream” />
       <mimeMap fileExtension=”.json” mimeType=”application/json” />
       <mimeMap fileExtension=”.wasm” mimeType=”application/wasm” />
       <mimeMap fileExtension=”.woff” mimeType=”application/font-woff” />
       <mimeMap fileExtension=”.woff2″ mimeType=”application/font-woff” />
     </staticContent>
     <httpCompression>
       <dynamicTypes>
         <add mimeType=”application/octet-stream” enabled=”true” />
         <add mimeType=”application/wasm” enabled=”true” />
       </dynamicTypes>
     </httpCompression>
   </system.webServer>
</configuration>

Now you should be able to launch your Uno wasm-based application hosted in Azure App Service

image

Updated Uno Visual Studio Extension

Updated Uno Visual Studio Extension

I just noticed that the Uno Visual Studio Extension has been updated, so I figured I’d give it a go in Visual Studio 2019. After installing the extension I used the new startup dialog on Visual Studio to “Create a new project”. I searched Uno but Cross-Platform or any of the target platforms would show you the templates.

image

After selecting the Cross-Platform App (Uno Platform) template I had to enter a few details before hitting Create

image

Seconds later I had a brand new solution with five projects giving me the ability to target iOS, Android and Wasm all from the code I write in a UWP app.

image

Selecting the Wasm project I set it as the startup project and hit F5 to run. After a few minutes of building (the first build is slow as it downloads packages etc) it launched Chrome with my fancy Hello World project running.

image

Note: Small confession that I had to run the project twice because the first time failed without error. On second attempt it ran successfully. However, it’s been a bit hit and miss in terms of getting it to run. I added “<MonoRuntimeDebuggerEnabled>true</MonoRuntimeDebuggerEnabled>” to the csproj file and that seems to have got it to run more reliably.

Adding Content

I wanted to add some content to my page but figured I’d start off slow – how about a couple of labels and a button:

<Page
     x_Class=”FirstUnoProject.MainPage”
    

    
    
    

    
     mc_Ignorable=”d”>
   <Grid Background=”{ThemeResource ApplicationPageBackgroundThemeBrush}”>
     <StackPanel>
       <TextBlock Text=”Hello, world !” Margin=”20″ FontSize=”30″ />
       <TextBlock Text=”Hello, world !” Margin=”20″ FontSize=”30″ />
       <TextBlock Text=”Hello, world !” Margin=”20″ FontSize=”30″ />
       <Button Content=”Press Me!” Click=”IveBeenPressed” />
     </StackPanel>
   </Grid>
</Page>

and a bit of code behind:

public async void IveBeenPressed(object sender, RoutedEventArgs e)
{
     await new MessageDialog(“I’ve been Pressed!”, “Pressed”).ShowAsync();
}

Now tell me you don’t expect this to work on the web, right? Sure enough works a treat.

image

It’d be great if the message box had “Pressed” as the title instead of “localhost:5210 says” but I’m still mind blown that this still works?

One thing to be aware of is that I’ve had some mixed experiences with Wasm as the resoluts seem to vary depending on which combination of Uno packages are referenced. This is definitely a work in progress but I feel that this is probably in line with where WebAssembly support is at across the board.

Web Development with NO JavaScript; Is Uno the Future Web Platform?

Web Development with NO JavaScript; Is Uno the Future Web Platform?

This is probably stating the obvious but I’m a big advocate for rich client applications. With the recent advances in PWAs I’m looking down the barrel of having to concede to all those fellow developers that have jacked themselves up on as many JavaScript frameworks they can find and now charge through the roof because they’re one of only a handful who actually understand the difference between == and ===. I’m sure the time will come but for now, it may be that the Uno Platform can help me leverage my UWP skills in building apps for iOS, Android and the Web (via WebAssembly).

In my previous post I covered Getting Started with Platform Uno where I simply used the extension to create a new project and discussed running it on various platforms. One of the features of UWP that I really like is that ability to gracefully handle different screen sizes through the use of Visual States. So I figured I’d give states a go in my basic Uno app. Here’s the XAML (note there’s no code behind) for my MainPage:

<Page x_Class=”UnoGettingStarted.MainPage”
      

      
      
      

      
       mc_Ignorable=”d”>
     <Grid>
         <VisualStateManager.VisualStateGroups>
             <VisualStateGroup x_Name=”SizeGroup”>
                 <VisualState x_Name=”Small”>
                    <VisualState.StateTriggers>
                         <AdaptiveTrigger MinWindowWidth=”0″ />
                     </VisualState.StateTriggers>

                     <VisualState.Setters>
                         <Setter Target=”MainContent.Visibility”
                                 Value=”Collapsed” />
                     </VisualState.Setters>
                 </VisualState>
                 <VisualState x_Name=”Large”>
                    <VisualState.StateTriggers>
                         <AdaptiveTrigger MinWindowWidth=”1000″ />
                     </VisualState.StateTriggers>

                 </VisualState>
             </VisualStateGroup>
         </VisualStateManager.VisualStateGroups>
         <StackPanel VerticalAlignment=”Stretch”
                     x_Name=”MainContent”
                     HorizontalAlignment=”Stretch”>
             <TextBlock Text=”Hello, world !”
                        Margin=”20″
                        FontSize=”30″ />
             <Button Content=”Test”
                     Click=”TestClick” />
         </StackPanel>
     </Grid>
</Page>

This XAML shows two Visual States, “Small,” hides the MainContent StackPanel, and “Large” which has no setters, so is the default states of the page. I’ve added an AdaptiveTrigger to both states for clarity. The “Small” Visual State for window sizes from 0; The “Large” Visual State for window sizes over 1000.

Whilst this is a relatively simple example, when I build and run this, it not only works on UWP (expected) but also on Wasm. The image below shows the output – the red text has been added afterwards to indicate which Visual State the page is in.

image

Serious props to the Platform Uno team, this is awesome.

Getting Started with Platform Uno

Getting Started with Platform Uno

A while ago I posted on Building a TipCalc using Platform Uno and at the time there was quite a few steps to jump through to get a basic application running from scratch. In this post I’m going to cover off how incredibly easy it now is to get started with the Uno Platform. Before I get into the steps, I want to give some background on why after almost a year am I coming back to looking at Uno. For those who have been reading my blog or have worked with Built to Roam you’ll know that we specialise in building cross-platform applications, whether it be a mobile app spanning iOS and Android, a Windows app targeting desktop and Xbox, or an enterprise solution that’s available across web, mobile and desktop. With a deep heritage in the Microsoft ecosystem we have seen the emergence of technologies such as Xamarin.Forms – historically this was rudimentary framework for rapidly developing forms based applications, primarily for line of business solutions. We’ve also seen other frameworks emerge such as React Native, Flutter and of course PWAs. Each framework has its advantaged and disadvantages; each framework uses a unique set of tools, workflow and languages. The question we continually ask ourselves is which framework is going to provide the best value for our customers and that will allow us to build user interfaces that include high fidelity controls and rich animation.

We also evaluate frameworks based on the target platforms that they support, which is what has led me to this post. One of the amazing things about Xamarin.Forms is that it has provided support for the three main platforms, iOS, Android and Windows, as part of the core platform. In fact the tag line is currently “Native UIs for iOS, Android and Windows from a single, shared codebase”.

image

What’s mind blowing is if you look at the Other Platforms page you’ll see that there is also support for GTK, Mac, Tizen and WPF. Unfortunately, these other platforms do not get the same love as the core platforms, so don’t expect them to be kept up to date with the latest releases.

At this point you might be thinking, why stray from Xamarin.Forms? Well in recent times there has been a shift away from supporting UWP as a core component of Xamarin.Forms. When asked on Twitter about UWP support for Shell, David Ortinau’s response was just another nail in the coffin for UWP app developers who are already struggling an up hill battle to convince customers of the value proposition of building for Windows.

image

So this leads me to again revisit other frameworks but we find the situation isn’t much better:

  • React Native – iOS and Android only – There is a React Native for Windows but again, it’s not part of the core offering, so it will trail (perhaps not by much but the commit history indicates a difference between 1 day ago for the main Reach Native repo and 26 days ago on the Windows repo, for the most recent commit).
  • Flutter – iOS and Android only – There is work on desktop embedding and having Flutter work on the web, so perhaps we’ll see more support beyond Google I/O
  • PWAs – varying level of support on different platforms – Clearly Microsoft sees this as the path forward for some of their Office suite of apps but the lack of native UI I think is still a limitation of PWAs.

At this point I remembered that Uno provided an interesting take on cross platform development. I also remembered that they’ve been doing a lot of work to support WebAssembly, so perhaps this could be the perfect solution. In the simplest form, the Uno Platform is #uwpeverywhere (an initiative that I’ve long believed Microsoft should have championed, after all it’s called Universal for a reason, right?) but beyond that Uno is about being able to “Build native apps for Mobile and Web using XAML and C#”.

image

Let’s get cracking with building a Uno application and see how it pans out. If you’re on the Uno Platform homepage and wondering how to get started, don’t worry, you’re not alone – I was looking for a big “Get Started” button but instead I see links to sample apps and to the source code. If you find yourself across at the source code, you’re about to embark down the wrong path – you don’t need to grab their source code as everything is distributed via nuget! So where do you go? Well you need to find the Uno Documentation and then click the link to Getting Started – now we’re cooking with gas! Unfortunately most of this page is pretty useless until you come to want to debug your application on WebAssembly. Instead what you really want to do i install the Uno Visual Studio Extension and use that to create your application.

Getting Started

  • Install the Uno Visual Studio Extension
  • Open Visual Studio and select File, New, Project
  • Search for “cross platform” and select the Cross-Platform App (Uno Platform) template

image

  • Important: Update nuget package references – if you don’t do this, it’s unlikely that your application will run as a WebAssembly (check the “Include prerelease” to get the latest update for Wasm support)

image

  • For WebAssembly in the csproj file:
    • Add <DotNetCliToolReference Include=”Uno.Wasm.Bootstrap.Cli” Version=”1.0.0-dev.214″ /> into the same ItemGroup as the PackageReference for Uno.Wasm.Bootstrap
    • Add <MonoRuntimeDebuggerEnabled>true</MonoRuntimeDebuggerEnabled> to the initial PropertyGroup
    • Change the Project element to <Project Sdk=”Microsoft.NET.Sdk.Web”>
  • Build and run each platform – I’m showing UWP, Android and WebAssembly here but iOS works straight from the template too

image

So, now that we have a UWP application that runs on iOS, Android, Windows and Web, are we satisfied with this as a cross platform solution? I think I’m enjoying working in UWP again but it’ll take a bit more investigation to see if this is a viable solution or not.

———-

Contact Built to Roam for more information on building cross-platform applications

———-

Building a TipCalc using Platform Uno

Building a TipCalc using Platform Uno

Recently I’ve been playing around with Platform.Uno – I’ve long held the view that Microsoft’s Xamarin.Forms was a stop-gap in delivering a true cross-platform solution. Whilst Xamarin.Forms is a highly productive platform, it has some severe limitations, specifically around overriding the look and feel of the native controls. This is where Flutter has stolen a lot of mindshare, mainly because customers and companies looking to build apps, no longer look for apps that match the platform style. They’re much more focussed on strong brand presence and great user experience, meaning that a great Material app on iOS will get better reviews and usage than a mediocre app that uses the default iOS style. Platform Uno attempts to deliver on what I believe the Universal Windows Platform (UWP) should have been – taking UWP XAML across iOS, Android and Web Assembly.

Anyhow, here goes with building a TipCalc. I’ll start with the basic UWP application:

I’ll create a separate project for our ViewModel:

Running the UWP app at this point gives an operational tip calculator

image

So now, let’s add some Uno goodness. We’ll start by moving the MainPage.xaml into its own class library:

  • Add a new project based on the Class Library (.NET Standard) project template, TipCalc.UI
  • Remove class1.cs
  • Add reference to project TipCalc.Core
  • Move MainPage.xaml and MainPage.xaml.cs to the TipCalc.UI project
  • Change namespace from TipCalc.UWP to TipCalc.UI for both MainPage.xaml and MainPage.xaml.cs
  • Edit TipCalc.UI.csproj to allow it to multi-target (ie generate multiple dlls that target UWP, Droid, iOS etc) and make sure correct build action is set for MainPage.xaml
  • Add reference to Uno.UI nuget package (currently in prerelease).
  • Final TipCalc.UI.csproj should look similar to
  • <Project Sdk=”MSBuild.Sdk.Extras/1.5.4″>
       <PropertyGroup>
         <TargetFrameworks>xamarinios10;MonoAndroid80;uap10.0</TargetFrameworks>
       </PropertyGroup>


      <PropertyGroup Condition=” ‘$(TargetFramework)’ == ‘uap10.0’ “>
         <GenerateLibraryLayout>true</GenerateLibraryLayout>
       </PropertyGroup>


      <ItemGroup Condition=” ‘$(TargetFramework)’ == ‘uap10.0’ “>
         <PackageReference Include=”Microsoft.NETCore.UniversalWindowsPlatform” Version=”6.1.5″ />
       </ItemGroup>


      <ItemGroup>
         <PackageReference Include=”Uno.UI” Version=”1.31.0-dev.52″ />
       </ItemGroup>


      <ItemGroup>
         <Page Include=”MainPage.xaml”>
           <SubType>Designer</SubType>
           <Generator>MSBuild:Compile</Generator>
         </Page>
       </ItemGroup>


      <ItemGroup>
         <ProjectReference Include=”..TipCalc.CoreTipCalc.Core.csproj” />
       </ItemGroup>
    </Project>

  • Add Uno.UI nuget reference to TipCalc.Core. Resulting csproj should look similar to
  • <Project Sdk=”MSBuild.Sdk.Extras/1.5.4″>
       <PropertyGroup>
         <TargetFrameworks>xamarinios10;MonoAndroid80;uap10.0</TargetFrameworks>
       </PropertyGroup>


      <ItemGroup>
         <PackageReference Include=”Uno.UI” Version=”1.31.0-dev.52″ />
       </ItemGroup>
    </Project>

  • Add reference to TipCalc.UI to TipCalc.UWP

By all accounts at this point you’ve done a fair bit of lifting with out much to show for it. In fact, because of some absolute genius coding by Microsoft, if you run the UWP application at this point you’ll most likely see a System.AccessViolationException when attempting to navigate to MainPage

image

This seems to be an issue where you’re attempting to navigate to a page that’s defined in a separate assembly and there are no other XAML pages defined in the head (ie UWP) project. Simple solution is just to add a dummy page to the UWP project that you’ll never use…. but seriously Microsoft??? Oh, and this isn’t the first time this has been pointed out (see https://social.msdn.microsoft.com/Forums/windowsserver/en-US/4efa91ad-fa8f-45f0-9864-c2fd2b24477c/uwpc-accessviolationexception-when-navigating-froma-another-assembly?forum=wpdevelop which links to an old post http://danielvaughan.org/posts/uwp/2015/08/15/UWP-AccessViolationException-when-Navigating-to-a-Page-in-Another-Assembly/)

Right, so the question is – after all this effort, how are we any closer to having a cross platform application. Well, let’s spit out the Android build:

  • Add a new project based on the Android App (Xamarin) project template, TipCalc.Droid
    image
  • Use the Blank App template
  • Uninstall any of the Xamarin.Android.Support nuget packages that are installed by default
  • Add reference to Uno.UI nuget package
  • Add reference to both TipCalc.UI and TipCalc.Core
  • Add a reference to Mono.Android.Export
    image
  • Add App.xaml and App.xaml.cs from the TipCalc.UWP project to the TipCalc.Droid project As Link
    image
  • Alter MainActivity to the following
  • [Activity(
         MainLauncher = true,
         ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.ScreenSize,
         WindowSoftInputMode = SoftInput.AdjustNothing | SoftInput.StateHidden
    )]
    public class MainActivity : Windows.UI.Xaml.ApplicationActivity
    {
    }

  • Add an Application class as follows
  • [global::Android.App.ApplicationAttribute(
         Label = “@string/app_name”,
         LargeHeap = true,
         HardwareAccelerated = true,
         Theme = “@style/AppTheme”
    )]
    public class Application : Windows.UI.Xaml.NativeApplication
    {
         public Application(IntPtr javaReference, JniHandleOwnership transfer)
             : base(new App(), javaReference, transfer)
         {
         }
    }

Now we’re good to go. Set the TipCalc.Droid to be the start up project and off we go. The app looks basically the same as it did on UWP

image

This took quite a bit of effort to get going but now that it’s there, you can imagine that productivity is going to be quite good. Next up I’ll add in the iOS project but I don’t envisage that, nor Web Assembly, being that hard from the samples I’ve seen to date.

Source code available at https://github.com/nickrandolph/UnoSamples