Content from Former Microsoft WPF and Silverlight Team Member

Someone pointed me in the direction of a series of great blog posts that have recently been migrated to a github repository. The posts are quite old, dating back to a period between 2005 and 2013 when Beatriz Stollnitz worked for Microsoft as part of the WPF and Silverlight teams. Here are the links to each of the posts – happy reading!

Update 11th August: I’ve forked the repository and have started to work my way through the sample code to a) update it to WPF on .NET 4.7.2 and b) provide a similar example for UWP + Uno (iOS, Android, WASM). Feel free to check it out and help out!

01-DataContext

02-EmptyBinding

03-GetListBoxItem

04-BindToComboBox

05-DisplayMemberPath

06-SelectedValue

07-ChangePanelItemsControl

08-BarGraph

09-CollectionViewSourceSample

10-MasterDetail

11-MasterDetailThreeLevels

12-DataBoundDialogBox

13-TemplatingItems

14-SortingGroups

15-GroupingTreeView

16-GroupByType

17-BoundListView

18-ThreeLevelMasterDetailADO

19-ObjectDataProviderSample

20-InsertingSeparators

21-CustomSorting

24-AsynchronousBinding

25-BindToEnum

26-DataTriggerSample

27-ConvertXaml

28-FilterSample

29-MultipleFilters

30-MultiBindingConverter

31-ChangesMultithreading

32-PolygonBinding

33-PolygonBinding2

34-PolygonBinding3

35-CommonQuestions

36-ADOIndependentView

37-PlanetsListBox

38-UpdateExplicit

39-TreeViewPerformancePart1

40-TreeViewPerformancePart2

41-TreeViewPerformancePart3

42-WPFPresenter

43-BindToXLinq

44-XLinqXMLMasterDetail

45-DebuggingDataBinding

46-DragDropListBox

47-ExpandTreeViewPart1

48-ExpandTreeViewPart2

49-ExpandTreeViewPart3

51-UIVirtualization

52-DataVirtualization

54-PieChartWithLabels

55-PieChartWithLabelsSilverlight

56-PieChartWithLabelsSilverlight

57-DataVirtualization

58-MultipleStyles

59-WPFCollectionViewSource

60-SLCollectionViewSource

61-OredevComputerWeekly

62-DataVirtualizationFiltering

64-DataVirtualizationFilteringSorting

66-SortingHierarchy

67-PieChartWithLabelsUpdates

69-BindRadioButtonsToEnumsPart1

70-BindRadioButtonsToEnumsPart2

71-BindRadioButtonsToEnumsPart3

72-BindRadioButtonsToEnumsPart4

73-BindRadioButtonsToEnumsPart5

74-PositioningDataBoundItems

75-SimultaneousEnableDisable

76-FocusWatcher

77-CaptureWatcher

78-BetterBindableBase

79-BooleanConverters

Using the UWP SplitView on iOS, Android and WebAssembly with Uno

In this post we’re going to cover one of the basics of app navigation which is the use of the UWP SplitView. If you’re coming from iOS and Android development you might be thinking “huh, I don’t even know what that is.” Well the good news is that it’s actually something you’re already familiar with. Whether you’re used to an app that has a master-details layout, or one that uses a burger menu to display a flyout menu, these can both be implemented using the UWP SplitView.

One thing to be aware of is that the UWP SplitView is one of the basic controls that was added early in the UWP lifecycle. Since then there have been other controls added, such as the NavigationView and the MasterDetailsView (Windows Community Toolkit) that provide extended functionality and are worth exploring depending on the requirements of your project.

New Project – Cross-Platform App (Uno Platform)

Let’s get into it – we’re going to start with creating a new project based on the latest Uno Visual Studio Extension (updated at end of July – if you’re using an older version I would recommend updating). I’m also working with the Visual Studio 2019 (16.3 preview 1.0) as this provides the best support for working with WebAssembly.

Cross-Platform App using the Uno Solution Template

Once you’ve created your solution, I would highly recommend that you a) update NuGet references and b) go through each platform and make sure you can build and run the application. However, as at the time of writing do NOT update the Microsoft.Extensions.Logging.* packages – there’s a note in the csproj files that states “Note that for WebAssembly version 1.1.1 of the console logger required.” My recommendation is to leave these packages at v1.1.1 across all projects. For the Uno libraries I pick the latest prerelease versions but be warned that this occasionally backfires as you may get an unstable version – this is why it’s important to run each platform before proceeding!!

Note: One issue I’m currently seeing in the Uno template is that it generates assets (i.e. images) with names that include the scale factor eg scale-200. This is fine for UWP but fails to build for Android. I’ve gone through and just removed the scale factor from the filenames. You’ll need to rebuild your UWP project to make sure it picks up the filename changes correctly.

Note 2: When running the WebAssembly (aka WASM) project, make sure you select “Start without Debugging” in Visual Studio.

Note 3: When running the iOS build you may need to set the Deployment Target in the Info.plist. If you’re using the latest preview of Visual Studio it will pick up iOS 12.4 on the build agent which will cause you app to fail to deploy if you haven’t set the Deployment Target.

Adding the UWP SplitView

Now that we have our new application up and running, it’s time to add the SplitView control.

Design in Blend

For this we’re going to switch over to Blend – yeh, the product still exists and you’ll see in a minute why we’re going to be using it. To make the switch, right-click on the MainPage.xaml in the shared project and select Design in Blend.

Switching to Design in Blend

Switching to Blend will take a few seconds, particularly if this is the first time you’ve ever opened Blend. You’ll also see errors as the iOS and Android projects won’t open – ignore these and don’t worry about the upgrade log that will popup (these are irritating but can be safely ignored as they won’t impact your project).

Despite selecting Design in Blend from the MainPage.xaml, once in Blend, you’ll actually need to use the Solution Explorer window to open MainPage.xaml in the designer. Once you’ve opened MainPage.xaml, Blend should feel fairly familiar, even if this is the first time you’ve used it. This is because a lot of the windows are shared with Visual Studio – the separation between the products is a mix of legacy (don’t want to disrupt the tiny percentage of developers who still use it) and the optimising that design tasks will be done using Blend.

Blend Designer

Adding the SplitView

There are a number of ways to add the SplitView control into the MainPage. If you’re familiar with writing XAML it’s probably just easiest to add the SplitView element. However, to build familiarity with Blend, let’s add the SplitView using the designer. In the Assets window, enter “splitview” to locate the SplitView control. Use the mouse to drag the SplitView down to the Objects and Timeline window at the position in the hierarchy where you want it to appear. In this case we’re going to drop it inside the existing Grid – I typically have a Grid as the root of most pages to allow setting of background and other properties using a application-wide style, independently of what content is nested within the Grid.

Adding the UWP SplitView

Adding the SplitView this way will add it as the second child of the Grid, immediately following the existing TextBlock. As we want the SplitView to take up the entire space of the page, we’re going to move the TextBlock in the main content area by dragging the TextBlock into the Grid that is nested within the SplitView. The XAML of the page should now look like the following:

<Page
    x:Class="UwpSplitViewSample.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:local="using:UwpSplitViewSample"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d">
    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
        <SplitView>
            <SplitView.Pane>
                <Grid />
            </SplitView.Pane>
            <Grid>
                <TextBlock
                    Margin="20,20,20,20"
                    FontSize="30"
                    Text="Hello, world !" />
            </Grid>
        </SplitView>
    </Grid>
</Page>

The SplitView is made up of two distinct areas: the main content area and the pane that can be shown/hidden (i.e. the flyout). From this code we can see that the SplitView as two nested XAML elements which align to the SplitView areas. The first is the SplitView.Pane which defines the layout of the content that will appear within the pane/flyout of the SplitView. The second defines the layout of content to appear within the main content area of the SplitView, in this case a Grid with a TextBlock contained within it.

Adjusting Layout with Blend

I’ll repeat this: For those of us familiar with writing XAML, you’ll find that manually crafting XAML is by far the quickest way to add and edit elements. However, Blend provides a number of shortcuts for positioning elements that can come in handy. Some of which we’ll cover here.

Designing the SplitView Pane

For the purpose of this post, all we’re going to do is to add a StackPanel with a TextBlock and Button as children to both the SplitView.Pane and the SplitView. The TextBlock will be used to indicate what part of the SplitView it is (Pane or Content) and the Button will be used to Show and Hide the pane of the SplitView.

Let’s start with the SplitView.Pane which already has a Grid element. In the Objects and Timeline window, right-click on the Grid and select Change Layout Type, followed by StackPanel.

Change Layout Type to StackPanel

You should see the Grid change to StackPanel and remains selected in the Objects and Timeline window. Next, with the StackPanel still in focus, use the Assets window to locate the TextBlock and double-click on TextBlock to add a TextBlock as a child to the StackPanel. Repeat, this time for a Button.

We’re going to position the StackPanel in the centre of the SplitView pane. To do this, again keep the StackPanel selected in the Objects and Timeline window, we’re going to go to the Properties window and scroll down to the Layout section at set the HorizontalAlignment and VerticalAlignment properties to Center.

Centering the StackPanel

Whilst we’re using the Properties window, select the TextBlock and change the Text property to “Pane”, and then select the Button and change the Content property to “Hide Pane” (Note that unlike Xamarin.Forms that has a Text property, to set the text on a Button in UWP XAML you need to set the Content property).

The last thing we’re going to do with the XAML for the SplitView.Pane is to add an event handler to the Click event of the Button. With the Button selected in the Objects and Timeline window, go to the Properties window, and click on the lightning bold icon to switch from properties to events view. Locate the Click event and type the name of the event handler you want to create, in this case PaneButtonClick. When you press Enter the PaneButtonClick method will be created in the MainPage.xaml.cs code behind file and the XAML will be updated with the Click property.

Adding an Event Handler for the Click Event

For the moment, that’s where we’ll leave the XAML for the SplitView.Pane and we’ll implement the logic for the Click event handler shortly.

Designing the SplitView Content

The layout of the SplitView content is going to be very similar to that of the pane: A StackPanel with nested TextBlock and Button. However, we’re going to go about it slightly differently, considering the fact that we already have a TextBlock nested in a Grid in the content area. We’re going to start by wrapping the TextBlock in a StackPanel. To do this, right-click somewhere in the TextBlock on the designer and select Group Into, followed by StackPanel (this also works if you right-click on the TextBlock in the Objects and Timeline window).

After doing this the hierarchy of elements in the SplitView content will be Grid, StackPanel, TextBlock. However, if you look at the XAML you’ll notice that Blend has added some additional attributes, setting the Margin on the StackPanel and the Height and Width on the TextBlock. To remove these, we can just right-click the StackPanel and select Layout, followed by Reset All. Repeat this on the TextBlock.

From here we can repeat the remaining steps that we did for the SplitView pane:

  • Add a Button to the StackPanel
  • Set HorizontalAlignment and VerticalAlignment on the StackPanel to Center
  • Set Text on the TextBlock to “Content Area”
  • Set Content on the Button to “Show Pane”
  • Add an event handler called ShowPaneButtonClick to the Button Click event

The final XAML in MainPage.xaml should look similar to:

<Page x:Class="UwpSplitViewSample.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:local="using:UwpSplitViewSample"
      xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
      mc:Ignorable="d">
  <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
    <SplitView>
      <SplitView.Pane>
        <StackPanel HorizontalAlignment="Center"
                    VerticalAlignment="Center">
          <TextBlock Text="Pane"
                     TextWrapping="Wrap" />
          <Button Click="PaneButtonClick"
                  Content="Hide Pane" />
        </StackPanel>
      </SplitView.Pane>
      <Grid>
        <StackPanel HorizontalAlignment="Center"
                    VerticalAlignment="Center"
                    Orientation="Vertical">
          <TextBlock Text="Content Area" />
          <Button Click="ShowPaneButtonClick"
                  Content="Show Pane" />
        </StackPanel>
      </Grid>
    </SplitView>
  </Grid>
</Page>

DisplayMode Property on the UWP SplitView

The UWP SplitView has two properties that are worth exploring in a bit of detail and we’ll use these to control the behaviour, or should I say the layout, of the SplitView based on screen width. Let’s start with the easy one, which is the IsPaneOpen property. As you can imagine from the name, this property indicates whether the pane of the SplitView is currently open (i.e. displayed in full, rather than hidden or in compact mode), or not.

The second property worth looking at is the DisplayMode of the SplitView, and has the following four possible values:

  • Overlay – When the pane is visible it appears as an overlay across the content area. It doesn’t affect the size of the content area when it is shown/hidden.
  • Inline – When the pane is displayed it reduces the size of the content area by the width of the pane. As the pane is shown/hidden the width of the content area shrinks and grows accordingly.
  • CompactOverlay – Same as Overlay, except when the pane isn’t open, it still takes up space on the page equal to the CompactPaneLength property. Typically the compact view would show a list of icon buttons as a summary of the options available in the pane when it’s open.
  • CompactInline – Same as Inline, except when the pane isn’t open it still takes up space on the page equal to the CompactPaneLength property.

We’ll leave the use of the CompactOverlay and CompactInline options for the moment and focus on the other two options. The Overlay option is great for when the size of the application is narrow, for example on a mobile phone. The Inline option is better suited for when the application has sufficient width to allow the pane to be shown and there still to remain sufficient content area to be useful.

Using VisualState to Set DisplayMode

With this in mind, we’re going to use visual states to set the DisplayMode property based on the width of the application. For the purpose of this post we’re going to use a split point of 900, meaning that if the width of the application is greater than 900 the DisplayMode will be set to Inline. If it’s below 900 it’ll be set to Overlay.

To begin with, we’re going to set the default value of the DisplayMode to Compact by adding DisplayMode=”Compact” to the SplitView element. Next in Blend we’re going to add two visual states via the States window. In the States window, click the Add state group button and give the state group a name, SizeStateGroup.

Adding a Visual State Group

Next, click the Add state button twice to add two visual states and name them NarrowState and WideState. The actual names of the visual state group and the states are only for your benefit at this point, so you can name them according to what makes sense for you.

Adding a Visual State

Click on the WideState and you should see a red dot appear along side the state name. This indicates that Blend is in visual state editing mode. You should also see a red border appear around the main design area. Note that when you’re in visual state editing mode in Blend, any change you make via the tool windows (eg changing a property) will be tracked against the selected visual state.

Visual State Editing Mode

In visual state editing mode for the WideState, use the Properties window to change the DisplayMode to Inline. Next click the lightning icon button, Edit Adaptive Triggers, alongside the WideState visual state.

Adding an AdaptiveTrigger

In the Collection Editor: StateTriggers, set the MinWindowHeight to 0 and the MinWindowWidth to 900. This is adding a trigger which will return true when the width of the application exceeds 900. At this point the VisualStateManager will automatically switch to the WideState, without you having to explicitly run any code. Repeat the process of setting an adaptive trigger but this time for the NarrowState and set both MinWindowHeight and MinWindowWidth to 0.

The final XAML:

<Page x:Class="UwpSplitViewSample.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:local="using:UwpSplitViewSample"
      xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
      mc:Ignorable="d">

  <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
    <SplitView x:Name="splitView"
               DisplayMode="Overlay">
      <SplitView.Pane>
        <StackPanel HorizontalAlignment="Center"
                    VerticalAlignment="Center">
          <TextBlock Text="Pane"
                     TextWrapping="Wrap" />
          <Button Click="PaneButtonClick"
                  Content="Hide Pane" />
        </StackPanel>
      </SplitView.Pane>
      <Grid>
        <StackPanel HorizontalAlignment="Center"
                    VerticalAlignment="Center"
                    Orientation="Vertical">
          <TextBlock Text="Content Area" />
          <Button Click="ShowPaneButtonClick"
                  Content="Show Pane" />
        </StackPanel>
      </Grid>
    </SplitView>
    <VisualStateManager.VisualStateGroups>
      <VisualStateGroup x:Name="SizeStateGroup">
       
        <VisualState x:Name="WideState">
          <VisualState.StateTriggers>
            <AdaptiveTrigger MinWindowHeight="0"
                             MinWindowWidth="900" />
          </VisualState.StateTriggers>
          <VisualState.Setters>
            <Setter Target="splitView.(SplitView.DisplayMode)" Value="Inline" />
          </VisualState.Setters>
        </VisualState>
        <VisualState x:Name="NarrowState">
          <VisualState.StateTriggers>
            <AdaptiveTrigger MinWindowHeight="0"
                             MinWindowWidth="0" />
          </VisualState.StateTriggers>
        </VisualState>
      </VisualStateGroup>
    </VisualStateManager.VisualStateGroups>
  </Grid>
</Page>

Note: In UWP the order of the VisualState elements doesn’t matter as all the AdaptiveTriggers are evaluated and the best match is determined. For example if the application width is 1000, the conditions for the triggers for both NarrowState and WideState are met. However, the WideState is a better match since the width is > 900. For Uno (i.e. iOS, Android and WebAssembly), the order matters – the first trigger to match, wins. In the above XAML you’ll see the WideState before the NarrowState to make sure that if the application width is > 900, the WideState visual state is active.

Opening and Closing the UWP SplitView Pane

If you run the application at this point and adjust the width of the application the DisplayMode property will switch between Inline and Overlay. However, you won’t notice any change because we currently don’t have a way to open the pane. To do this we’ll add some code to the Click event handlers we created earlier, as follows:

private void PaneButtonClick(object sender, RoutedEventArgs e)
{
    splitView.IsPaneOpen = false;
}
private void ShowPaneButtonClick(object sender, RoutedEventArgs e)
{
    splitView.IsPaneOpen = true;
}

UWP SplitView In Action

With all the XAML and code in place, let’s see it in action on all various platforms.

UWP with Resizing Application
Opening the Pane on WASM
Auto-dismiss Flyout on Android

In this post we’ve covered off how easily you can build a responsive interface using Blend and the UWP SplitView. The fact that it then just works on iOS, Android and WebAssembly is a big bonus thanks to the Uno platform.

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

Optimising Multi-Targeting with Visual Studio Solution Filters

Over time Visual Studio has progressively improved support for solutions that have a large number of projects. MvvmCross used to be over 200 projects to handle each of the target platforms it supports. With the introduction of multi-targeted projects the number of projects dropped significantly to around 50 projects. For example, the core MvvmCross project has 10 target framework monikers (TFM), instead of having a separate project for each framework. However, the decrease in load time (due to fewer projects) was offset by an increase in build time. Rebuilding the MvvmCross project triggers a build for each TFM, so the project is built 10 times. In this post, I’ll walk through a couple of techniques we use to reduce the build time when working with MvvmCross.

Whilst MvvmCross supports a wide range of target platforms, the reality is that when I’m making changes to MvvmCross I’ll be doing most of the work against one platform. For example being on Windows I might pick Android or Windows and use either the Playground or Playground.Forms sample apps to run up and test my changes. Since I’m working with only one platform at any given time, I don’t need every target framework to be built every time I make a change. Unfortunately there’s no way in Visual Studio to tell it not to build every target framework.

Conditional Target Frameworks

To get around the limitations of Visual Studio we’ve introduced some conditional logic into the project files that determine which TFMs are built. If we look at the top of the MvvmCross.csproj file we can see that there are a number of TFM lists.

<Project Sdk="MSBuild.Sdk.Extras">
  <PropertyGroup Condition=" '$(TargetsToBuild)' == 'All' ">
    <TargetFrameworks Condition=" '$(OS)' == 'Windows_NT' ">netstandard2.0;net461;Xamarin.iOS10;Xamarin.Mac20;Xamarin.TVOS10;Xamarin.WatchOS10;MonoAndroid90;tizen40;netcoreapp2.1;uap10.0.16299</TargetFrameworks>
    <TargetFrameworks Condition=" '$(OS)' != 'Windows_NT' ">netstandard2.0;net461;Xamarin.iOS10;Xamarin.Mac20;Xamarin.TVOS10;Xamarin.WatchOS10;MonoAndroid90;tizen40;netcoreapp2.1</TargetFrameworks>
  </PropertyGroup>

  <PropertyGroup Condition=" '$(TargetsToBuild)' != 'All' ">
    <TargetFrameworks Condition=" '$(TargetsToBuild)' == 'Android' ">netstandard2.0;MonoAndroid90;</TargetFrameworks>
    <TargetFrameworks Condition=" '$(TargetsToBuild)' == 'Uap' ">netstandard2.0;uap10.0.16299</TargetFrameworks>
    <TargetFrameworks Condition=" '$(TargetsToBuild)' == 'iOS' ">netstandard2.0;Xamarin.iOS10</TargetFrameworks>
  </PropertyGroup>

In the Directory.build.props file for MvvmCross, which we’ll come to in a minute, we’ve defined a property called TargetsToBuild. If this is set to ‘All’, we set the TargetFrameworks property to include all the TFMs (except when the OS isn’t Windows_NT where we leave out Uap). However, if the TargetsToBuild is something different we restrict the TargetFrameworks property to the appropriate TFM (eg MonoAndroid90 when the TargetsToBuild is ‘Android’). We also include netstandard2.0 to help ensure developers don’t accidentally include platform specific code in files that are used by all platforms.

I mentioned that we define the TargetsToBuild in the Directory.build.props file. Currently what you’ll find in the Directory.build.props file in the MvvmCross repository is something that looks like the following:

<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
  <TargetsToBuild>All</TargetsToBuild> 
  <!--<TargetsToBuild>Android</TargetsToBuild>-->
  <!--<TargetsToBuild>Uap</TargetsToBuild>--> 
  <!--<TargetsToBuild>iOS</TargetsToBuild>--> 
</PropertyGroup>

<PropertyGroup Condition=" '$(Configuration)' != 'Debug' ">
  <TargetsToBuild>All</TargetsToBuild>
</PropertyGroup>

The first PropertyGroup, which is used when the build Configuration is set to Debug, allows the developer to switch TargetsToBuild by simply commenting and uncommenting the different values. For example to switch to Android I simply comment out the line with ‘All’ in it and uncomment the ‘Android’ line. Note that you have to restart Visual Studio for this change to take effect.

Solution Filters (.slnf)

Using the TargetsToBuild property works really well and significantly cuts down on the build time when doing development with MvvmCross. However, whenever I step in to work on MvvmCross I find it frustrating that Visual Studio has to load all 50+ projects when I’m not going to use them all. I’ve noticed recently that a couple of OSS projects such as Platform.Uno and Allan Richie’s Shiny have started to include solution filter files (.slnf). Solution Filter files make it easy to open a solution with only a subset of projects loaded.

There’s plenty of documentation on how to create a solution filter file, so I’m not going to cover that here. However, for MvvmCross it makes sense to have different solution filters for work with each platform. In my PR I’ve created filters for All, Android, iOS and Uap, which match the different TargetsToBuild options (the reason for this will become evident shortly).

Launch Automation

I was looking around for a way to combine solution filtering with the conditional logic for TFMs. Unfortunately there’s currently no mechanism for linking them. What this means is that developers wanting to work for particular platform have to first set the TargetsToBuild in the Directory.build.props, and then open the solution using the appropriate filter file.

I figured that if I can’t link the solution filter with a specific list of TFMs, the least I could do was to automate the process of setting the TargetsToBuild and launching the solution. For this I created a series of .bat files (yeh, old school I know): LaunchVS.All.bat, LaunchVS.Android.bat, LaunchVS.iOS.bat and LaunchVS.Uap.bat. Each of these platform specific launch files invokes “LaunchVS.bat XXX” where XXX is the corresponding platform (eg “LaunchVS.bat Android”).

The LaunchVS batch file does two things:

  • Invokes a powershell command that does a find and replace to update a property, TargetsToBuildDeveloperOverride, in the Directory.build.props file.
  • Launches Visual Studio by starting the corresponding solution filter file. Hence the reason why the names of the filter files needed to match the platforms options for TargetsToBuild.
powershell -Command "(gc Directory.build.props) -replace '<TargetsToBuildDeveloperOverride>[a-zA-Z]*</TargetsToBuildDeveloperOverride>', '<TargetsToBuildDeveloperOverride>%~1</TargetsToBuildDeveloperOverride> ' | Out-File -encoding ASCII Directory.build.props"
start MvvmCross.%~1.slnf

At this point you might be wondering why we’re updating the TargetsToBuildDeveloperOverride property instead of TargetsToBuild. The reason for this is that if we simply did a find-and-replace using the TargetsToBuild property it would replace both the value used for the Debug configuration but also the other configurations. It’s important that we don’t modify the Release configuration by accidentally changing the TFMs, since this could then accidentally be committed to the repository. To avoid this problem I created the TargetsToBuildDeveloperOverride property which is only used to set the TargetsToBuild property for the Debug configuration.

<PropertyGroup>
  <TargetsToBuildDeveloperOverride>Uap</TargetsToBuildDeveloperOverride>          
</PropertyGroup>

<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
  <TargetsToBuild>$(TargetsToBuildDeveloperOverride)</TargetsToBuild> 
</PropertyGroup>

<PropertyGroup Condition=" '$(Configuration)' != 'Debug' ">
  <TargetsToBuild>All</TargetsToBuild>
</PropertyGroup>

With these changes, all a developer has to do when working with a particular platform is to double-click the appropriate .bat file. The Directory.build.props will be updated and the corresponding .slnf file will be launched. If the updated Directory.build.props does accidentally get committed to the MvvmCross repository it won’t affect the Release build configuration. The LaunchVS batch file is clever enough to switch from one platform to another without the developer having to undo changes to the Directory.build.props file.

Hopefully in this post you’ve seen one option for switching TFMs and making it easier for developers to work with a multi-targeted solution. If you use multi-targeting in your application or library, it’s work considering adding filtering and conditional TFMs to make development easier.

ListView and GridView Templates for Windows (UWP)

In my previous post I discussed Control Template in Windows development (UWP and Platform.Uno). I feel the topic of templates warrants at least one follow up post. So, in this post I’m going to walk through ListView Templates and GridView Templates. As both ListView and GridView inherit from ListViewBase, I’m actually going to focus my attention on the ListView. However, the GridView shares the same templates, just with a default horizontal layout.

You might be thinking that surely the ListView only has 1, maybe 2, templates. In this post you’ll see that there are all sorts of templates, that allow you to tailor the ListView. The flexibility offered by the XAML framework, whether you code in XAML or in C#, is truly amazing, as you’ll come to see.

Populating a ListView with Data

Let’s jump in and build out a basic page with some data. For the purpose of this post I’m going to define a static array of data that we’ll be working. I’ve exposed a property, Presenters, that lists a selection of presenters for the upcoming Xamarin Developer Summit. Each presenter has a Name, Title, Company and AvatarUrl.

public sealed partial class MainPage 
{
    public MainPage()
    {
        this.InitializeComponent();
    }
 
    public Presenter[] Presenters { get; } = 
        new Presenter[]
    {
        ("Dan Siegel", "Microsoft MVP & Owner","AvantiPoint", 
        "https://xamarindevelopersummit.com/wp-content/uploads/2019/04/[email protected]" ),
        ("David Ortinau", "Senior Program Manager","Microsoft", 
        "https://xamarindevelopersummit.com/wp-content/uploads/2019/04/david_spk.png" ),
        ("James Montemagno", "Principal Program Manager","Microsoft", 
        "https://xamarindevelopersummit.com/wp-content/uploads/2019/04/james_spk.png" ),
        ("Donovan Brown", "Principal DevOps Manager","Microsoft", 
        "https://xamarindevelopersummit.com/wp-content/uploads/2019/04/donovan_spk.png" ),
        ("Jonathan Peppers", "Senior Software Engineer","Microsoft", 
        "https://xamarindevelopersummit.com/wp-content/uploads/2019/05/[email protected]" ),
        ("Martijn van Dijk", "Microsoft and Xamarin MVP","Baseflow", 
        "https://xamarindevelopersummit.com/wp-content/uploads/2019/04/[email protected]" )
    };
}

public class Presenter
{
    public string AvatarUrl { get; set; }
    public string Name { get; set; }
    public string Title { get; set; }
    public string Company { get; set; }

    public static implicit operator Presenter((string Name, string Title, string Company, string AvatarUrl) info)
    {
        return new Presenter { Name = info.Name, Title=info.Title, Company = info.Company, AvatarUrl = info.AvatarUrl };
    }
}

In the XAML for the MainPage I’m going to add a ListView and set the ItemsSource property to the Presenters property. I’m using the x:Bind syntax to eliminate the need for reflection and improve performance of the app. However, if your data isn’t available when the page loads, you should consider setting the Mode to OneWay if using the x:Bind syntax.

<Grid>
    <ListView ItemsSource="{x:Bind Presenters}" />
</Grid>

At this point if I run the app, what I see is a blank window. However, on closer inspection you can see that there are indeed multiple rows. As the mouse moves over a row it gets a grey background. When I click on a row, that row gets a black border around it, to indicate it’s the selected row.

Clearly we need to adjust the layout of the ListView so that we can see information about the presenters.

Default Layout using ToString

We’ll start with the simplest way to get information to display in the ListView, which is to override the ToString method. Here I’m returning a string made up of the Name, Title and Company of the presenter.

public override string ToString()
{
    return $"{Name} - {Title} - {Company}";
}

Running the app again, without modifying the XAML, I can see that each row corresponds to a presenter in the Presenters array.

Display Single Property using DisplayMemberPath

The DisplayMemberPath property on the ListView can be used to specify a property on the data bound item. In this case we’re setting the path to be the Name property.

<ListView DisplayMemberPath="Name"
        ItemsSource="{x:Bind Presenters}" />

Whilst the DisplayMemberPath might be useful for simple data sets, it does rely on reflection. The string literal set as the DisplayMemberPath needs to be converted into a get operation on the corresponding property.

Item Layout using ItemTemplate and ItemTemplateSelector

What do you do if you want to specify a more complex layout for the items in the array. One of the most common templates to be specified on a ListView is the ItemTemplate. By defining a DataTemplate that can be used by the ListView, you can control exactly how each item in the ListView is presented.

Using a DataTemplate for an ItemTemplate

If you haven’t worked with ListView templates before, it’s worth opening your solution in Visual Studio Blend. In the Objects and Timeline window, right-click on the ListView and select Edit Additional Templates, Edit Generated Items (ItemTemplate) and Create Empty.

Give the new template a name, PresenterItemTemplate, and add it as a resource on the page.

The XAML only contains the ItemTemplate and ItemsSource properties. The DisplayMemberPath shown earlier is no longer required

<ListView ItemTemplate="{StaticResource PresenterItemTemplate}"
            ItemsSource="{x:Bind Presenters}" />

Each presenter is displayed using two columns that you can see defined in the following XAML. In the first column is an Image using the AvatarUrl property. The second column has a StackPanel, allowing the Name and Company to be stacked.

<DataTemplate x:Key="PresenterItemTemplate"
                x:DataType="local:Presenter">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="100" />
            <ColumnDefinition />
        </Grid.ColumnDefinitions>
        <Image Margin="8"
                Source="{x:Bind AvatarUrl}" />
        <StackPanel Grid.Column="1"
                    VerticalAlignment="Center">
            <TextBlock Margin="0,0,0,4"
                        Text="{x:Bind Name}"
                        Style="{StaticResource TitleTextBlockStyle}" />
            <TextBlock Text="{x:Bind Company}"
                        Style="{StaticResource SubtitleTextBlockStyle}" />
        </StackPanel>
    </Grid>
</DataTemplate>

Our items are already looking much better. Note that the hexagonal frame around the presenter’s avatar is part of the image, not the ItemTemplate.

Multiple Templates using a DataTemplateSelector as the ItemTemplateSelector

In some cases it may be necessary that you need to modify the ItemTemplate based on some attribute of the data. By setting the ItemTemplateSelector property on the ListView, you can dynamically switch the ItemTemplate that’s used.

For this example we’re going to use whether the presenter works for Microsoft or not, based on their Company property. I’ve created a derived class, PresenterTemplateSelector, off of the DataTemplateSelector base class. In the SelectTemplateCore method I’m inspecting the Company property and returning one of two DataTemplate based on whether its equal to “Microsoft”.

public class PresenterTemplateSelector:DataTemplateSelector
{
    public DataTemplate RegularPresenter { get; set; }
    public DataTemplate MicrosoftPresenter { get; set; }

    protected override DataTemplate SelectTemplateCore(object item)
    {
        if(item is Presenter presenter)
        {
            return presenter.Company == "Microsoft" ? MicrosoftPresenter : RegularPresenter;
        }

        return base.SelectTemplateCore(item);
    }
}

In the following XAML I’ve defined two DataTemplates. The difference is that the MicrosoftPresenterItemTemplate uses a Microsoft Logo in place of the presenter’s avatar image.

<Page.Resources>
    <DataTemplate x:Key="PresenterItemTemplate"
                    x:DataType="local:Presenter">
        <Grid>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="100" />
                <ColumnDefinition />
            </Grid.ColumnDefinitions>
            <Image Margin="8"
                    Source="{x:Bind AvatarUrl}" />
            <StackPanel Grid.Column="1"
                        VerticalAlignment="Center">
                <TextBlock Margin="0,0,0,4"
                            Text="{x:Bind Name}"
                            Style="{StaticResource TitleTextBlockStyle}" />
                <TextBlock Text="{x:Bind Company}"
                            Style="{StaticResource SubtitleTextBlockStyle}" />
            </StackPanel>
        </Grid>
    </DataTemplate>
    <DataTemplate x:Key="MicrosoftPresenterItemTemplate"
                    x:DataType="local:Presenter">
        <Grid>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="100" />
                <ColumnDefinition />
            </Grid.ColumnDefinitions>
            <Image Margin="8,32"
                    Source="http://img-prod-cms-rt-microsoft-com.akamaized.net/cms/api/am/imageFileData/RE2qVsJ?ver=3f74" />
            <StackPanel Grid.Column="1"
                        VerticalAlignment="Center">
                <TextBlock Margin="0,0,0,4"
                            Text="{x:Bind Name}"
                            Style="{StaticResource TitleTextBlockStyle}" />
                <TextBlock Text="{x:Bind Company}"
                            Style="{StaticResource SubtitleTextBlockStyle}" />
            </StackPanel>
        </Grid>
    </DataTemplate>
    <local:PresenterTemplateSelector x:Key="PresenterTemplateSelector"
                                        RegularPresenter="{StaticResource PresenterItemTemplate}"
                                        MicrosoftPresenter="{StaticResource MicrosoftPresenterItemTemplate}" />
</Page.Resources>


<Grid>
    <ListView ItemTemplateSelector="{StaticResource PresenterTemplateSelector}"
                ItemsSource="{x:Bind Presenters}" />
</Grid>

The two DataTemplates are specified as the RegularPresenter and MicrosoftPresenter properties on the PresenterTemplateSelector instance. Subsequently the PresenterTemplateSelector is set as the ItemTemplateSelector on the ListView. Running the application replaces the presenter’s avatar with a Microsoft logo for when the presenter has Microsoft as their Company.

Modifying Selection, Focus and PointerOver (Hover) Style using an ItemContainerStyle

We’ve seen a number of ways to adjust the layout for each element in the ListView. However, despite the variations in layout, when you move the mouse over an item, or click on an item to select it, the change in appearance is always the same. This is because the border that you see when an item is selected, or the gray background when you move the mouse over an item, are controlled by the ItemContainerStyle for the ListView.

You can think of the ItemContainerStyle as being a wrapper around each element in the ListView. It can be used to add borders, background or margin to every item in the ListView. The ItemContainerStyle also has a number of visual states which can be used to adjust the appearance of the item based on how the user is interacting with it.

Default Styles in Generic.xaml

The ItemContainerStyle is moderately complex, so unless you’ve spent a bit of time working with the ListView, I would recommend using one of the defaults as a starting point. The defaults are all listed in the generic.xaml file which can be located at C:\Program Files (x86)\Windows Kits\10\DesignTime\CommonConfiguration\Neutral\UAP\10.0.18362.0\Generic (or similar) folder on your computer.

As its name suggests, the ItemContainerStyle is indeed a Style. It is used to style each ListViewItem that is displayed within a ListView. In order to find the default ItemContainerStyle we need to look through the generic.xaml for a Style that has a TargetType of ListViewItem but has no Key set (hence making it the implicit style for all ListViewItme). In the following XAML, taken from generic.xaml, we can see that the Style is based on another explicit Style, ListViewItemRevealStyle.

<Style TargetType="ListViewItem" BasedOn="{StaticResource ListViewItemRevealStyle}" />

In Blend we can use the Apply Resource option to explicitly set the ItemContainerStyle to be the ListViewItemRevealStyle.

Custom ItemContainerStyle

Once we’ve set the ItemContainerStyle of our ListView to be the ListViewItemRevealStyle we can then use the Edit a Copy option to take a copy of the ListViewItemRevealStyle that we can make changes to.

The new Style needs a name and can be added to the current page.

Once the new Style has been added to the page, it immediately gets focus in the Objects and Timeline view. With the ItemContainerStyle in focus, you can then make changes in the Properties window. Any changes will be created as a Setter on the Style.

The following XAML is the copy of the ListViewItemRevealStyle that was copied in. There are three main parts to this Style:

  • Setters – for default values to be applied to the ListViewItem. Since each ListViewItem is generated on demand by the ListView, this is actually your opportunity to set properties directly on the ListViewItem.
  • ListViewItemPresenter – this element makes up the entire ControlTemplate for the ListViewItem. It encapsulates all the styling that you see by default around the ListViewItem and includes a large number of properties that can be adjusted to control the appearance of the ListViewItem. A lot of these properties are already set to a theme resource, making them easy to customise.
  • VisualStateGroups – these are visual states that define changes or transitions for the ListViewItem in different states. This Style only defines the CommonStates and EnabledStates VisualStateGroups as these include common states such as Selected and Pressed states which are the most likely states you’ll want to customise. If you do modify the Selected state, you’ll probably want to customise PointerOverSelected and PressedSelected for a consistent appearance when the user is interacting with the selected item.
<Style x:Key="CustomContainerStyle" TargetType="ListViewItem">
    <Setter Property="FontFamily" Value="{ThemeResource ContentControlThemeFontFamily}" />
    <Setter Property="FontSize" Value="{ThemeResource ControlContentThemeFontSize}" />
    <Setter Property="Background" Value="{ThemeResource ListViewItemBackground}" />
    <Setter Property="Foreground" Value="{ThemeResource ListViewItemForeground}" />
    <Setter Property="TabNavigation" Value="Local" />
    <Setter Property="IsHoldingEnabled" Value="True" />
    <Setter Property="Padding" Value="12,0,12,0" />
    <Setter Property="HorizontalContentAlignment" Value="Left" />
    <Setter Property="VerticalContentAlignment" Value="Center" />
    <Setter Property="MinWidth" Value="{ThemeResource ListViewItemMinWidth}" />
    <Setter Property="MinHeight" Value="{ThemeResource ListViewItemMinHeight}" />
    <Setter Property="AllowDrop" Value="False" />
    <Setter Property="UseSystemFocusVisuals" Value="{StaticResource UseSystemFocusVisuals}" />
    <Setter Property="FocusVisualMargin" Value="0" />
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="ListViewItem">
                <ListViewItemPresenter
                x:Name="Root"
                HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
                VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
                CheckBoxBrush="{ThemeResource ListViewItemCheckBoxBrush}"
                CheckBrush="{ThemeResource ListViewItemCheckBrush}"
                CheckMode="{ThemeResource ListViewItemCheckMode}"
                ContentMargin="{TemplateBinding Padding}"
                ContentTransitions="{TemplateBinding ContentTransitions}"
                Control.IsTemplateFocusTarget="True"
                DisabledOpacity="{ThemeResource ListViewItemDisabledThemeOpacity}"
                DragBackground="{ThemeResource ListViewItemDragBackground}"
                DragForeground="{ThemeResource ListViewItemDragForeground}"
                DragOpacity="{ThemeResource ListViewItemDragThemeOpacity}"
                FocusBorderBrush="{ThemeResource ListViewItemFocusBorderBrush}"
                FocusSecondaryBorderBrush="{ThemeResource ListViewItemFocusSecondaryBorderBrush}"
                FocusVisualMargin="{TemplateBinding FocusVisualMargin}"
                PlaceholderBackground="{ThemeResource ListViewItemPlaceholderBackground}"
                PointerOverBackground="{ThemeResource ListViewItemBackgroundPointerOver}"
                PointerOverForeground="{ThemeResource ListViewItemForegroundPointerOver}"
                PressedBackground="{ThemeResource ListViewItemBackgroundPressed}"
                ReorderHintOffset="{ThemeResource ListViewItemReorderHintThemeOffset}"
                RevealBackground="{ThemeResource ListViewItemRevealBackground}"
                RevealBorderBrush="{ThemeResource ListViewItemRevealBorderBrush}"
                RevealBorderThickness="{ThemeResource ListViewItemRevealBorderThemeThickness}"
                SelectedBackground="{ThemeResource ListViewItemBackgroundSelected}"
                SelectedForeground="{ThemeResource ListViewItemForegroundSelected}"
                SelectedPointerOverBackground="{ThemeResource ListViewItemBackgroundSelectedPointerOver}"
                SelectedPressedBackground="{ThemeResource ListViewItemBackgroundSelectedPressed}"
                SelectionCheckMarkVisualEnabled="{ThemeResource ListViewItemSelectionCheckMarkVisualEnabled}">
                    <VisualStateManager.VisualStateGroups>
                        <VisualStateGroup x:Name="CommonStates">
                            <VisualState x:Name="Normal" />
                            <VisualState x:Name="Selected" />
                            <VisualState x:Name="PointerOver">
                                <VisualState.Setters>
                                    <Setter Target="Root.(RevealBrush.State)" Value="PointerOver" />
                                    <Setter Target="Root.RevealBorderBrush" Value="{ThemeResource ListViewItemRevealBorderBrushPointerOver}" />
                                </VisualState.Setters>
                            </VisualState>
                            <VisualState x:Name="PointerOverSelected">
                                <VisualState.Setters>
                                    <Setter Target="Root.(RevealBrush.State)" Value="PointerOver" />
                                    <Setter Target="Root.RevealBorderBrush" Value="{ThemeResource ListViewItemRevealBorderBrushPointerOver}" />
                                </VisualState.Setters>
                            </VisualState>
                            <VisualState x:Name="PointerOverPressed">
                                <VisualState.Setters>
                                    <Setter Target="Root.(RevealBrush.State)" Value="Pressed" />
                                    <Setter Target="Root.RevealBorderBrush" Value="{ThemeResource ListViewItemRevealBorderBrushPressed}" />
                                </VisualState.Setters>
                            </VisualState>
                            <VisualState x:Name="Pressed">
                                <VisualState.Setters>
                                    <Setter Target="Root.(RevealBrush.State)" Value="Pressed" />
                                    <Setter Target="Root.RevealBorderBrush" Value="{ThemeResource ListViewItemRevealBorderBrushPressed}" />
                                </VisualState.Setters>
                            </VisualState>
                            <VisualState x:Name="PressedSelected">
                                <VisualState.Setters>
                                    <Setter Target="Root.(RevealBrush.State)" Value="Pressed" />
                                    <Setter Target="Root.RevealBorderBrush" Value="{ThemeResource ListViewItemRevealBorderBrushPressed}" />
                                </VisualState.Setters>
                            </VisualState>
                        </VisualStateGroup>
                        <VisualStateGroup x:Name="DisabledStates">
                            <VisualState x:Name="Enabled" />
                            <VisualState x:Name="Disabled">
                                <VisualState.Setters>
                                    <Setter Target="Root.RevealBorderThickness" Value="0" />
                                </VisualState.Setters>
                            </VisualState>
                        </VisualStateGroup>
                    </VisualStateManager.VisualStateGroups>
                </ListViewItemPresenter>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

The ListViewItemPresenter is the single element that makes up the ControlTemplate. It is responsible for hosting the ItemTemplate and does infact inherit from ContentPresenter. However, unlike the ContentPresenter which is a basic content host, the ListViewItemPresenter implements standard ListView behaviour such as selection borders and changing the background when you hover over the item etc.

Overriding a ThemeResource to Customise the ListViewItemPresenter

A lot of the behaviour of the ListViewItemPresenter is configurable on the ListViewItemPresenterOr by simply adjusting its properties in the ControlTemplate. Furthermore, a good percentage of these properties are already specified in the ControlTemplate using either a ThemeResource or via a TemplateBinding.

For example take the PointerOverBackground property, which determines the background colour of the ListViewItem when the mouse hovers over it. By default the PointerOverBackground property uses the ListViewItemBackgroundPointerOver theme resource.

<ListViewItemPresenter
    x:Name="Root" ...
    PointerOverBackground="{ThemeResource ListViewItemBackgroundPointerOver}" />

One way to change the PointerOverBackground property is to set it directly on the ListViewItemPresenter.

<ListViewItemPresenter
    x:Name="Root" ...
    PointerOverBackground="Red" />

Alternatively, I can leave the PointerOverBackground set to the ListViewItemBackgroundPointerOver theme resource. I can then override the default value of the ListViewItemBackgroundPointerOver resource. If adjusting properties on the ListViewItemPresenter is the only reason to override the default Style, you can do this by simply defining the matching static resource.

<SolidColorBrush x:Key="ListViewItemBackgroundPointerOver">Red</SolidColorBrush>

ListViewItemPresenter Properties that have a TemplateBinding

There’s a bit of inconsistency with how the properties on the ListViewItemPresenter are specified. As we’ve seen with the PointerOverBackground property, some properties are set using a theme resource. However, there are other properties that are set using a template binding. For example, the HorizontalContentAlignment property is set using a TemplateBinding.

<ListViewItemPresenter
    x:Name="Root" ...
    HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}" />

This means that instead of overriding a theme resource, instead we have to look above the ContentTemplate to the Setters defined in the Style for the ListViewItem. In this case the HorizontalContentAlignment property is set to Left.

<Style x:Key="CustomContainerStyle" TargetType="ListViewItem">
    ...
    <Setter Property="HorizontalContentAlignment" Value="Left" />

If you’re not familiar with the HorizontalContentAlignment property it’s used to define how the contents of a ContentPresenter is aligned and sized. With the default value of Left, the content of the ListViewItem will be positioned adjacent to the left edge. Even if you specify the HorizontalAlignment to be Center or Stretch on the first element of the ItemTemplate, the contents will still be aligned to the left. This is a common issue that developers run into when using the ListView and is easily fixed by setting the HorizontalContentAlignment to Stretch.

Visual States on the ListViewItemPresenter

The ListViewItemPresenter encapsulates a lot of behaviour associated with how the ListViewItem is styled. However, in the ControlTemplate for the ListViewItem you can see that there are some visual states defined within the ListViewItemPresenter. These visual states can be used to further customise the appearance of the ListViewItem based on the state of the item in the list.

In the default Style for the ListViewItem, which we copied into our project earlier, there are two VisualStateGroups defined: CommonStates and DisabledStates. From an initial inspection this would appear to cover most states that you can imagine a ListViewItem would be in, such as Normal, Pressed, PointerOver, Enabled etc. However, there is a set of visual states that haven’t been defined here which is common to a lot of XAML element. The FocusStates group is typically made up of Focused and Unfocused states. As you can imagine, these define the changes to the appearance when an element gets or loses focus.

I’m going to adjust the ListViewItemPresenter to shrink the ListViewItem when it gets focus. I’ll do this by applying a render transform to reduce the ScaleX and ScaleY properties to 0.8. Before I can add the visual states, I first need to make sure that there is a render transform object that I can manipulate. In this case I’m going to use a CompositeTransform as this gives me the most flexibility to apply any render transform, not just a scale transform.

<ListViewItemPresenter
    x:Name="Root" ...
    RenderTransformOrigin="0.5,0.5">
        <ListViewItemPresenter.RenderTransform>
            <CompositeTransform x:Name="CustomTransform"/>
        </ListViewItemPresenter.RenderTransform>

Next I’m going to add a new VisualStateGroup to the ListViewItemPresenter. In this group I’ve defined two visual states. The Unfocused state doesn’t have any properties set, whereas the Focused state sets the ScaleX and ScaleY properties.

<ListViewItemPresenter
    x:Name="Root" ... >
        <VisualStateManager.VisualStateGroups>
            ...
            <VisualStateGroup x:Name="FocusStates">
                <VisualState x:Name="Focused">
                    <VisualState.Setters>
                        <Setter Target="CustomTransform.ScaleX" Value="0.8" />
                        <Setter Target="CustomTransform.ScaleY" Value="0.8" />
                    </VisualState.Setters>
                </VisualState>
                <VisualState x:Name="Unfocused" />
            </VisualStateGroup>
        </VisualStateManager.VisualStateGroups>
    </ListViewItemPresenter>

In order to see the impact of this I’ve set the SelectionMode on the ListView to Extended. This defines how many items can be selected, and how the ListView allows items to be selected. The Extended value means that on Windows you can hold down the Ctrl key whilst using the up and down arrow keys to move the focus between items without selecting them.

The changes we’ve applied to the ListViewItemPresenter (that you can see in the following image) are:

  • We’ve overridden the default value of the ListViewItemBackgroundPointerOver to set it to Red
  • We’ve applied a scale transform when an item is in the Focused state.

In the following image you can see three complete items that are in different states:

  • Selected – the first item in the list has been selected, which is highlighted by the green background
  • PointerOver – when the screenshot was taken the mouse pointer was hovering over the second item, hence the Red background thanks to the ListViewItemBackgroundPointerOver resource.
  • Focused – the keyboard has been used to move focus to the third item by holding the Ctrl key and using the arrow keys. The Focused visual state causes the item to be scaled down to 80%.

Expanded Style for ListViewItem

In the previous sections we’ve been working with a copy of the default Style for the ListViewItem. However, if you take a look in the generic.xaml file, you’ll see that there is another Style for the ListViewItem called ListViewItemExpanded. When building for Windows 8/8.1 this expanded Style used to be the default, before the introduction of the ListViewItemPresenter. The ListViewItemExpanded Style goes on for almost 500 lines in order to explicitly define similar behaviour to the ListViewItemPresenter. Unless you really can’t get the ListViewItemPresenter to do what you need, I would avoid attempting to extend the ListViewItemExpanded Style.

So now that I’ve warned you about the complexity of the ListViewItemExpanded Style, it does bode the question as to why I mentioned it. For a couple of reasons: Firstly, it’s important to know that this Style exists and where to find it, in case you really want to dig in and customise the ListViewItem. The second reason is so that I can point out that in place of the ListViewItemPresenter, the ListViewItemExpanded Style uses a vanilla ContentPresenter to host each item in the ListView.

Minimal Style for ListViewItem

Occasionally you may want to do away with all the visual enhancements that you get out of the box with the ListView. This may be to squeeze that last little bit of performance out of the ListView (often required on Xbox where memory management is abysmal), or it may be that you don’t want all the hover effects, or the animations when items are selected or add/removed from the ListView. As mentioned in the previous section where we discussed the ListViewItemExpanded Style, the key to hosting items in the ListView is the ContentPresenter. Thus, it’s easy for us to create a bare minimum Style that doesn’t do anything other than host the items in the ListView.

<Style x:Key="MinimalContainerStyle" TargetType="ListViewItem">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="ListViewItem">
                <ContentPresenter x:Name="ContentPresenter"
                    Grid.Column="1"
                    HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
                    VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
                    ContentTemplate="{TemplateBinding ContentTemplate}"
                    Content="{TemplateBinding Content}" />
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

As you can see from the following image there’s no additional padding or margin, and there’s no selection, focus or mouse over states.

Note: If you get to the point where you strip out all the layout, animation and other behaviour from the ListViewItem (such as in the MinimalContainerSylte above), you should consider using an ItemsControl instead of the ListView. You can think of the ItemsControl as a raw repeater that allows you to data bind a collection of elements in order to have them presented in a list on the screen. However, it doesn’t support selection and other interactivity features that the ListView has.

ItemsPanel

So far we’ve looked at templates that govern how each item in the ListView appear. The ItemsPanel property on the ListView accepts an ItemsPanelTemplat which defines how items are placed relative to each other. For example the default ItemsPanelTemplate that is specified on the ListView template uses an ItemsStackPanel to efficiently layout items in a vertical stack

<ItemsPanelTemplate>
    <ItemsStackPanel Orientation="Vertical" />
</ItemsPanelTemplate>

What’s interesting is that you can easily switch between laying items out in a vertical list, to having them presented horizontally, by changing the Orientation to Horizontal on the ItemsStackPanel. You will also have to adjust the default Setters on the ListView template to enable horizontal scrolling and disable vertical scrolling.

Most XAML developers will be familiar with a regular StackPanel that’s used to present items in either a vertical or horizontal list. The ItemsStackPanel is similar in that it presents items by stacking them. However, it has been specifically designed for use with a ListView, GridView or even an ItemsControl. It is capable of virtualising the items in the ListView making it particularly efficient for large lists.

It is worth pointing out that virtualisation isn’t always a good thing. If you have a small number of items in the list, you may get a better experience by disabling virtualisation. Using a regular StackPanel instead of an ItemsStackPanel will prevent any virtualisation meaning that scrolling won’t have any ghost cells (cells where the contents haven’t completely rendered). However, be aware that as the number of items increases, so will both the load time and the memory usage.

GridView v ListView

Whilst on the topic of the ItemsPanel property, it’s worth looking at the default value for the GridView. Instead of using an ItemsStackPanel, the GridView uses an ItemsWrapGrid, allowing items to wrap in either a horizontal or vertical direction.

Both the GridView and ListView inherit from the ListViewBase class. If you inspect the default Style for both GridView and ListView you’ll see that the main variations are in the direction of scrolling and the ItemsPanel. As an experiment to see how well you understand the various templates, try converting a GridView into a ListView and vice versa – you can do this just by adjusting the attributes of the various templates.

Header and Footer Templates

We’re getting towards the end of the templates for the ListView but I’d be remiss if I didn’t point out the Header and Footer templates. One basic usage for these is to add space to the beginning or the end of the list of items. However, if you just want to do this, you may want to consider just adding a margin to the ItemsStackPanel in the ItemsPanel.

The header and footers of a ListView are pairs of properties. There are a Header and HeaderTemplate properties, and there are Footer and FooterTemplate properties. These work very similar to Content and ContentTemplate properties on a ContentControl. The Header and Footer represent the data that you want to display. Then of course the HeaderTemplate and FooterTemplate define the template for that data.

<ListView ...
    Header="This is the header">
    <ListView.HeaderTemplate>
        <DataTemplate>
            <TextBlock Text="{Binding}" Style="{StaticResource TitleTextBlockStyle}"/>
        </DataTemplate>
    </ListView.HeaderTemplate>
</ListView>

The following image illustrates the Header being applied via the HeaderTemplate as the beginning of the ListView. In this case I’ve added a Margin of 50 to the top of the ItemsStackPanel, which as you can see appears after the Header but before the list of items.

ListView Template

So the last template I’m going to cover in this post is of course the template for the ListView itself. If you look in the generic.xaml file you’ll find implicit styles for both the ListView and the GridView. The default styles define things like the transitions to be applied to items (i.e. the ItemContainerTransitions), the ItemsPanel (i.e. ItemsStackPanel for ListView, ItemsWrapGrid for GridView) and of course the ControlTemplate for the ListView and GridView themselves.

The ControlTemplate for both ListView and GridView are relatively simple and are made up of a Border, a nested ScrollViewer and a nested ItemsPresenter.

Scroll Events

A scenario that comes up from time to time is that you want to trigger some behaviour based upon the ListView being scrolled. This might be the first time its scrolled, or perhaps it’s when the scrolling gets to a certain point. Either way, you can easily attach an event to the ScrollViewer that’s part of the ControlTemplate for the ListView.

Templates for ListView and GridView

In this post we’ve walked through a variety of different ways you can customise the appearance of the ListView and GridView controls. There are templates for controlling how items appear and their behaviour, and there are templates for styling the ListView and GridView themselves.

One set of templates we didn’t touch on in this post are those that are used when grouping data. Whilst grouped data typically uses the ItemTemplate to render each item in the list, there are separate templates for the group header and footers. We’ll leave the topic of working with grouped data for another post.

XAML Control Templates for Windows (UWP) and Platform.Uno

Recently there has been a lot of discussion about using code to declare the user interface of an app. Such as a recent post I did following the announcement of SwiftUI by Apple. In the Xamarin.Forms world the hashtag CSharpForMarkup has become the latest distraction. CSharpForMarkup encourages developers to move away from XAML to defining their layouts using C#. Whilst I’m not against this, I do feel that we’re all discussing the wrong problem. Whether you use code or XAML to define your layouts, Xamarin.Forms suffers from being tied to the underlying platform controls. Renderers, Effects and Visual are all mechanisms to compensate for not having XAML control templates for every control (more on templates).

Enter Platform.Uno and their mission to take the XAML framework from Windows (UWP) and adapt it to other platforms. As an alternative for building apps for iOS and Android, Platform Uno was moderately interesting. With their push to support WebAssembly, Platform.Uno opens up a world of opportunities for building rich applications for the web. Their recent example of publishing the Windows calculator to the web (https://calculator.platform.uno) is just the the start for this technology.

In this post we’ll walk through how the XAML templating system works and how to go about defining a Control Template to customise the behaviour of controls.

Note: In this post I’ll be using XAML so that I can use Blend to assist with writing XAML. You can choose to use C# if you prefer defining your layouts in code. The point of this post is to highlight the templating capabilities of UWP and Platform.Uno.

Lookless or Templated Controls

One of the foundations of XAML was the notion of a templated, or lookless, control. In his post “What does it mean to be ‘lookless’?” Dave observes that a lookless control is one where the UI is declared in XAML and the behaviour or functionality of the control is defined in code. My view is that a lookless control is one where the functionality of the control isn’t tied to a specific design. By this I mean that each aspect of the control should be defined using a template, thus making it a templated control. A developer using the control should be able to adjust any of the templates without affecting the functionality of the control.

What is a Button?

Let’s look at what this means in practice by examining a Button. If I asked you what you thought a Button in UWP is, you’d probably answer by writing the following element:

<Button Content="Press Me!" />

All you’ve done is declared a Button with content that says “Press Me!” that uses the default Button styles. You haven’t actually answered the question. So let’s start with a simple definition of a button.

A Button is an element that responds to the user tapping or clicking by performing some action.

However, in the context of UWP where a Button is an actual control, this definition is a little too vague. For example, the following Border/TextBlock combination matches the definition

<Border Background="Gray" Tapped="PressMeTapped">
    <TextBlock Text="Press Me!"/>
</Border>

Tapping on the Border does invoke the PressMeTapped event handler but is that all we mean when we say a Button. I would argue that one of the significant aspects to a Button is that it gives some visual cues to the user. By this I mean that when the user hovers their mouse over a button they can the mouse pointer changes, or the Button adjusts how it looks. Similarly, when the user taps or clicks on the Button they should get some visual feedback confirming their action. This feedback is what separates a Button from just simply a tap or click event handler that can be attached to any element.

What we’ve just observed is that the Button has a number of different visual states. In the context of a UWP button these are the Normal, PointerOver and Pressed states. Using the default Button style in the Light theme, these states are shown in the following image.

States in the Control Template for the UWP Button
UWP Button States

As part of extending our definition of a Button I was careful not to define what feedback, or change to layout, should be used for each state of the Button. UWP ships with Dark and Light themes and a set of default control styles that combine to give you the states shown above. However, the important thing to remember is that the visual appearance isn’t what defines a Button. Perhaps an updated definition might be something like:

A Button is an element that responds to the user tapping or clicking by performing some action. It has Normal, PointerOver, Pressed and Disabled visual states that can provide feedback and guide the user on how to interact with the control.

Content v Text Properties

Xamarin.Forms deviates from most other XAML platforms when it comes to defining elements and attributes. For example, instead of using a StackPanel, Xamarin.Forms has a StackLayout. Not only are they named differently, they also exhibit different behaviour. Similarly, the Button control has a Text property instead of a Content property. Whilst this might seem like a decision to simplify the Button control it highlights the limitation of Xamarin.Forms due to the lack of templating.

Let’s back up for a second and take a look at the Content property. In a lot of cases you might simply set the Content attribute to a string, such as the earlier example where I set the Content to “Press Me!”. However, to override the styling of the text, or add other content to the Button, I can set the Content property explicitly.

<Button>
    <StackPanel>
        <TextBlock Style="{StaticResource HeaderTextBlockStyle}"
                   Text="Press Me!" />
        <TextBlock Style="{StaticResource BodyTextBlockStyle}"
                   Text="or.... perhaps not" />
    </StackPanel>
</Button>

Note: As the Content property on the Button is annotated with the ContentPropertyAttribute, we don’t need to wrap the StackPanel in Button.Content tags.

Even though I’ve changed the content of the Button, I haven’t in anyway changed the way the Button functions. In fact, in this case I haven’t even changed how the Button changes appearance for the different states.

Using a Content Template

Before we get into customising the different Button states I just wanted to touch on the use of content templates. In the previous example I showed how the Content of the Button could be set to any arbitrary XAML. What happens if I want to reuse the content layout but just with different text? This is where the ContentTemplate property is useful.

The ContentTemplate property expects a DataTemplate which will be used to define the layout for the Content. At runtime the DataContext of the ContentTemplate will be set to the value of the Content property. Let’s see this in practice by moving the XAML from the Content property into the ContentTemplate.

<Button>
  <Button.Content>
    <local:ButtonData Heading="Press Me!" SubText="or.... perhaps not" />
  </Button.Content>
  <Button.ContentTemplate>
    <DataTemplate>
      <StackPanel>
        <TextBlock Style="{StaticResource HeaderTextBlockStyle}"
                   Text="{Binding Heading}" />
        <TextBlock Style="{StaticResource BodyTextBlockStyle}"
                   Text="{Binding SubText}" />
      </StackPanel>
    </DataTemplate>
  </Button.ContentTemplate>
</Button>

In this code block you can see that the Content property has been set to a new instance of the ButtonData class. The two TextBlock elements are subsequently data bound to the Heading and SubText properties.

public class ButtonData
{
    public string Heading { get; set; }

    public string SubText { get; set; }
}

What this shows is that we’ve separated the data (i.e. the instance of the ButtonData class) from the presentation (i.e. the DataTemplate that is specified for the ContentTemplate property). Now if we wanted to reuse the DataTemplate across multiple elements we can extract it as a StaticResource. The following code illustrates extracting the DataTemplate to a StaticResource, as well as updating the data binding syntax. The x:Bind syntax gives us strongly typed data binding. Meaning we get intellisense and better runtime performance as there are no reflection calls. Button1 and Button2 are properties on the page that return instances of the ButtonData class.

<Page.Resources>
    <DataTemplate x:Key="MyButtonTemplate"
                  x:DataType="local:ButtonData">
        <StackPanel>
            <TextBlock Style="{StaticResource HeaderTextBlockStyle}"
                       Text="{x:Bind Heading}" />
            <TextBlock Style="{StaticResource BodyTextBlockStyle}"
                       Text="{x:Bind SubText}" />
        </StackPanel>
    </DataTemplate>
</Page.Resources>
<Button Content="{x:Bind Button1}"
        ContentTemplate="{StaticResource MyButtonTemplate}" />
<Button Content="{x:Bind Button2}"
        ContentTemplate="{StaticResource MyButtonTemplate}" />

Custom Control Templates

So far we’ve seen how the Button control supports arbitrary Content and the use of ContentTemplates to reuse layouts across multiple controls. Neither of these can be used to adjust the visual appearance of the different Button states. In this section we’re going to start by looking at what properties you can adjust to tweak the existing states. We’ll then bust out Blend and start messing with the way a Button looks. All of this will be done without changing the functionality of the Button.

ThemeResources in the Control Template

In the previous section I added some arbitrary XAML to the Content of the Button. However, when I run the application I still see the default background gray. The background of the Button control is part of the default Template for the control. We’ll delve into this in a little more detail in the next sections. For the time being we’re going to adjust the colour of the background and round the corners on the border.

Adjusting the background of the Button when it’s in the Normal state can be done two ways. You can either specify the Background on the Button element itself, or you can override the ButtonBackground resource with the colour of your choosing.

<Page.Resources>
    <Color x:Key="ButtonBackground">DarkGreen</Color>
</Page.Resources>

<Button Content="{x:Bind Button1}"
        Background="Blue"
        ContentTemplate="{StaticResource MyButtonTemplate}" />
<Button Width="200"
        Height="200"
        HorizontalAlignment="Center"
        Content="{x:Bind Button2}"
        ContentTemplate="{StaticResource MyButtonTemplate}" />

At this point if you run the application up on UWP you’ll see that the initial colours for the two Button elements are blue and dark green respectivey. However, if you move the mouse over each of them you’ll see that they switch back to a grey background. This is because we’ve only adjusted the colour used to set the initial background colour. To adjust the colour for the PointerOver and Pressed states we need to adjust the appropriate resources, which are aptly called ButtonBackgroundPressed and ButtonBackgroundPressed. A full list of the overridable colours is available in the documentation for the Button control. Alternatively you can find the entire list of built in color resources in the ThemeResources.xaml file (more information here).

Button Colors in ThemeResources.xaml

In addition to changing the background colour, we also wanted to round the corners of the border that appears around the control when the user moves the mouse in closer. This can be done by simply setting the CornerRadius property to 10 on the Button element.

When we get to talking about visual states we’ll go through how properties on the Button control are modified with each visual state.

Changing the Control Template Layout

Sometimes it’s necessary to make changes that can’t be achieved by either setting the ContentTemplate or overriding the default colour resources. For example, say you wanted to add the Built to Roam logo as a watermark behind the content of the Button. To do this we need to override the Template for the Button. For this I’m going to use Blend as it’s particularly good at edit templates.

To begin editing the control template, locate the Button in the Objects and Timelines window. Right-click on the [Button] and select Edit Template, followed by Edit a Copy.

Editing a Copy of the Default Button Template

Next, you’ll be prompted to give the new template a name and specify where you want the template to be defined. When the new template is created it will include the default Button Template, thus allowing you to customise it. In this case the WatermarkButtonTemplate will be defined in the current document, limiting its availability to just the Button elements on the page.

Naming the New Control Template

Note: In this example we’re copying the default Button template. However, what gets copied into the XAML is actually the entire default Button style. This includes all the default property setters. The value of the Template property is set to a new instance of a ControlTemplate.

I’m not going to go through the changes to the template in detail but the summary is:

  • Wrap the ContentPresenter in a Grid called ContentFrame
  • Move Background, BackgroundSizing, BorderBrush, BorderThickness and CornerRadius from the ContentPresenter to ContentFrame
  • Adjust Visual States so that they reference ContentFrame for border and background properties. Leave any references to Foreground targetting the ContentPresenter element.
  • Add an Image inside the Grid with Source set to an image added to the Assets folder.
  • Set Opacity to 20% (0.2) and Stretch to Uniform on the Image.

Before:

<ControlTemplate TargetType="Button">
    <ContentPresenter
        x:Name="ContentPresenter"
        Padding="{TemplateBinding Padding}"
        HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
        VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
        AutomationProperties.AccessibilityView="Raw"
        Background="{TemplateBinding Background}"
        BackgroundSizing="{TemplateBinding BackgroundSizing}"
        BorderBrush="{TemplateBinding BorderBrush}"
        BorderThickness="{TemplateBinding BorderThickness}"
        Content="{TemplateBinding Content}"
        ContentTemplate="{TemplateBinding ContentTemplate}"
        ContentTransitions="{TemplateBinding ContentTransitions}"
        CornerRadius="{TemplateBinding CornerRadius}">
        <VisualStateManager.VisualStateGroups>                
        ...
        </VisualStateManager.VisualStateGroups>
    </ContentPresenter>
</ControlTemplate>

After:

<ControlTemplate TargetType="Button">
    <Grid
        x:Name="ContentFrame"
        Padding="{TemplateBinding Padding}"
        Background="{TemplateBinding Background}"
        BackgroundSizing="{TemplateBinding BackgroundSizing}"
        BorderBrush="{TemplateBinding BorderBrush}"
        BorderThickness="{TemplateBinding BorderThickness}"
        CornerRadius="{TemplateBinding CornerRadius}">
        <Image
            Opacity="0.2"
            Source="Assets/BuiltToRoamLogo.png"
            Stretch="Uniform" />
        <ContentPresenter
            x:Name="ContentPresenter"
            HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
            VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
            AutomationProperties.AccessibilityView="Raw"
            Content="{TemplateBinding Content}"
            ContentTemplate="{TemplateBinding ContentTemplate}"
            ContentTransitions="{TemplateBinding ContentTransitions}"
            FontFamily="Segoe UI" />
        <VisualStateManager.VisualStateGroups>
        ...
        </VisualStateManager.VisualStateGroups>
    </Grid>
</ControlTemplate>

The net result is that the image is displayed on top of the background colour (which is set on the parent Grid) but beneath the content.

The important thing about defining this ControlTemplate is that it can be reused on any Button, irrespective of the Content that is being displayed. Again, these changes have affected the design of the Button but we still haven’t modified what happens when the user interacts with the Button.

Tweaking Control Template Visual States

The last set of changes we’re going to look at is how we modify the behaviour of the Button when the user does interact with it. In this case we’re going to look at the Pressed state. We’re going to change the default behaviour to slightly enlarge the Button when it’s in the Pressed state. To do this we need to modify the Pressed VisualState that is defined inside the ControlTemplate.

Using Blend we’ll step through adjusting the Pressed Visual State to expand the Button to 150% of it’s original size (i.e. a scale of 1.5 in both X and Y directions). To get started in the Objects and Timeline window, right click the Button element and select Edit Template, then Edit Current. This is similar to what we did previously but this time we already have a ControlTemplate in the XAML for the page.

Edit an Existing Control Template

Next, from the States window, select the Pressed state. If you click where the word Pressed is written you should see both the focus border around the Pressed state and a red dot appear alongside, indicating that Blend is in recording mode. You should also see a red border appear around the main design surface. If you look at the Button on the design surface it should appear like it’s been Pressed to illustrate what the Pressed state looks like.

Switching to the Pressed state

In the Properties window, locate the Transform section. Select the third icon in, which is for adjusting the Scale of the element. Set both X and Y values to 1.5. You should see a white square alongside each of the values indicating that you’ve explicitly set the value.

Adjusting the Scale Transform for X and Y

If you go back to the Objects and Timeline window you should see that under the ContentFrame there are entries for RenderTransform/ScaleX and RenderTransform/ScaleY to indicate that these properties have been modified for the currently selected visual state.

Visual State Properties

If you run the application now and click on the Button you should see that whilst the mouse button is depressed the Button moves to the Pressed state which shows the Button enlarged.

Enlarged Pressed State for Button

If you examine the change to the XAML, what you’ll see is that two Setters were added to the Pressed Visual State.

<VisualState x:Name="Pressed">
    <VisualState.Setters>
        <Setter Target="ContentFrame.(UIElement.RenderTransform).(CompositeTransform.TranslateX)" Value="1.5" />
        <Setter Target="ContentFrame.(UIElement.RenderTransform).(CompositeTransform.TranslateY)" Value="1.5" />
    </VisualState.Setters>
    <Storyboard>
        ... other existing animations ...
    </Storyboard>
</VisualState>

Animating Content Template State Transitions

The change we just made to the Control Template was to change the Scale X and Scale Y properties for the Pressed state. However, this is rather a jarring experience as there’s no transition or animation defined. The final step in this process is to add a transition both too and from the Pressed state.

In the States window, click the arrow with a plus sign and select the first item (i.e. *=>Pressed) to add a transition that will be invoked whenever the Button goes to the Pressed state, regardless of the initial state.

Adding Transition into Pressed State

If you look in the Objects and Timeline window you can now see a timeline that will define what animations are run during the transition.

Start of the Transition Timeline

Over in the Properties window, locate the Transform section and click on the empty square alongside the X property. Select Convert to Local Value, which will set the value to 1. The square will now appear as a sold white square. Repeat for Scale Y

Setting the Start of the Transition

Back in the Objects and Timeline window you should now see a keyframe marker appear at time 0. Drag the vertical orange line to 0.5 seconds.

End of Transition in Timeline

Now in the Properties window set the Scale X and Scale Y to 1.5, which is the end values for the transition. Thisshould match the values previously set for the Pressed state.

End Transition Values

Repeat this process for a transition for leaving the Pressed state. The transition should be the reverse with the Scale X and Y starting at 1.5 and ending at 1.

Transitions In and Out of Pressed State

And there you have it, an animated Pressed state.

Animated Pressed State in Control Template

The final XAML for the Transitions includes two Storyboards that defines the transition to and from the Pressed state.

<VisualStateGroup.Transitions>
    <VisualTransition GeneratedDuration="00:00:00"
                      To="Pressed">
        <Storyboard>
            <DoubleAnimationUsingKeyFrames Storyboard.TargetName="ContentFrame"
                                           Storyboard.TargetProperty="(UIElement.RenderTransform).(CompositeTransform.ScaleX)">
                <EasingDoubleKeyFrame KeyTime="00:00:00"
                                      Value="1" />
                <EasingDoubleKeyFrame KeyTime="00:00:00.5000000"
                                      Value="1.5" />
            </DoubleAnimationUsingKeyFrames>
            <DoubleAnimationUsingKeyFrames Storyboard.TargetName="ContentFrame"
                                           Storyboard.TargetProperty="(UIElement.RenderTransform).(CompositeTransform.ScaleY)">
                <EasingDoubleKeyFrame KeyTime="00:00:00"
                                      Value="1" />
                <EasingDoubleKeyFrame KeyTime="00:00:00.5000000"
                                      Value="1.5" />
            </DoubleAnimationUsingKeyFrames>
        </Storyboard>
    </VisualTransition>
    <VisualTransition GeneratedDuration="00:00:00"
                      From="Pressed">
        <Storyboard>
            <DoubleAnimationUsingKeyFrames Storyboard.TargetName="ContentFrame"
                                           Storyboard.TargetProperty="(UIElement.RenderTransform).(CompositeTransform.ScaleX)">
                <EasingDoubleKeyFrame KeyTime="00:00:00"
                                      Value="1.5" />
                <EasingDoubleKeyFrame KeyTime="00:00:00.5000000"
                                      Value="1" />
            </DoubleAnimationUsingKeyFrames>
            <DoubleAnimationUsingKeyFrames Storyboard.TargetName="ContentFrame"
                                           Storyboard.TargetProperty="(UIElement.RenderTransform).(CompositeTransform.ScaleY)">
                <EasingDoubleKeyFrame KeyTime="00:00:00"
                                      Value="1.5" />
                <EasingDoubleKeyFrame KeyTime="00:00:00.5000000"
                                      Value="1" />
            </DoubleAnimationUsingKeyFrames>
        </Storyboard>
    </VisualTransition>
</VisualStateGroup.Transitions>

As you can see from this walkthrough, the XAML templating system is relatively sophisticated. It allows for multiple levels of configuration or tweaking in order to get it looking just right.

In coming posts I’ll be talking more about what differentiates XAML and why it’s still being used across Windows, Xamarin.Forms and of course Platform Uno. If you’re looking for cross platform solutions, please don’t hesitate to contact me or anyone on the team at Built to Roam.

Xamarin Developer Summit Schedule Breakdown

In just under a month some of the biggest names in the Xamarin community will be presenting alongside a prominent members of the Microsoft Xamarin and Xamarin.Forms teams at the Xamarin Developer Summit.

Xamarin Developer Summit – 11/12 July – Houston, Texas

Whilst I’m not going to be able to make it across to the summit I wanted to do a bit call out to all the great sessions that are in the schedule. The summit is a result of the massive effort that Dan Siegel has put in and the support from all the fantastic presenters.

If you scan the schedule you’ll no doubt be familiar with some of the names but with so many great sessions, how do you know where to start. I thought I’d take the opportunity to break the sessions into some groups to help you decide what’s of interest. These are of course just my groups based on what I can gather from the session description.

Getting Started / Overview

Workshops

Optimisation / Quality

Testing

Mvvm / Architecture

  • Xamarin.Forms made better with Prism – Hussain Abbasi
  • Streamline & Simplify Events with Reactive Extensions – Shane Neuville
  • Rapid Enterprise Architecture Delivery – Paul Schroeder
  • Reactive UI and Reactive Extensions for Xamarin.Forms – Michael Stonis
  • Mobile architecture with MvvmCross, are you doing it right? – Martijn van Dijk
  • Using Xamarin.Forms Shell to easily create a consistent, dynamic, customized, and feature filled UI – Shane Neuville

Security

  • Authentication and Authorization for Xamarin apps using ADB2C and MSAL – David Allen

Device

AI / ML

  • Cognitive Services in Xamarin Applications – Veronika Kolesnikova
  • Build Smarter Cross-Platform Applications Using Xamarin, Azure Cognitive Services, and ML.NET – Richard Taylor
  • How to build your modern ai app with Xamarin – Lo Kinfey

Azure

  • Crafting Real-Time Mobile Apps with SignalR – James Montemagno
  • Light up Xamarin Apps with ChatBots – Sam Basu
  • Build a mobile chatbot with Xamarin & Bot Framework – Luis Beltran
  • Build a Media Streaming App With Azure and Xamarin – Matt Soucoup
  • Create Mixed Reality Experiences with Azure Spatial Anchors and Xamarin – Sweekriti Satpathy

Data

Design

  • Let’s Make Crazy Beautiful UI With Xamarin.Forms – David Ortinau
  • Creating Consistent UI with Xamarin.Forms Visual – David Ortinau

General

DevOps

For more information on any of these topics then you should Register and bring all your learnings back to your company, community through user groups and other forums.

Decompilers for .NET and Windows (UWP) Apps

I think I’ve been living under a rock as I’ve only just come across dnSpy, a decompiler for .net!

I’ve been building apps and services with .NET for a long time, so a Twitter thread talking about decompilers amused me. David Kean’s comment pretty accurately reflects my sentiment regarding Reflector.

Reflector was such a simple tool and it just worked. That was until Red Gate took over and I can’t even remember what happened to it. Does it still exist?

The thread started with Jared commenting that developers should be using ilSpy instead of hacking with ildasm. I’ve rocked ilSpy in my toolkit for a while now and it’s always served me well. In fact I go so far as to set it as the default file handler for .dll files. After all, what other program are you going to want to launch when you double-click on a .dll file.

dnSpy: A Decompiler for .NET

What really knocked my socks off was that someone mentioned dnSpy which I’d never heard of. Thinking it was something similar to ilspy I didn’t think much of it but figured I would download it and take a look.

Next thing I knew it was like I had opened an entire development environment. The layout was familiar, all the way down to the Debug menu that allows me to attach to process.

What? come again? Attach to Process? Yes, that’s right you can attach to a running process, set breakpoints and intercept exceptions (and I’m sure a whole bunch more things. Ironically whilst I was attempting to modify an image of dnspy running, Paint.NET crashed on me. Whilst Paint.NET was hung, I was able to attach to the process using dnspy and see that it was stuck waiting for a print dialog to return.

dnSpy: a Decompiler for .NET

If you’re using ilspy, you should check out dnspy

For anyone still using ildasm, you should check out ilspy and dnspy

Developers not using a decompiler, what have you been doing? Get yourself a decompiler for .NET, and use either ilspy or dnspy.


Nick Randolph @thenickrandolph
If you need help debugging your application, contact Built to Roam


Fiddler Debugging Http/Https Traffic for Xamarin iOS, Android and Windows (UWP) Applications

Debugging Http/Https Traffic using Fiddler for Xamarin iOS, Android and Windows (UWP) Applications

One of the most frustrating things as a frontend developer is when you are receiving incorrect data. You don’t know whether the problem lies with your application, or the backend services. The easiest way to validate this is to pretend to be a hacker. You can stage a man-in-the-middle attack on your own application. Debugging using tools like Fiddler or Charles can be used to inspect the traffic from your application. Unfortunately, the same effort that goes into protecting apps from such attacks, also means that it is harder for developers to setup Fiddler debugging.

In this post I’ll walk through setting up Fiddler debugging for a Xamarin.Forms application. The same basic approach will work for a native or Xamarin iOS/Android application as well. For this post I’ve created an application using the Blank Xamarin.Forms template that comes with Visual Studio 2019. I’ve selected to target all three platforms.

In the OnAppearing method in the MainPage of the Xamarin.Forms application, I’ve added some basic code to retrieve a string for a Https endpoint. We’ll use a Https endpoint on the assumption that if we can intercept Https then we can also intercept Http traffic.

protected override async void OnAppearing()
{
     base.OnAppearing();
     var client = new HttpClient();
     var users = await client.GetStringAsync("https://reqres.in/api/users?page=0");
     Debug.WriteLine(users);
}

Setting Up Fiddler Debugging

Before we get started with the individual platforms, it’s worth checking your configuration for Fiddler:

  • You need to make sure you have setup Https traffic decryption. I’m not going to repeat the documentation, so check out how to Configure Fiddler to Decrypt HTTPS Traffic.
  • You’ll also need to setup Fiddler debugging for remote traffic. This can be done by opening the Options window. Under the Connections tab, make sure the “Allow remote computers to connect” checkbox is checked. If you’re running Skype, or some other communication tools, it may have jumped onto the default port configured in Fiddler. In this case you can adjust the “Fiddler listens on port”.
Fiddler Debugging Options Window
Enabling remote connection in Fiddler

Windows (UWP)

I’ll start with Windows, because it’s relatively straight forward to get setup. With Fiddler running, you can run the UWP project from within Visual Studio and Fiddler debugging will work. Fiddler will capture the Https traffic without any further configuration or setup.

Fiddler Debugging
Capturing network traffic in Fiddler

In some cases, you need to make sure your application is added to the exemption list, so that traffic can be routed to the local machine. For more information see the blog post Revisiting Fiddler and Win8+ Immersive applications. To check this, click the WinConfig button in the top left corner of the Fiddler interface.

WinConfig
Click WinConfig to adjust the exemption list for Windows 10 apps

Locate your application and confirm that there is a check in the box next to your application. If it’s not checked, check the box, and then click Save Changes

Exemption Utility
Locate your UWP application and check the box to add your application to exemption list

One other issue I’ve seen but can’t reproduce reliably, is that sometimes when you run your application from Visual Studio it unchecks the box in the WinConfig in Fiddler. If for some reason you no longer see traffic in Fiddler for your application, go back and double check the exemption list in Fiddler.

Diagnosing Issues with Fiddler on Windows

When configuring Https traffic decryption, you’ll be prompted to install and trust the Fiddler root certificate. If you don’t accept all the prompts, Fiddler debugging for Windows applications will not work. Windows traffic debugging works without any further configuration because the Fiddler root certificate is trusted on the machine running Fiddler.

If you’re attempting to run the Windows application on a different machine than the one running Fiddler, you’ll need to install and trust the Fiddler root certificate. This is in addition to setting the remote machine as a proxy, which is a topic for another post. Navigate to http://[ipaddress]:[port] where [ipaddress] is the ip address of the machine running Fiddler and the [port] is the port number that Fiddler is listening on. Do NOT use https as this site is http only. You should see a screen similar to the following image – if you don’t, make sure you check that Fiddler is running!!

Fiddler Debugging Echo Service
Click on the FiddlerRoot certificate to install the root certificate

Click on the FiddlerRoot Certificate and install as a Trusted Certificate Authority on the Local Machine. This should allow your Windows application to trust Fiddler.

iOS Fiddler Debugging

Next up is iOS and in this case we’re going to use the iOS simulator. The same process should work with any iOS device that’s on the same local network as the machine running Fiddler. There are two steps to setup iOS for traffic debugging:

  1. Trust the Fiddler root certificate, and
  2. Set the http proxy to use the machine running Fiddler.

To setup the simulator, first launch the iOS application from Visual Studio. If you’re on Windows, this will launch the remote viewer for the simulator. Once the simulator is running, stop debugging and we’ll setup the simulator for traffic debugging.

Trusting the Fiddler Certificate

Navigate to http:[ipaddress]:[port] (eg http://192.168.1.109:8888) to load the Fiddler echo page. Then click on the Fiddler Certificate link. Follow the prompts to download and install the certificate.

Fiddler Echo Service iPhone Download Certificate iPhone Download Profile iPhone

In addition to downloading the certificate you also need to install it. Go to Settings / General / Profile and click through on the FiddlerRoot profile in order to Install it.

image image image image image image image

The Fiddler root certificate needs to be trusted as a root certificate. Go to Settings / About / Certificate Trust Settings and toggle the switch next to the FiddlerRoot certificate. Fiddler generates a certificate for each site you go to that is derived from the root certificate, so the root certificate needs to be installed as a trusted certificate.

image image image image

The only difference between a real iOS device and the simulator is that on a real iOS device you can set a network proxy. There are online tutorials, such as this one, for instructions on setting a proxy. Unfortunately, setting up a proxy this isn’t configurable on the simulator. If you want to use a proxy in the simulator, you can set the proxy on the mac running the simulator but this would affect all traffic on the mac. Alternatively, when running on the simulator, we can adjust the HttpClient to use a WebProxy using the following code:

var handler = new HttpClientHandler();
handler.Proxy = new WebProxy("192.168.1.109", 8888);
var client = new HttpClient(handler);

Running the iOS application should show network traffic in Fiddler debugging window. You should still see the returned data printed out in the Output window (ie from the Debug.WriteLine statement).

Android Fiddler Debugging

For Android I’m going to use the Android Simulator. Real devices should behave similarly, assuming they’re connected to the same local network as the machine running Fiddler.

Unlike iOS, that will use any proxy configured for the device, for Android you need to explicitly opt in to use a proxy in your code. You’ll need to use code similar to the following on both emulator and real devices:

var handler = new HttpClientHandler();
handler.Proxy = new WebProxy("192.168.1.109", 8888);
var client = new HttpClient(handler);

What’s a little scary about this code is that it “just works”. You might be thinking that this is a good thing. However, it does raise the question of how much of the system security model does the Microsoft built HttpClientHandler respect. What I would have expected is an SSL fail exception because the Fiddler root certificate isn’t trusted by the emulator. Furthermore, the application is not configured to use any user certificates.

The other thing to point out here is that you should not be using the HttpClientHandler. I’ve discussed this in my previous post on working with the HttpClient. Let’s change our code by moving it into the OnCreate method of the Android head project. We’ll also change over to using the AndroidClientHandler.

protected override async void OnCreate(Bundle savedInstanceState)
{
     ...
     var handler = new AndroidClientHandler();
     handler.Proxy = new WebProxy("192.168.1.109", 8888);
     var client = new HttpClient(handler);
     var users = await client.GetStringAsync("https://reqres.in/api/users?page=0");
     System.Diagnostics.Debug.WriteLine(users);
}

When we run the application we see the very familiar SSL handshake exception being raised, which is what we should expect. To get things to work, we now need to install the Fiddler certificate and configure the application to use user certificates.

Install the Fiddler Root Certificate

To install the Fiddler root certificate, navigate to http:[ipaddress]:[port] (eg http://192.168.1.109:8888) to load the Fiddler echo page. Click on the Fiddler Certificate link in order to download the certificate. Follow the prompts to download and install the certificate

image image image image image  image

After installing the certificate go to Setting / Security and Location / Encryption & credentials / User credentials to inspect the certificate

image  image

With the certificate installed into the user store, you need to configure the Android project to allow the use of certificates from the user store. In my post Working with Self Signed Certificates (Certificate Pinning) in Android Applications with Xamarin.Forms, I covered this in detail. The quick summary is that you need to create a network_security_config.xml file which sets the trust-anchors property (set using the debug-overrides element) to include certificates from the user store. You then need to reference this xml file from the networkSecurityConfig attribute on the application element in the AndroidManifest.xml file.

After installing the certificate and adding the network security configuration to the Android application you should now see network requests from the application appear within Fiddler debugging window.

Side Note: Links on how to setup your iOS, Android or Windows device for remote debugging

Side Note: Links on how to setup your iOS, Android or Windows device for remote debugging

Whether it be for convenience, or out of necessity, being about to set up remote debugging to your device makes for a better debugging experience. Particularly if you’re working on a Windows machine, it’s really useful to not have to sit within a meter of the machine running the build agent. 

iOS on WiFi

On iOS, you need to connect your device to Xcode this first time. Thereafter you can debug across a WiFi network. Note that there are some limitations, specifically older devices won’t support network debugging.

Configure Remote Debugging for iOS

Android ADB Configuration

For Android, you need to configure ADB to connect to your device. Note that this configuration only lasts whilst the IP address of the device is retained. If a new IP address is leased, you’ll need to reconfigure ADB to allow for remote debugging.

Windows Remote Machine Setup for Remote Debugging

With a Windows application, use the debugging tools to connect to your device and then debug using the remote machine option in Visual Studio.

Remote Machine settings

The ability to do remote debugging is particularly useful when developing an Xbox application. With the Xbox running in Dev Mode it’s possible to use the Find option to locate the address of the Xbox. Debugging an Xbox application is then the same as debugging any other Windows 10 application.

If you need to diagnose the network traffic from your application, check out the post on using Fiddler for debugging


Nick Randolph @thenickrandolph

If you need help debugging your application, contact Built to Roam

 


Why are Developers Not Developing Applications for Windows?

Why are Developers Not Developing Applications for Windows?

The actual answer to this question is that basic premise of the question is wrong – developers are and will continue to develop for Windows. The question is a bit like saying “Why are all web developers using React?” and is driven by the typical over-hyped communities that latch onto the latest shiny thing that some developer has hacked together in their spare time (or equally as bad the latest language/framework/library released by Microsoft, Google, Facebook etc). Now, I’m not going to write here that Microsoft has made it easy for Windows developers, nor that the latest incarnation of their developer platform, Universal Windows Platform, has been in any form successful. What I do want to point out is that there are a massive number of applications still written, maintained and even created for Windows every day, and that building applications for Windows allows you to target an astonishingly large market (think of the total number of Windows based devices that are targetable).

So if there are so many developers still writing applications for Windows, why does the market think that Windows isn’t worth targeting? and why are there so few developers wanting to invest into learning the latest features of UWP?

I was doing a bit of trawling today looking for some content on why developers should build for Windows and came across this post entitled “Choose Your Platform” which kind of confuses me – I typically use “platform” to refer to the operating system an application is to run on but interestingly Wikipedia defines a computing platform as something more generic that can be applied at different levels. Interestingly enough, from the url of the page you can see it was originally called “Choose your technology” which I guess is even more vague.

What’s interesting about the “Choose Your Platform” page is that it dumps UWP, WPF and WinForms into one bucket and Win32 API into another…. I feel this is kind of weird. It also completely negates all the higher level frameworks/technologies (Electron, PWAs, Xamarin Forms, React Native etc) that could be used to build applications for Windows (and other platforms).

I clicked around a bit and went back up to the parent page “Develop Windows desktop applications” – here, as you can see from the image, the situation gets even more laughable…. In all seriousness, did Microsoft find some programmer out of the late 90’s to write this: “Create Windows desktop applications that your customers can use at work and play using Win32 and COM APIs to leverage features of the operating system”.

image

Over the last couple of years it’s been evident that Microsoft views Windows, and the associated app platform, as playing second (or perhaps third) fiddle to everything they’re selling that starts with Azure. Furthermore, what cycles have been invested in the Windows developer platform have been retrained on uplifting all those WinForms and WPF applications so that they can be distributed via the Store, take advantage of all the security/identify context that comes with Windows 10, and of course all the new APIs that are being added to UWP.

What’s missing is any form of marketing that explains in clear terms why companies, and developers, should care about Windows development? It’s not enough to say “you can build an app that uses ink” or “you can build an app that connects to Azure” – seriously I can build an iOS/Android app that does both of those just as quickly as I can for Windows. The big value proposition of UWP was it’s ability to write once and run anywhere – this all but died when Windows Phone was buried (RIP). Xbox requires so much hackery and patching the user interface and interaction model that it’s almost quicker to build a separate app, and Surface Hub (v1) doesn’t support .NET Standard 2.0. Seriously, and we wonder why Windows is such an after thought.

So, let’s adjust my opening question to “Why are companies ignoring Windows when building applications?” – and the answer is because they don’t see any benefit over what they can build via a responsive website. It’s as simple as that. Microsoft, lift your game. Fix your online presence and start promoting your developer platform again.

———-

Contact Built to Roam for more information on building applications for Windows

———-

Getting Started with Xamarin.Forms, Refactoring MvvmCross and Services

Getting Started with Xamarin.Forms, Refactoring MvvmCross and Services

Previous posts in this sequence:
Getting Started with Xamarin.Forms and Multi-Targeting in Visual Studio
Getting Started with Xamarin.Forms and SDK Versions
Getting Started with Xamarin.Forms with MvvmCross
Getting Started with Xamarin.Forms and Authenticating with ADAL

In this post I’m going to do a bit of a refactor based on the work completed in the previous post where we added code directly into the MainViewModel to use the Azure Active Directory Authentication Library. This would be fine if the MainViewModel was the only view model in the application that needed access to the Authenticate method. As I will probably want to make use of it elsewhere, I’ve decided to refactor that code into a class called CredentialService.

When I wired up MvvmCross a couple of posts ago, I did so without going into much detail on how the relationship between view models and their corresponding page is setup. I’m not going to delve into the inner workings of MvvmCross but suffice to say that there is some reflection magic that goes on based on the names of the classes. A view model with the name MainViewModel will be paired with a page called MainPage.

In addition to the magic that glues the view model and pages together (and thus the navigation model) MvvmCross also has a quite powerful dependency injection framework. The CredentialService that I’ll be creating will be registered with the DI framework as a singleton that can be easily accessed.

Let’s start by creating the CredentialService and it’s corresponding interface ICredentialService in a folder called Services in the Core library.

public partial class CredentialService:ICredentialService
{
     private const string AuthEndpoint = “
https://login.microsoftonline.com/{0}/oauth2/token”;


    private const string ResourceId = “https://graph.windows.net/”;
     private const string TenantId = “[your tenantId]”;
     private const string ClientId = “[your clientId]”;
     private const string RedirectURI = “[your redirectURI]”;


    public string AccessToken { get; set; }


    public async Task<string> Authenticate()
     {
         string authority = string.Format(CultureInfo.InvariantCulture, AuthEndpoint, TenantId);
         var authContext = new AuthenticationContext(authority);


        var result = await authContext.AcquireTokenAsync(ResourceId, ClientId, new Uri(RedirectURI), this.PlatformParameters);
         AccessToken = result.AccessToken;
         return AccessToken;
     }
}

public interface ICredentialService
{
     Task<string> Authenticate();
}

The platform specific code will need to be extracted out of the MainViewModel.Platform.cs into the corresponding CredentialService.Platform.cs. For example the UWP class would be:

public partial class CredentialService
{
     private IPlatformParameters PlatformParameters => new PlatformParameters(PromptBehavior.Always, false);
}

In order to register the CredentialService with the DI framework, we just need to add a small bit of code to the Initialize method of the App class in the Core library

public override void Initialize()
{
     CreatableTypes()
         .EndingWith(“Service”)
         .AsInterfaces()
         .RegisterAsLazySingleton();


    RegisterAppStart<MainViewModel>();
}

The last step is to make use of the service in the MainViewModel. We do this by adding a parameter of type ICredentialService to the MainViewModel constructor.

private ICredentialService CredentialService { get; }
public MainViewModel(ICredentialService credentialService)
{
     CredentialService = credentialService;
}


private async Task Authenticate()
{
     Debug.WriteLine(await CredentialService.Authenticate());
}

And that’s it – we haven’t changed any functionality but the solution is better organised and structured in a way that services can easily be added without cluttering up view models.

image

Getting Started with Xamarin.Forms and Authenticating with ADAL

Getting Started with Xamarin.Forms and Authenticating with ADAL

Previous posts in this sequence:
Getting Started with Xamarin.Forms and Multi-Targeting in Visual Studio
Getting Started with Xamarin.Forms and SDK Versions
Getting Started with Xamarin.Forms with MvvmCross

Now that we’ve got the basics of an application, we’re going to add some authentication using the Azure Active Directory Authentication Library. We’re going to start with registering an application with Azure Active Directory (AAD) (the docs do cover this here but I figured I’d cover it again anyway). Start by heading over to the Azure portal and selecting Azure Active Directory from the set of tabs on the left.

image

In the list of tabs for the AAD, find the App Registrations tab

image

Click “New application registration” and fill in the details for the new app

image

Note that the Sign-on URL doesn’t have to be a real URL, just one that both the portal and the app know about – we’ll use this later! After you’re done creating the app registration you should be able to retrieve the Application ID. Save this information for later as we’ll need it when we’re authenticating the user within our application.

image

The pieces of information you’ll need in order to use ADAL within your application are:

clientId – This is the Application ID that was displayed after you created the app registration in the Azure portal. This is a GUID

redirectUri – This is the Redirect URI that you specified when creating the app registration. If you didn’t write this down, or you want to double check, or change it, you can do so by clicking the Redirect URIs tab under Settings for the app registration. This is a URL (e.g. https://stratapark)

resourceId – This is the identifier of the resource you want to access. In this case we just want to authenticate the user, so we’re going to use the default resource, which is the AAD directory itself, https://graph.windows.net/. This can be a URL or a GUID depending on the type of resource you’re referencing

tenantId – This is the identifier of the tenant that you’re authenticating against. This can be the URL or GUID of your tenant.

To retrieve the tenantId you need to select the Properties tab of the Azure Active Directory and copy the Directory ID

imagehttps://www.nuget.org/packages/Microsoft.IdentityModel.Clients.ActiveDirectory/

Now that we’ve setup the app registration, we’re good to start writing some code. Firstly, we need to add a reference to the ADAL library from NuGet – search for Microsoft.IdentityModel.Clients.ActiveDirectory

image

Add the ADAL library to all projects.

Let’s start with the UI and add a button to the MainPage.

<Button Text=”Authenticate” Command=”{Binding AuthenticateCommand}” />

And of course we need a command within the MainViewModel. In fact we’ll add the bulk of the authentication code to the MainViewModel too:

private IMvxCommand authenticateCommand;
public IMvxCommand AuthenticateCommand => authenticateCommand ?? (authenticateCommand = new MvxAsyncCommand(Authenticate));


private const string AuthEndpoint = “https://login.microsoftonline.com/{0}/oauth2/token”;


private const string ResourceId= “https://graph.windows.net/”;

private const string TenantId = “[your tenantId]”;
private const string ClientId = “[your clientId]”;
private const string RedirectURI = “[your redirectUri]”;


public async Task Authenticate()
{
     string authority = string.Format(CultureInfo.InvariantCulture, AuthEndpoint, TenantId);
     var authContext = new AuthenticationContext(authority);


    var result = await authContext.AcquireTokenAsync(ResourceId, ClientId, new Uri(RedirectURI), PlatformParameters);
     Debug.WriteLine( result.AccessToken);
}

The only thing missing is the PlatformParameters (bolded in the code above). As the name suggests, these are platform specific parameters required by the ADAL library. For each platform we need to expose a property that returns an IPlatformParameters within the MainViewModel.Platform.cs for each platform.

Android

private IPlatformParameters PlatformParameters => new PlatformParameters(CurrentActivity, true, PromptBehavior.Auto);
public Activity CurrentActivity
{
     get
     {
         var top = Mvx.IoCProvider.Resolve<IMvxAndroidCurrentTopActivity>();
         return top.Activity;
     }
}

iOS

private IPlatformParameters PlatformParameters => new PlatformParameters(GetTopViewController(),true, PromptBehavior.Auto);
public static UIViewController GetTopViewController()
{
     var window = UIApplication.SharedApplication.KeyWindow;
     var vc = window.RootViewController;
     while (vc.PresentedViewController != null)
         vc = vc.PresentedViewController;


    if (vc is UINavigationController navController)
         vc = navController.ViewControllers.Last();


    return vc;
}

UAP

private IPlatformParameters PlatformParameters => new PlatformParameters(PromptBehavior.Always, false);

NetStandard

private IPlatformParameters PlatformParameters => null;

Note: you need to provide the NetStandard implementation otherwise the NetStandard target won’t build.

There is one more thing we need to add. Android requires an additional piece of code within the Activity that hosts the Xamarin.Forms application

protected override void OnActivityResult(int requestCode, Result resultCode, Intent data)
{
     base.OnActivityResult(requestCode, resultCode, data);
     AuthenticationAgentContinuationHelper.SetAuthenticationAgentContinuationEventArgs(requestCode, resultCode, data);
}

If your Android application authenticates but doesn’t ever return the access token, chances are that your OnActivityResult override is either missing, or on the wrong Activity – I spent a bit of time trying to work out why iOS and UWP worked but Android would just hang; turns out I was missing this override.

There you have it – you’re good to go with using ADAL to authenticate your users within your Xamarin.Forms application

Getting Started with Xamarin.Forms with MvvmCross

Getting Started with Xamarin.Forms with MvvmCross

Previous posts in this sequence:

Getting Started with Xamarin.Forms and Multi-Targeting in Visual Studio

Getting Started with Xamarin.Forms and SDK Versions

Next up is adding in MvvmCross – no surprises here: Add the MvvmCross package to all projects in the solution

image

We need to make a few adjustments to the Core library. First up, we need to add an App class to Core that inherits from MvxApplication. For the moment the only thing this class is going to do is to set the initial view model of the application. One of the core ideas behind MvvmCross is that navigation is driven from view models, rather than at the view level. By setting the initial view model, we’re effectively defining which view will be shown when our application launches.

public class App : MvxApplication
{
     public override void Initialize()
     {
         RegisterAppStart<MainViewModel>();
     }
}

We’ll do a bit of tidying up whilst we’re making changes to the Core project. We’ll create a ViewModels folder and move MainViewModel into folder

We’ll update MainViewModel and MainViewModel.Platform.cs (all four) to add ViewModels to namespace. We’ll also update MainViewModel to inherit from MvxViewModel.

Now that we’ve updated the Core library, we’ll turn our attention to the UI library and the head projects. We’ll add the MvvmCross.Forms NuGet reference to all projects, except Core.

image

The only other change we need to make to the UI library for the moment is to update MainPage to inherit from MvxContentPage instead of ContentPage. You’ll need to do this in both the xaml and xaml.cs files. However, I would recommend dropping the inheritance from the xaml.cs file as it is not required in multiple places.

Next up we need to adjust each of the head projects (UWP, iOS and Android) so that they use MvvmCross to launch the Xamarin.Forms views.

UWP

Rename MainPage to HostPage (in both MainPage.xaml and MainPage.xaml.cs, and the filenames themselves) in the UWP head project – we do this to avoid any confusion between the name of the page and any views that are automatically associated with MainViewModel. Also update HostPage to inherit from MvxFormsWindowsPage.

For UWP we need an additional step due to some limitation around the support for generics. Add a help class ProxyMvxApplication, defined as follows:

public abstract class ProxyMvxApplication : MvxWindowsApplication<MvxFormsWindowsSetup<Core.App, UI.App>, Core.App, UI.App> { }

Change App.xaml and App.xaml.cs to inherit from ProxyMvxApplication, and remove all code in App.xaml.cs other than the constructor which should contain a single call to InitializeComponent. Override the HostWindowsPageType method in the App class

protected override Type HostWindowsPageType()
{
     return typeof(HostPage);
}

Build and run the UWP application

iOS

Update the AppDelegate as follows

public partial class AppDelegate : MvxFormsApplicationDelegate<MvxFormsIosSetup<Core.App, UI.App>, Core.App, UI.App>
{ }

Build and run the iOS application.

Android

Rename and update the MainActivity as follows. Again, we rename the activity to avoid confusion with any activity or view that would be automatically associated with MainViewModel.

public class HostActivity
         : MvxFormsAppCompatActivity<MvxFormsAndroidSetup<Core.App, UI.App>, Core.App, UI.App>
{ }

Build and run the Android application.

You may experience a build error when you attempt to build and run the Android application, relating to referencing the Mono.Android.Export library. In this case, use the Add Reference dialog to manually add the missing reference.

image

There you have it – all three platforms running Xamarin.Forms application powered by MvvmCross.

Who broke my messaging app?

Who broke my messaging app?

Prior to the iPhone the majority of applications written were for desktop based devices, whether they be for a Mac or PC, or in some cases both. With the iPhone we saw an entire industry of mobile app development opening up from being a very niche, specialist field, into a field full of imposters who claimed they knew how to build a mobile application, just because they were now able to build and publish an iPhone app. The first generation of iPhone applications weren’t anything special and in fact if you compared the sophistication of most iPhone applications from that era to those being written for other mobile platforms, they were definitely a case of “beauty over brains” – You have to hand it to Apple, as they really did make it easy to build high quality apps that looked great, even if they suffered from a lack of substance.

So hang on, how does this relate to my messaging app? Well, whilst the quality and complexity of mobile applications has increased, one thing that hasn’t is that they are all single window. Due to the screen real estate, mobile application have a single window. With the introduction iPad and other tablets, we saw the use of split screen and master-detail style layouts but even these were all contained within a single window. In the context of a messaging application, whether it be Skype, Messenger, WhatsApp, Twitter etc, the layout is all basically the same – you have a list of contacts and/or groups, you tap into a contact or group and are taken to a chat interface where you can message. In the case of devices with more screen, often the contacts/groups will be listed in a pane on the left or right of the current conversation but in both cases, if you have multiple conversations going, you have to switch between active conversations.

I have nothing against the single window view when working on a mobile device, and I think I’m ok with it on a tablet device but when we graduate to a laptop/desktop we enter a world of inefficiencies and poor user experience. My mobile comes with me virtually everywhere I go but when I’m working, or at home surfing the internet, I’m typically on my laptop (as an aside, I’m running the Surface Book 2 i7 15” and it’s an amazing bit of kit – highly recommend it!). I’ll often have a combination of Messenger, Skype, Teams, LinkedIn and Twitter open and will switch between conversations within each as required. However, typically at any given point in time I’ll be involved in one or two conversations, and it’s really annoying to have to keep switching between these conversations and whatever else I might be doing. For example I might be having a couple of DM conversations on Twitter but at the same time catching up with what’s been happening on my main feed – this means I have to keep switching between conversations, rather than being able to keep them both open at once.

Now I think you can see where I’m going with this – I run an operating system that I think most of you will have heard of…. Windows…. not “Window” but “Windows” <<- plural!!

Why is it that very few modern applications make use of multiple-windows? Well, it’s because of the iPhone – for the last decade app developers (and don’t forget all those app designers who can’t comprehend how an app can use multiple windows) have been building apps that only use one window because building iPhone applications was where the money was, and still is. It doesn’t help that Android apps are also predominately single window.

Let’s go back a few years an look at some user interfaces that have been discarded in favour of the now pathetic single-window messaging app experience:

Do you remember mIRC?

Whilst it was technically one window, it did support having multiple conversations open at the same time using child windows.

Related image

Do you remember MSN/Live Messenger?

It had multiple windows that you could open an arrange how you like. In fact you could force a window to stay on top of everything so it didn’t get obscured by other windows you might be switching between.

Image result for live messenger multi window

So where to from here?

Well the good news is that Windows app developers already have great multi-windowing support out of the box – you just need to build your application to take advantage of it (see https://docs.microsoft.com/en-us/windows/uwp/design/layout/show-multiple-views).

The bad news is that I doubt any of the major messaging services will take advantage of this in the short term, so we’re going to be stuck with a sub-optimal experience. Neither Skype and Teams, both Microsoft products, have any form of multi window support. Messenger still looks like it has been thrown together by some intern at Facebook, so I doubt we’ll see any innovation there. Twitter is a PWA, so I’m not sure how easy it will be for it to take advantage of multiple windows – having said that, as a PWA it can access any of the Windows 10 APIs, so there should be no reason why it can’t spawn additional windows.

Microsoft Whiteboard

Microsoft Whiteboard

I noticed the other day that Microsoft have added another app to the suite of apps that are available to Office 365 subscribers, Microsoft Whiteboard (https://products.office.com/en-us/microsoft-whiteboard/digital-whiteboard-app). On downloading it from the Store I was immediately impressed with the overall look and feel of the app – very professional and clearly showcases what can be done with the Windows platform.

Having said this, here are my immediate frustrations:

Where are the apps for other platforms?

Namely iOS, Android, Web, MacOS – it’s very transparent that this is a push to promote how great the inking experience is on Windows but for this to be a viable solution for businesses it needs to be available anywhere

Why upload PNGs?

Being a developer I immediately ran the app through Fiddler and what shocked me was that PNGs are being uploaded. I haven’t delved into how the synchronisation process works when multiple people are collaborating but I can’t imagine any scenario where uploading PNG is efficient. If you look at what other shared drawing experiences do (eg http://cosketch.com) there is no sharing of image, rather the line segments are sent back and forth.

Why isn’t this a Control

Again with my developer hat on, this shared whiteboard needs to be made available as an Office 365 control that developers can simply drop into their application in order to integrate a shared whiteboard experience. As more applications are built that tap into the Microsoft Graph and leverage the fact that users are connected with either an MSA or an Office 365 account, having rich component such as this would significantly cut development time and make it easier to build amazing applications.

Overall I’m impressed with Microsoft Whiteboard and hope that this is a sign of some of the great innovation that the Microsoft 365 platform will bring with it.

Styling the MediaPlayerElement and MediaTransportControls in a Universal Windows Platform (UWP) Application

Styling the MediaPlayerElement and MediaTransportControls in a Universal Windows Platform (UWP) Application

In my previous post, Building Media Applications for the Universal Windows Platform (UWP) using the MediaPlayerElement, I covered the basics of building a UWP media application using the MediaPlayerElement. Towards the end of the post I showed how you can override some of the built in behaviour but I was yet to show you how you can adjust the visual style of the player or the controls. In this post I’ll do just that, I’ll show you where to find the built in styles and how to start customizing them, including some gotchas that you should be aware of.

Ok, let’s get started by looking for the built in styles. Whilst Microsoft now do a good job of documenting the built in styles online (for example the styles and templates for the MediaTransportControls element is defined at https://msdn.microsoft.com/en-us/library/windows/apps/mt414180.aspx), there is nothing like looking at what ships in the platform. For this we go to generic.xaml, typically located at C:Program Files (x86)Windows Kits10DesignTimeCommonConfigurationNeutralUAP10.0.16299.0Generic – Note that this may vary based on which platform SDK you have installed. Inside generic.xaml I’m going to locate the Style for the MediaPlayerElement:

<Style TargetType=”MediaPlayerElement”>
     <Setter Property=”HorizontalAlignment” Value=”Stretch” />
     <Setter Property=”VerticalAlignment” Value=”Stretch” />
     <Setter Property=”IsTabStop” Value=”False” />
     <Setter Property=”Template”>
         <Setter.Value>
             <ControlTemplate TargetType=”MediaPlayerElement”>
                 <Grid x_Name=”LayoutRoot”>
                     <Border Background=”Transparent” />
                     <Image  x_Name=”PosterImage”
                         Visibility=”Collapsed”
                         Source=”{TemplateBinding PosterSource}”
                         Stretch=”{TemplateBinding Stretch}” />
                     <MediaPlayerPresenter x_Name=”MediaPlayerPresenter”
                         IsFullWindow=”{TemplateBinding IsFullWindow}”
                         Stretch=”{TemplateBinding Stretch}”
                         MediaPlayer=”{TemplateBinding MediaPlayer}” />
                     <ContentPresenter     x_Name=”TransportControlsPresenter”
                         Visibility=”{TemplateBinding AreTransportControlsEnabled}” />
                     <Grid x_Name=”TimedTextSourcePresenter” />
                 </Grid>
             </ControlTemplate>
         </Setter.Value>
     </Setter>
</Style>

As you can see, the Style for the MediaPlayerElement is made up of a few different components:

  • MediaPlayerPresenter – this is where the actual video content will be displayed
  • ContentPresenter – this is where the player controls will be displayed
  • “TimedTextSourcePresenter” – this is where the closed captions will be presented

Let’s make a subtle change here by adding another Grid immediately after the TimedTextSourcePresenter with a semi-transparent Pink colour and a TextBlock to indicate that this is a customized player.

<Page.Resources>
     <Style TargetType=”MediaPlayerElement”>
         <Setter Property=”HorizontalAlignment”
                 Value=”Stretch” />
         <Setter Property=”VerticalAlignment”
                 Value=”Stretch” />
         <Setter Property=”IsTabStop”
                 Value=”False” />
         <Setter Property=”Template”>
             <Setter.Value>
                 <ControlTemplate TargetType=”MediaPlayerElement”>
                     <Grid x_Name=”LayoutRoot”>
                         <Border Background=”Transparent” />
                         <Image  x_Name=”PosterImage”
                                 Visibility=”Collapsed”
                                 Source=”{TemplateBinding PosterSource}”
                                 Stretch=”{TemplateBinding Stretch}” />
                         <MediaPlayerPresenter x_Name=”MediaPlayerPresenter”
                                                 IsFullWindow=”{TemplateBinding IsFullWindow}”
                                                 Stretch=”{TemplateBinding Stretch}”
                                                 MediaPlayer=”{TemplateBinding MediaPlayer}” />
                         <ContentPresenter     x_Name=”TransportControlsPresenter”
                                                 Visibility=”{TemplateBinding AreTransportControlsEnabled}” />
                         <Grid x_Name=”TimedTextSourcePresenter” />
                        <Grid Background=”Pink”
                                 IsHitTestVisible=”False”
                                 Opacity=”0.4″>
                             <TextBlock Foreground=”Black”
                                         Text=”Custom Player” HorizontalAlignment=”Left” VerticalAlignment=”Bottom” Margin=”50,5,5,5″/>
                         </Grid>

                     </Grid>
                 </ControlTemplate>
             </Setter.Value>
         </Setter>
     </Style>
<Page.Resources>

You’ll notice that I’ve added this Style into the Resources dictionary for the page and that it’s an implicit style (ie I haven’t assigned it a Key) which means that any MediaPlayerElement on this page will pick up this style. You can of course set this Style either directly on the MediaPlayerElement itself or even define it in an application level resource dictionary.

image

Note also that the IsHitTestVisible attribute is set on the Grid, otherwise it will block user input to the player controls as the grid sits over the top of the other elements within the MediaPlayerElement control template.

If you recall from my previous post I intercepted the behaviour that steps the video back and forward so that I could customize this behaviour. Of course, this means that the step forward/backward icons aren’t accurate, since I’m not longer stepping back by 10seconds and forward by 30seconds. In order to override the icons on these buttons, I need to override the Style for the MediaTransportControls. Again the default Style can be found in generic.xaml – it’s really long, so I’m not going to copy the whole thing in here. Here’s a cut down version of the Style showing the changes to the FontIcon for the SkipBackwardButton and SkipForwardButton.

<Page.Resources>
     <Style TargetType=”MediaTransportControls”>
     …
         <Setter Property=”Template”>
             <Setter.Value>
                 <ControlTemplate TargetType=”MediaTransportControls”>
                     <Grid x_Name=”RootGrid”
                             Background=”Transparent”>
             …   
             <AppBarButton x_Name=’SkipBackwardButton’
                          Style='{StaticResource AppBarButtonStyle}’
                          MediaTransportControlsHelper.DropoutOrder=’5′
                          Visibility=’Collapsed’>
                           <AppBarButton.Icon>
                         
  <!–<FontIcon Glyph=”&#xED3C;” />–>
                            <FontIcon Glyph=”&#xF100;”
                                      FontFamily=”FontAwesome” />

                           </AppBarButton.Icon>
                         </AppBarButton>
                         …
                         <AppBarButton x_Name=’SkipForwardButton’
                          Style='{StaticResource AppBarButtonStyle}’
                          MediaTransportControlsHelper.DropoutOrder=’5′
                          Visibility=’Collapsed’>
                           <AppBarButton.Icon>
                         
  <!–<FontIcon Glyph=”&#xED3D;” />–>
                            <FontIcon Glyph=”&#xF101;”
                                      FontFamily=”FontAwesome” />
                           </AppBarButton.Icon>

                         </AppBarButton>
                     </Grid>
                 </ControlTemplate>
             </Setter.Value>
         </Setter>
     </Style>
</Page.Resources>

You might be wondering where I got the new hex code for the FontAwesome font. Rather than go online and having to hunt down hex values for the different characters within a font, if you open Word and go to Insert, Symbol, you can then select the Font and select the character you want. In the lower right corner it states what the Character code is. Remember that you need to make sure the font exists on the computer you’re going to run the application on, so you may want to consider including the font within your application if you pick a non-standard font.

image

Ok, so running the application now, we can see the updated skip backward and skip forward buttons:

image

Unfortunately, here lies a major bug in the implementation of the MediaPlayerElement – if you press the full screen button, you’ll see that the Style for the MediaTransportControls has reverted back to the built in style.

image

This happens because when the video runs in full screen, the MediaTransportControls are being hosted in a separate visual tree to the main part of the application – you can see this if you look at the Live Visual Tree.

image

So, the question is how to handle this? Well the good news is that it’s surprisingly simple. What’s happening is that as the MediaTransportControls are being attached to a different visual tree, they’re not able to resolve the Style defined within the scope of the page. Moving the custom Style for the MediaTransportControls to an application level resource fixes this issue.

<Application
     x_Class=”PlayerSample.App”
     http://schemas.microsoft.com/winfx/2006/xaml/presentation"”>http://schemas.microsoft.com/winfx/2006/xaml/presentation”
     http://schemas.microsoft.com/winfx/2006/xaml"”>http://schemas.microsoft.com/winfx/2006/xaml”
     RequestedTheme=”Light”>
     <Application.Resources>
         <Style TargetType=”MediaTransportControls”>
         …
         </Style>
     </Application.Resources>
</Application>

The documentation online at https://docs.microsoft.com/en-us/windows/uwp/controls-and-patterns/custom-transport-controls indicates that you should use an explicit style – two things a) this doesn’t fix the full screen issue; you must define the Style at an application level regardless of whether you make it an explicit or implicit style and b) you can make the choice yourself whether to use an implcit or explicit style, depending on whether you want multiple different player styles (ie where an explicit style makes sense).

Building Xbox One applications with the Universal Windows Platform

Building Xbox One applications with the Universal Windows Platform

When building a Universal Windows Platform application there are times when you’ll want a different layout/functionality for a particular platform. For the most part building applications across desktop and phone, other than dealing with different screen sizes and orientation, as a developer you can rely on a similar interaction model: mouce clicks and taps equate to roughly the same interaction for most applications. However, this is not the case with the Xbox – the interaction model using either the media remote or a controller relies on a focus state moving around the screen, followed by pressing the action button to select the currently focussed item. In addition to making sure that every control has an appropriate “in focus” visual, it’s essential to make sure that the flow of focus makes sense and that the user can’t get lost in the application (eg nothing focussed, or nothing appearing to be in focus, on the screen).

The different interaction model between desktop/mobile and Xbox means that you’ll often want to present a different layout, or perhaps expose different functionality on Xbox. There are a number of ways to handle this both in XAML and in code:

1. [C#] The device family can be detected using DeviceFamily property. This returns values such as Windows.Mobile, Windows.Desktop and Windows.Xbox which can be used to define business logic that may be platforms specific. Note that the family name for Surface Hub is Windows.Team

var family = Windows.System.Profile.AnalyticsInfo.VersionInfo.DeviceFamily

2. [XAML] The device family can be used to trigger different visual states. There are a number of state triggers that have been packaged together as a NuGet package – https://github.com/dotMorten/WindowsStateTriggers.

<VisualStateManager.VisualStateGroups>
     <VisualStateGroup x_Name=”PlatformStates”>
            <VisualState x_Name=”Xbox”>
             <VisualState.Setters>
                 <Setter Target=”textBlock.(TextBlock.Text)”
                         Value=”Running on Xbox” />
             </VisualState.Setters>
             <VisualState.StateTriggers>
                 <WindowsStateTriggers:DeviceFamilyStateTrigger DeviceFamily=”Xbox” />
             </VisualState.StateTriggers>
         </VisualState>
         … other platform states …
     </VisualStateGroup>
</VisualStateManager.VisualStateGroups>

3. [XAML] Use different XAML files for each device family

Main.xaml  // The default XAML file

Main.DeviceFamily-Xbox.xaml  // An Xbox specific XAML file defined within the file name

/DeviceFamily-Xbox/Main.xaml // An Xbox specific XAML file defined using a folder to group together platform specific files.

Using these three different methodds might seem easy enough at first but you start to realise how frustrating it is when you want to work on a specific feature (for example navigation) but you don’t have a specific device with you (for example there’s no Xbox nearby for running the app on). To at least partial solve this problem, here are three tips that will allow you to work on your Xbox app on your desktop.

1. [C#] Instead of referencing the AnalyticsInfo class, we can add a wrapper class that allows us to override the device family whilst debugging the application. Use DeviceInfo.DeviceFamily instead of AnalyticsInfo.VersionInfo.DeviceFamily

#if DEBUG
#define DEBUG_PLATFORM_XBOX
#endif


public static class DeviceInfo
{
     public static string DeviceFamily
     {
         get
         {
#if DEBUG_PLATFORM_XBOX
             return “Windows.Xbox”;
#else
             return AnalyticsInfo.VersionInfo.DeviceFamily;
#endif
         }
     }
}

In this case the compilation constant DEBUG_PLATFORM_XBOX is used to override the actual DeviceFamily value. Note we’ve wrapped this using a #if for the DEBUG constant – this prevents the override accidentally being deployed as part of a release build.

2. [XAML] The DeviceFamilyStateTrigger (see https://github.com/dotMorten/WindowsStateTriggers/blob/master/src/WindowsStateTriggers/DeviceFamilyStateTrigger.cs) uses the DeviceFamily returned by the AnalyticsInfo class. A clone of this trigger can be made that relies on DeviceInfo.DeviceFamily (from [1]); this would activate states based on the overridden device family value.

3. [XAML] In order to debug the Xbox layout (eg MainPage.DeviceFamily-Xbox.xaml) on a desktop machine, simply rename the file to specify the Desktop device family (ie MainPage.DeviceFamily-Desktop.xaml).

Combining these three tips allows for the majority of the application to look and feel like it would when running on an Xbox. Unfortunately some of the built in components do render differently on desktop and xbox – they will always render as per the actual device family, despite attempts to override this value.

Building Media Applications for the Universal Windows Platform (UWP) using the MediaPlayerElement

Building Media Applications for the Universal Windows Platform (UWP) using the MediaPlayerElement

Over the past couple of years at Built to Roam we’ve built a number of media applications. Back when UWP was first launched, all media was played using the MediaElement, which was fairly familiar to XAML developers as it’s been the core element for any video or audio playback on Windows and Windows Phone for quite some time. Unfortunately the built in controls on the MediaElement were not easily stylable, resulting in most developers switching to using the Player Framework, which provided a host of capabilities out of the box such as closed captions, stylable regions and controls, and nice viewmodel architecture that made data binding easy.

Fast forward a year and a bit, and there’s a new player in town, literally. The MediaPlayerElement is a new control that shipped with the Anniversary update (v10.0.14393.0) and is definitely the control that you want to use if you want to do fancy things, such as a persistent video overlay where playback continues as the user continues to navigates throughout the application – this can be achieved by detaching the internal MediaPlayer from one MediaPlayerElement and attach it to another. It’s important to note that whilst the MediaPlayerElement has been around since the Anniversary update, features have been added in both the Creators update and more recently the Fall Creators update, so make sure you take a look at the documentation to make sure you are using features that align with the minimum and target versions you have set for your application.

In this post we’re going to look at the basics of using the MediaPlayerElement and how you can alter its styling. I’m going to start with a new UWP project, selecting the Creators update as the minimum version and the Fall Creators update as the target version. The MediaPlayerElement can be super simple to use – for example the following code is the simplest way to add video to a page that will automatically start playing.

<MediaPlayerElement Source=”http://sample-videos.com/video/mp4/240/big_buck_bunny_240p_30mb.mp4″  AutoPlay=”True”/>

By default the MediaPlayerElement doesn’t show any of the typical playback controls you’d expect, instead it’s just a raw video canvas that can be used to render video anywher on a page or control. In order to enable the default controls, you simply need to add the AreTransportControlsEnabled attribute set to true (I’ve removed the AutoPlay attribute as there are now going to be playback controls that can be used to start/stop video etc)

<MediaPlayerElement Source=”http://sample-videos.com/video/mp4/240/big_buck_bunny_240p_30mb.mp4″  AreTransportControlsEnabled=”True”/>

The following image shows the default controls with Progress Bar, Play/Pause, Volume, Aspect Ratio, Cast and Full screen buttons and as you’d imagine, they’re all connected to the appropriate action.

image

But what about other controls like fast forward/rewind, skip forward/backward etc. To customise what controls appear you need to override the TransportControls attribute on the MediaPlayerElement:

<MediaPlayerElement Source=”http://sample-videos.com/video/mp4/240/big_buck_bunny_240p_30mb.mp4″
                     AreTransportControlsEnabled=”True”>
    <MediaPlayerElement.TransportControls>
         <MediaTransportControls IsSkipForwardButtonVisible=”True”
                                 IsSkipBackwardEnabled=”True”
                                 IsSkipBackwardButtonVisible=”True”
                                 IsSkipForwardEnabled=”True”
                                 IsFastForwardButtonVisible=”True”
                                 IsFastForwardEnabled=”True”
                                 IsFastRewindButtonVisible=”True”
                                 IsFastRewindEnabled=”True” />
     </MediaPlayerElement.TransportControls>

</MediaPlayerElement>

Here I’ve set the TransportControls attribute to be a new instance of the MediaTransportControls class – this is actually what the default behaviour does; the difference is that I now have access to an instance that I can set attributes on. Here I’m enabling, and showing both the buttons to skip forward/backward and the buttons for fast forward/rewind. The updated layout looks like the following:

image

As you can see from the image the skip forward and backward buttons default to 30 and 10 seconds respectively. In order to adjust these you’ll need to override the behaviour that is associated with the skip forward/backward buttons, which can be done by attaching an event handler to the PositionReceived event on the MediaPlaybackCommandManager:

protected override void OnNavigatedTo(NavigationEventArgs e)
{
     base.OnNavigatedTo(e);


    Player.MediaPlayer.CommandManager.PositionReceived += CommandManager_PositionReceived;
}


private void CommandManager_PositionReceived(MediaPlaybackCommandManager sender, MediaPlaybackCommandManagerPositionReceivedEventArgs args)
{
     sender.MediaPlayer.PlaybackSession.Position = TimeSpan.FromMinutes(2);
     args.Handled = true;
}

In this case, I’ve named the MediaPlayerElement in the XAML (ie by adding x_Name=”Player” to the MediaPlayerElement), and then when the page has been navigated to I’m attaching the appropriate event handler. When either skip forward or backward is pressed, the position in the playback is set to the 2 minute mark and the args.Handled property is set to true, effectivey overriding the default skip behaviour. Big reminder: if you are attaching event handlers in code, make sure you detach them too (ie PositionReceived –= CommandManager_PositionReceived in the OnNavigatedFrom method).

The fast forward and fast rewind buttons will increment the playback speed up and down by doubling the rate over the range of rates supported by the content (eg –8, –4, –2, –1, 1, 2, 4, 8). The MediaTransportControls class is clever enough to detect the supported range of rates and automatically disable the fast foward/rewind buttons when the limit on the range has been reached.

Note: In working with the MediaPlayerElement, and even in preparing the sample code for this blog post, I was not able to get the fast rewind functionality to work. Fast foward appears to work but it causes the control bar to hide and show in a weird oscillating fashion. My recommendation would be to give these buttons a wide birth as this feature is severely under baked – if you want to implement something similar, perhaps with thumbnails, you should consider implementing it yourself!!! (and yes, we’ve had to do this on a project)

In this post, you’ve seen the basics of adding a MediaPlayerElment and playing some content using the built in media controls. I’ll go into more detail soon on really customising the style of your player.

Update: It seems that by setting the minimum version of the UWP application to the Fall Creators update, it appears that fast forward/rewind seems to work. I’m guessing it may have been fixed!

Getting Started: Xamarin Forms with .NET Standard 2.0

Getting Started: Xamarin Forms with .NET Standard 2.0

In my earlier post Getting Started: Xamarin Forms with .NET Standard I covered how to create a new Xamarin Forms project which uses a .NET Standard 1.4 library to share the views between iOS, Android and UWP. At the time, whilst iOS and Android supported .NET Standard 2.0, support still wasn’t available for UWP. Almost immediately after publishing the blog post, Microsoft announced that Visual Studio 2017 preview 15.4 would allow UWP applications to reference .NET Standard 2.0 libraries. Unfortunately this didn’t work in the first drop, 15.4.0 Preview. This was just updated to 15.4.0 Preview 2 (Release Notes: https://www.visualstudio.com/en-us/news/releasenotes/vs2017-preview-relnotes), which brings with it the support we’ve been after. In this post, I’m going to repeat the previous post on getting started with .NET Standard, this time using .NET Standard 2.0 for the UI project.

Let’s walk through the basics – create a new Cross Platform App (same as before)

image

Select the Xamarin.Forms (UI Technology) and Portable Class Library (PCL) (Code Sharing Strategy) – Don’t pick the Shared Project option!

image

Select the Insider Preview version of UWP for both Minimum and Target version – this is required for .NET Standard 2.0 support. If you want to target earlier versions of Windows 10, you’ll have to stick with .NET Standard 1.4.

image

Next, we’re going to replace the PCL with a new .NET Standard library

image

I’ll copy the App.xaml, App.xaml.cs, MainPage.xaml and MainPage.xaml.cs from the PCL into the .NET Standard library, before deleting the PCL from the project (see https://nicksnettravels.builttoroam.com/post/2017/08/26/Getting-Started-Xamarin-Forms-with-NET-Standard.aspx for more detailed instructions).

The big difference is that I’m not going to change the default Target Framework, leaving it as .NET Standard 2.0.

image

Next I need to make sure I add and upgrade references to Xamarin.Forms to each of the projects – this isn’t actually required, since the stable release of Xamarin Forms will actually work with .NET Standard but I’ve been working with the pre-release version quite a bit lately, so I’ll go with that for this example.

image

I also need to remember to add a reference to the .NET Standard project to each of the head projects for iOS, Android and UWP.

image

If you attempt to build and run at this point iOS and Android should work without issue. UWP will most likely compile but will raise an exception “Could not load file or assembly ‘netstandard, Version=2.0.0.0….” at runtime.

image

Essentially the UWP project structure has evolved a little, so you need to upgrade it. Now I think that you may be able to do this via package manager but I’ve never got it to work for UWP projects, so I will make the changes manually to the csproj file. Start by deleting the project.json file from the UWP project.

Next right-click the UWP project in Solution Explorer and select unload project. Next, right-click on the UWP project node and select Edit MySecondXamarinFormsApp.UWP.csproj.

Add a new PropertyGroup – this changes the way packages are referenced, eliminating the need for the project.json file, replacing it with references within the csproj file.

<PropertyGroup>
   <RestoreProjectStyle>PackageReference</RestoreProjectStyle>
</PropertyGroup>

image

Next we need to add back the package references that were in the project.json – if you’re doing this on an existing project, you may want to keep the project.json file handy so you know which packages to add. In this case there are just two projects:

<ItemGroup>
   <PackageReference Include=”Microsoft.NETCore.UniversalWindowsPlatform”>
     <Version>6.0.0-preview1-25631-01</Version>
   </PackageReference>
   <PackageReference Include=”Xamarin.Forms”>
     <Version>2.4.0.269-pre2</Version>
   </PackageReference>
</ItemGroup>

image

Now you can right-click on the UWP project in Solution Explorer and select Reload project. Trigger a rebuild and now you should be able to run the UWP project.

image