Running Android Device Tests for Xamarin.Essentials on Windows

As a developer working with Xamarin or Xamarin.Forms you should be aware of the Xamarin.Essentials package that Microsoft have been developing that “provides developers with cross-platform APIs for their mobile applications”. If you haven’t taken the time to look through the source code, it’s well worth cloning the repository and taking a look. Not only is this a great example of how to do multi-targeting, they’ve also taken the time to invest in device specific tests. In this post we’re going to look at how to run the device tests for Android in the same way that it’s run as part of the Azure Pipelines build script.

The first thing to do is of course clone a copy of the repository and then launch the solution in Visual Studio – before we try to run the device tests we’d better make sure everything is able to be built and run. Unless you’ve been using the same machine for a long time, you’re unlikely to have all the difference MonoAndroid SDKs installed. When you take a look at the Dependencies for Xamarin.Essentials in the Solution Explorer window it’s likely to look similar to what mine did.

The good news is that if you force a build, some of these are likely to go away. For example I actually did have 8.0 and 8.1 installed, so after the build they were resolved and the warning indicators disappeared. Unfortunately, the bad news is that I still had a couple of missing versions.

Luckily this is easily resolved using the Android SDK Manager (launch this from Tools > Android > Android SDK Manager from within Visual Studio), where you can install the missing SDK Platforms.

You’ll need to accept the license terms.

Installing the missing Android SDKs should resolve the Android dependencies. If you’re still seeing a warning for the UAP dependency, you need to verify that you’ve got the Windows SDK v 10.0.16299 installed.

The build process for Xamarin.Essentials is driven from the azure-pipeline.yml file in the root of the repository. This YAML file defines various jobs, one of which is the devicetests_android, which as the name suggests, runs the device tests. Actually this job invokes build.sh, specifying the test-android-emu target. The build.sh is actually just a proxy for running build.cake.

I wanted to run the device tests on Windows, so I’m going to be invoking build.ps1, which is the Powershell equivalent for invoking build.cake. The following command invokes Powershell, passes the build.ps1 as the Powershell script to invoke, and then I’ve included the other parameters that were specified in the azure-pipeline.yml.

powershell -f build.ps1 --target=test-android-emu --settings_skipverification=true --verbosity=diagnostic

Note: You need to invoke this command from the DeviceTests folder.

Unfortunately, simply running the Powershell script from a command prompt isn’t sufficient. There are a few steps you’ll need to jump through in order to get the script to run correctly. Without making any changes, if you run the Powershell script you’ll most likely see an error similar to the following image.

Whilst the error is highlighted in red, the actual cause of the error is in light grey font and indicates that JAVA_HOME hasn’t been defined. Easily fixed by using the set command (make sure the path to the JDK matches where it is on your computer).

set JAVA_HOME=C:\Program Files\Android\Jdk\microsoft_dist_openjdk_1.8.0.25

Also, whilst we’re setting environment variables, check that ANDROID_HOME and ANDROID_SDK_ROOT are set to the root folder of the Android SDK installation (typically C:\Program Files (x86)\Android\android-sdk on Windows if installed via Visual Studio installer)

Suggestion: You’ll find that invoking the Powershell script can be quite time consuming because Xamarin.Essentials has to be built (once for each supported platform) and then the emulator needs to be created and launched. To accelerate this process, after running the script the first time, you can temporarily disable the build by commenting out the “.IsDependentOn("Build-Android")” line in the build.cake file.

Now, when you run the Powershell script, you’re likely to see a different error. Again, it’s not the highlighted error that contains the useful information. Looking a few lines earlier you can see that there is an error relating the system image that the script attempts to use.

Again, fixing this issue is relatively easy. You just need to pick one of the existing system images (listed alongside the error) and use it to set the ANDROID_EMU_TARGET variable.

set ANDROID_EMU_TARGET=system-images;android-29;google_apis_playstore;x86

The next error you’ll see is at the point where the script attempts to launch the emulator. It will fail, indicating that it can’t find the file specified.

It would appear that the build script, build.cake, attempts to call “emulator.bat”. However, this file doesn’t exist. Instead, there is an emulator.exe – we just need to adjust the build.cake to use .exe instead of .bat when locating the emulator command.

Unfortunately there’s also an issue with the search logic, resulting in using a copy of emulator.exe that doesn’t work. After making the above change to .exe, you’ll see that it attempts to launch the emulator but comes up with an error “PANIC: Missing emulator engine program for ‘x86’ CPU”.

Again, this issue is easily fixed with a small change to build.exe to get it to search for the emulator.exe in the correct folder. In the following code, we’ve adjusted the search logic to look in only the emulator folder on Windows.

if (ANDROID_HOME != null) {
        var andHome = new DirectoryPath(ANDROID_HOME);
        if (DirectoryExists(andHome)) {
            if(IsRunningOnWindows()){
                emulatorPath = MakeAbsolute(andHome.Combine("emulator").CombineWithFilePath("emulator" + emulatorExt)).FullPath;
                if (!FileExists(emulatorPath))
                    emulatorPath = "emulator" + emulatorExt;
            }
            else{
                emulatorPath = MakeAbsolute(andHome.Combine("tools").CombineWithFilePath("emulator" + emulatorExt)).FullPath;
                if (!FileExists(emulatorPath))
                    emulatorPath = MakeAbsolute(andHome.Combine("emulator").CombineWithFilePath("emulator" + emulatorExt)).FullPath;
                if (!FileExists(emulatorPath))
                    emulatorPath = "emulator" + emulatorExt;
            }
        }
    }

Finally, when you run the script now, it will run through without error. However, you will see a Windows Security Alert requesting a rule be added to the firewall. You’ll need to click the Allow access button in order for the emulator to talk back to the build script – it uses a TCP listener to retrieve the results from the device tests.

After granting permissions, you should see the following output – if you want more details on the device test output, there’s an xml file that is returned which has the full details of the test execution.

One last comment: If you have the emulator running when you run the device test script, you’ll find that it generates an error at the end of running the script. When the script attempts to launch the emulator, emulator.exe will detect the running emulator and will exit immediately. At the end of the script it attempts to terminate the emulator.exe process – since this process has already ended, it throws and error. You can ignore this error as the device tests will still have executed correctly.

Do Uno Mvvm?

Last week was a huge week for the Uno platform with their inaugural Uno conference, #UnoConf. As the technology continues to mature, I’ve no doubt that Uno will become a viable solution for building applications to target all sorts of markets. This includes support being progressively added by the various Mvvm frameworks.

Following my previous posts (MVVM Navigation with Xamarin.Forms Shell and MVVM Navigation with Xamarin.Forms Shell – Part II) where I discussed a simple approach to Mvvm with Xamarin.Forms, I figured I’d so something similar with Uno.

Mvvm with Uno

Let’s get on with it and start with a new Uno project – Download and install the Uno Platform Solution Templates extension from the Visual Studio marketplace, if you haven’t already. In Visual Studio, create a new project based on the Cross-Platform App (Uno Platform) project template. I’m going to call the app DoUnoMvvm.

Creating a Class Library

We’re going to separate out our viewmodels and services into a separate library, so add a new project, DoUnoMvvm.Core, based on the Class Library (.NET Standard) project template. Delete the class1.cs and then add a reference to the class library to each of the head projects (i.e. Droid, iOS, UWAP and Wams).

Adjusting NuGet Package References

Right-click on the solution node in the Solution Explorer window and select Manage NuGet Packages for Solution. Go to the Updates tab, check the Include prerelease option and then check the box alongside the packages Uno.Wasm.Bootstrap, Uno.UI, Microsoft.NETCore.UniversalWindowsPlatform and Uno.Core (don’t check either the Logging packages). Click Update to get the latest version of the packages that are checked.

From the Browse tab on the NuGet-Solution window used in the previous step, enter BuildIt.General.Uno into the search box. Select BuildIt.General.Uno and install the packages into all five of the projects.

Mvvm Basics with ViewModelLocator

Now we should be ready to start writing some code. We’re going to keep it simple with the following steps:

  • Create ViewModelLocator class – used for serving up viewmodels and creating services as required
  • Create an instance of ViewModelLocator in App Resources, making it accessible as a static resource in XAML
  • Create MainViewModel class – the viewmodel for the existing MainPage
  • Update ViewModelLocator with a property Main that returns an instance of the MainViewModel class
  • Set the DataContext of the MainPage to use the Main property on the ViewModelLocator
  • Run the application and show data is being served by the MainViewModel.

Here we go…. firstly a new ViewModelLocator class, which is added to the DoUnoMvvm.Core project

public class ViewModelLocator
{
}

Update App.xaml to create an instance of the ViewModelLocator class

<Application
    x:Class="DoUnoMvvm.App"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:DoUnoMvvm"
    xmlns:core="using:DoUnoMvvm.Core"
    RequestedTheme="Light">
  <Application.Resources>
    <core:ViewModelLocator x:Key="ViewModelLocator" />
  </Application.Resources>
</Application>

Now to create the MainViewModel, also in the DoUnoMvvm.Core project. We’ll create a property, WelcomeText, that will return some data to be displayed on MainPage.

public class MainViewModel
{
    public string WelcomeText => "How well do Uno Mvvm?";
}

We need to update the ViewModelLocator class to include the Main property

public class ViewModelLocator
{
    public MainViewModel Main => new MainViewModel();
}

And use this property when setting the DataContext for MainPage. I’ve also updated the TextBlock to be data bound to the WelcomeText property.

<Page
    x:Class="DoUnoMvvm.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:DoUnoMvvm"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    DataContext="{Binding Main, Source={StaticResource ViewModelLocator}}"
    mc:Ignorable="d">

  <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
    <TextBlock Text="{Binding WelcomeText}" Margin="20" FontSize="30" />
  </Grid>
</Page>

Run the application and there we have it, our first data bound page

Quick Navigation using Event Mapping

That’s pretty much the basics of Mvvm. However, following my previous posts discussing navigation, I just want to demonstrate how to abstract navigation away from both the page and the viewmodel – this allows for more independent testing of viewmodels as there’s no interdependency between viewmodels. Here’s the basic process:

  • Add a new page, SecondPage, that we’re going to navigate to
  • Add a corresponding viewmodel, SecondViewModel, and property, Second, on the ViewModelLocator
  • Update SecondPage to set the DataContext to be bound to the Second property on the ViewModelLocator
  • Add a Button to MainPage that invokes a method, Next, on the MainViewModel
  • Add an event, Complete, to MainViewModel, and raise it from the Next method.
  • Add a mapping to the App.xaml.cs that navigates to SecondPage when the Complete method is raised.

And here’s the code. I’m not going to show you the initial SecondPage as it’s just generated from the template and you’ll see it later anyhow. Instead, we’ll jump to the SecondViewModel (if you’re following along you still need to add the SecondPage to the DoUnoMvvm.Shared project in the Pages folder).

public class SecondViewModel
{
    public string ProgressText => "Now you know how to navigate....";
}

Add the Second property to the ViewModelLocator

public class ViewModelLocator
{
    public MainViewModel Main => new MainViewModel();
    public SecondViewModel Second => new SecondViewModel();
}

Now back to the SecondPage and I have set the DataContext and bound the TextBlock.

<Page
    x:Class="DoUnoMvvm.Shared.Pages.SecondPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:DoUnoMvvm.Shared.Pages"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    DataContext="{Binding Second, Source={StaticResource ViewModelLocator}}"
    Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
  <Grid>
    <TextBlock Text="{Binding ProgressText}" />
  </Grid>
</Page>

Now a Button to invoke the transition from MainPage to SecondPage

<Page
    x:Class="DoUnoMvvm.MainPage" ...
    DataContext="{Binding Main, Source={StaticResource ViewModelLocator}}" >
  <StackPanel>
    <TextBlock Text="{Binding WelcomeText}" Margin="20" FontSize="30" />
    <Button Content="Go to Second Page" Click="GoNextClick" />
  </StackPanel>
</Page>

Here we’re simply using a code behind but you could easily use a command. Unfortunately x:Bind doesn’t appear to be working with Uno yet, so you can’t simply bind the Click method to a method on the viewmodel.

public void GoNextClick(object sender, RoutedEventArgs e)
{
    (DataContext as MainViewModel)?.Next();
}

The Next method simply raises the Complete event

public class MainViewModel
{
    public event EventHandler Complete;

    public string WelcomeText => "How well do Uno Mvvm?";

    public void Next()
    {
        Complete?.Invoke(this, EventArgs.Empty);
    }
}

The final step is to add the mapping to App.xaml.cs to define what happens when the Complete event is triggered on the MainViewModel. Add the following property and method to App.xaml.cs, and update the App class to implement the IApplicationWithMapping interface (which comes from the BuildIt.General.Uno library that you should have added earlier)

public IDictionary<Type, IEventMap[]> Maps { get; } = new Dictionary<Type, IEventMap[]>();
private void MapPageTransitions()
{
    Maps.For<MainViewModel>()
        .Do(new EventHandler((s, args) => (Windows.UI.Xaml.Window.Current.Content as Frame)?.Navigate(typeof(SecondPage))))
        .When((vm, a) => vm.Complete += a, (vm, a) => vm.Complete -= a);
}

Invoke the MapPageTransitions method immediately after the Window.Current.Content property has been set equal to a new Frame. In order for the events to get correctly wired up you also need to update both MainPage and SecondPage to inherit from the MappingBasePage class.

Now when you run the application, MainPage will appear with a Button that you can click to navigate to the SecondPage.

Uno How to Mvvm!

You might be thinking…. you’ve just shown me how to do a bunch of UWP code… and that is EXACTLY the point. If you switch to the Droid or iOS or Wasm target, you can run the same application on each of those platforms with NO further code changes required. The Uno platform is about leveraging the skills you already have as a UWP (or as a Xamarin.Forms) developer, allowing you to build rich, high-quality applications for iOS, Android and Web.

Link to the source code

Creating a Flutter App for Web

I’ve covered this topic previously in my post Create, Build and Publish a Flutter Web App but things have changed a little now as web support has been merged, making it easier to build a single application that runs on iOS, Android and Web. To find out more you can check out the Flutter docs for web and Building a web application with Flutter. In this post, I’m going to cover my experience and talk a little about the debugging experience in Visual Studio Code.

With the release of Flutter 1.9 I took a read through the announcement from the Google Developer Days in China and I was initially a little thrown because it indicated that Flutter’s web support has been integrated into the main Flutter repository. I assumed this meant that if I upgraded to 1.9 I would be able to immediately create a Flutter app that targets the mobile and web platforms.

Switching Channel

After upgrading to 1.9 and trying to create a new Flutter project, I quickly realised that I had misunderstood the announcement and that I still needed to switch to a different channel in order to get the integrated experience for Flutter web. So I ran the following command

flutter channel master

After switching to the master channel, I ran:

flutter doctor
flutter upgrade
flutter config --enable-web

New Flutter Project With Web

Once you’ve run these methods, the next thing to do is to create a new Flutter project in Visual Studio code. Press Ctrl+Shift+P to bring up the Command Palette, type Flutter and click Flutter: New Project. After your project is created you’ll notice the addition of a web folder.

Launching Flutter for Web

When you go to launch your Flutter app from within Visual Studio Code you’ll need to decide whether to launch on iOS or Andoird, or the newly supported web. In the lower right corner of Visual Studio Code you can see the current debugging target – in this case it’s already set to use Chrome.

If you tap the debugging target you’ll be prompted to pick an alternative debugging target.

After setting the debugging target, if you press F5 your Flutter app will launch on the appropriate device or emulator. Note that since web support is in preview, there’s no step-through debugging support; you can set breakpoints etc, you just can’t step through the code and inspect variables.

Summary

This was just a short post to draw your attention to the need to switch to the master branch in order to try out the Flutter for web support.

MVVM Navigation with Xamarin.Forms Shell – Part II

Following my previous post on Mvvm Navigation with Xmarin.Forms Shell there were a few things that I felt I hadn’t addressed adequately.

Loading Data on Appearing

The first thing is how to load data when navigating to a page, and perhaps to do something when the user navigates away from the page. Since it’s the responsibility of the viewmodel to provide the data (i.e. via data binding), we have to invoke a method on the corresponding viewmodel when arriving at a page. In Xamarin.Forms the navigation to/from a page invokes the OnAppearing and OnDisappearing methods, which we can use to request that the viewmodel loads data.

The simplest approach is that for each page that needs to load data, the developer can override the OnAppearing method and simply call a method, for example LoadData, on the corresponding viewmodel. Since most pages are likely to have to load some data, this will quickly become a bit of a drag and something we can easily optimise. We’ll introduce an interface, INavigationViewModel, that when implemented by a viewmodel will define methods OnAppearing and OnLeaving. Then in our BasePage (which we introduced in the previous post) we simply need to check to see whether a viewmodel implements the interface before invoking the appropriate method.

public class BasePage : ContentPage
{
    protected override void OnAppearing()
    {
        base.OnAppearing();

        var vm = this.BindingContext as BaseViewModel;
        if (vm!=null && AppShell.Maps.TryGetValue(vm.GetType(), out var maps))
        {
            foreach (var map in maps)
            {
                map.Wire(vm);
            }
        }

        (vm as INavigationViewModel)?.OnAppearing();
    }

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

        var vm = this.BindingContext as BaseViewModel;
        if (vm!=null && AppShell.Maps.TryGetValue(vm.GetType(), out var maps))
        {
            foreach (var map in maps)
            {
                map.Unwire(vm);
            }
        }

        (vm as INavigationViewModel)?.OnLeaving();
    }
}

Note: If you’re going to be adapting some of this code for your project, you might want to consider making OnAppearing/OnLeaving return a Task that can be awaited.

Passing Parameter

The next point that we need to cover is how to pass a parameter from one page to the next. In our example, we have a list of items and when the user taps on an item the app navigates to the ItemDetailPage to view the details of the item. This requires some information about the selected item to be passed to the ItemDetailPage.

I’ve seen all sorts of mechanisms for passing data between pages across various application platforms. Some platforms only allow a simple query string to be passed as part of navigating between pages, whilst others allow you to pass entire objects. In the case of Xamarin.Forms the default navigation pattern doesn’t provide for a mechanism to pass data. However, because you create the instance of the new page before navigating to it, there’s a perfect opportunity to pass data from the existing page to the new page.

The following code is adapted from the code in the previous post to illustrate wiring up navigation to the ItemDetailPage based on the SelectedItemChanged event on the ItemsViewModel. Note that the selected item, passed into the event handler as the variable args, is set on the Item property of the ItemDetailViewModel.

// When the SelectedItemChanged event is raised on the ItemsViewModel
// navigate to the ItemDetailPage, passing in a new ItemDetailViewModel
// with the selected item
Maps.For<ItemsViewModel>()
    .Do(new EventHandler<Item>(async (s, args) =>
    {
        if (args == null)
        {
            return;
        }
        var page = new ItemDetailPage();
        if (page.BindingContext is ItemDetailViewModel vm)
        {
            vm.Item = args;
        }
        await Navigation.PushAsync(page);
    }))
    .When((vm, a) => vm.SelectedItemChanged += a, (vm, a) => vm.SelectedItemChanged -= a);

Note: Setting a property on the viewmodel will occur before the OnAppearing method is invoked (see previous discussion regarding loading data) which means the viewmodel can make use of whatever data you pass in when loading data. In this case we could have simply passed in the Id of the item we want to display and have the viewmodel load the rest of the data related to that item.

Returning a Parameter

One thing I’ve seen in a number of MVVM frameworks is the ability to navigate to a new page with the expectation that the page will return data at some point in the future. Whilst there are cases where this is convenient (eg prompting the user for some data) this pattern introduces a very heavy dependency between the lifecycle of two pages and their corresponding viewmodels.

An alternative is to assume that each page/viewmodel is independent and that when you arrive at a page any data that is displayed on the page should be refreshed. Of course, if you’ve got a long list of items that the user has scrolled mid-way down and you reload the list, it’s going to return to the top of the list, making the user experience quite nasty. Furthermore, if the list of items is coming from a service, you don’t want to be reloading that data every time the user goes back and forth to a details page.

Most of the above issues can be handled with appropriate caching of data in a service or manager class. If the user navigates to an item, makes changes and saves that item, the cached data in the service would be updated (along with any call required to any backend services). When the user returns to the list of items, the service would simply return the latest cached data. This addresses the latency issue of having to fetch data from a backend service but how do we address the scroll position issue?

One way is to only update the items in the list that have changed (ie catering for add, edit, delete of items). However, having to write this code for every page is again tiresome. In the past, I investigated a possible workaround for this issue when I discussed immutable data – check out posts I, II and III on working with immutable data

Summary

Again, the disclaimer here is that this isn’t a library that you can just drop in and use in your application. However, feel free to take/leave what code you find useful.

Get the latest source code

Flutter: Text Widget

In this post we’re going to look at the Text widget in Flutter and some of the options you can tweak when displaying text within your app. If you’re after a more detailed discussion of strings, characters and how they’re displayed, you should check out the post, Mastering Styled Text in Flutter. To get into it, we’re going to start off with a new Flutter project, which already displays text to indicate how many times the button has been pressed. The text is broken into two Text widgets to allow for the styling of the actual counter value to be different from the preceding text.

Text Constructor

This code snippet shows us a couple of things: firstly, that the first parameter of the Text constructor is the string to be displayed; secondly, we can override, or set, the style of the text using the style parameter. This, of course, prompts the question as to what parameters are there on the Text widget and what do they mean.

Let’s take a look at the Text constructor – select the Text widget and press F12 in Visual Studio Code to be taken to the actual code for the Text class and its constructor:

class Text extends StatelessWidget {
  const Text(
    this.data, {
    Key key,
    this.style,
    this.strutStyle,
    this.textAlign,
    this.textDirection,
    this.locale,
    this.softWrap,
    this.overflow,
    this.textScaleFactor,
    this.maxLines,
    this.semanticsLabel,
    this.textWidthBasis,
  })

As we can see from the constructor definition, there is a single data parameter, followed by a list of optional parameters. Let’s step through these parameters and take a look at what they do.

Text.style

The first optional parameter of the Text widget is “style” which is of type TextStyle. As you’d imagine the TextStyle class can be used to specify the foreground and background color, the font size and weight, letter and word spacing, locale, shadows and much more.

What’s interesting is that the first Text widget shown above, we didn’t specify a TextStyle, and yet the text was still displayed on the screen with a basic style applied. This is because when the style property isn’t set, the Text widget will search the widget tree looking for style information to use. To demonstrate this, try passing a Text widget into the runApp method.

void main() => runApp(Text("test"));

This will actually fail to run stating that RichText widgets require a Directionality widget ancestor. We can fix this easily by wrapping the Text widget with a Directionality widget as follows:

void main() => runApp(Directionality(textDirection: TextDirection.ltr, child: Text("test")));

After doing this, what we’re left with on-screen is a very unstyled piece of text situated in the top left corner of the screen.

Returning now to our application, the question becomes, where does the default styling for Text come from. The answer to this question can be found in the documentation for the style property (press F12 in VS Code to navigate to the property definition where you can find the relevant documentation). The documentation states that the style property will be merged with the style associated with the closest enclosing DefaultTextStyle (assuming the inherit property on the style is set to true). What this means is that the Text widget will traverse up the widget hierarchy looking for a DefaultTextStyle widget on which to base the style of the text on.

So the next question you’re probably going to ask is where is the DefaultTextStyle widget being added to the hierarchy because it doesn’t appear anywhere in the app that was generated when we created the new project. Well, it might surprise you to know that the DefaultTextStyle widget appears at least twice, being added by both the MaterialApp and Scaffold widgets respectively. The following diagram illustrates just part of the widget hierarchy that shows the DefaultTextStyle appearing below the AnimatedDefaultTextStyle node.

Coming back to our Text widget, if we specify a value for the style property, it will be merged with the style of the nearest DefaultTextStyle widget. For example, let’s change the colour of the text to purple.

Text(
  'You have pushed the button this many times:',
  style: TextStyle(color: Colors.purple),
),

Alternatively, as we saw in the second Text widget in the initial example, the style of the Text widget can be set based on the current Theme. The Theme defines a number of different text styles that can be used – check out the Flutter documentation for more information on the individual text styles. In the following code, the display1 style is applied to the Text widget.

Text(
  '$_counter',
  style: Theme.of(context).textTheme.display1,
),

Sometime you’ll want to use one of the theme text styles but you want to adjust one or more attributes. For this, you can use the apply method as in the following example that sets the colour of the text to purple.

Text(
  '$_counter',
  style: Theme.of(context).textTheme.display1.apply(color: Colors.purple),
),

Since we’ve now set the colour of both Text widgets to purple, it would be good to be able to extract this so that it’s only applied once and will affect both Text widgets. This comes back to what I was saying about the DefaultTextStyle widget and how the style property merges with the nearest DefaultTextStyle widget – in order to set an attribute that should apply to all Text widget, we just need to add a DefaultTextStyle widget to the hierarchy, setting the appropriate attribute.

child: DefaultTextStyle(
  style: TextStyle(color: Colors.purple),
  child: Column(
    mainAxisAlignment: MainAxisAlignment.center,
    children: [
      Text(
        'You have pushed the button this many times:',
      ),
      Text('$_counter', style: Theme.of(context).textTheme.display1),
    ],
  ),
),

Note that the DefaultTextStyle widget also merges its style with the style of the nearest parent DefaultTextStyle widget.

Text.strutStyle    

Setting the strutStyle property gives you the ability to fine-tune the separation between rows of text. For example, if you have a number of Text widgets that have differing font style and sizes, you can specify the strutStyle to ensure the same spacing between each row.

Text(
  'This is a very long piece of text designed to wrap over multiple lines. This is a very long piece of text designed to wrap over multiple lines.',
  style: TextStyle(fontSize: 12),
  strutStyle: StrutStyle(fontSize: 13),
),
Text(
  'This is a very long piece of text designed to wrap over multiple lines. This is a very long piece of text designed to wrap over multiple lines.',
  style: TextStyle(fontSize: 14),
  strutStyle: StrutStyle(fontSize: 13),
),
Text(
  'This is a very long piece of text designed to wrap over multiple lines. This is a very long piece of text designed to wrap over multiple lines.',
  style: TextStyle(fontSize: 12),
  strutStyle: StrutStyle(fontSize: 13),
),

As you can see from the following image, despite the text in the middle section being a slightly larger font, the line separation is uniform across all lines of text.

Further information on using the strutStyle property can be found at the StrutStyle documentation

Text.textAlign and Text.textDirection

I’ve grouped the textAlign and textDirection properties together as they are related as the textDirection determines how some textAlign values control the layout of text in the Text widget. In the following example there are five Text widgets, with different combination of textAlign and textDirection property values.

Text(
  'JUSTIFIED - This is a very long piece of text designed to wrap over multiple lines. This is a very long piece of text designed to wrap over multiple lines.',
  textAlign: TextAlign.justify,
),
Text(
  'LEFT (LTR) - This is a very long piece of text designed to wrap over multiple lines. This is a very long piece of text designed to wrap over multiple lines.',
  textAlign: TextAlign.left,
  textDirection: TextDirection.ltr,
),
Text(
  'LEFT (RTL) - This is a very long piece of text designed to wrap over multiple lines. This is a very long piece of text designed to wrap over multiple lines.',
  textAlign: TextAlign.left,
  textDirection: TextDirection.rtl,
),
Text(
  'START (LTR) - This is a very long piece of text designed to wrap over multiple lines. This is a very long piece of text designed to wrap over multiple lines.',
  textAlign: TextAlign.start,
  textDirection: TextDirection.ltr,
),
Text(
  'START (RTL) - This is a very long piece of text designed to wrap over multiple lines. This is a very long piece of text designed to wrap over multiple lines.',
  textAlign: TextAlign.start,
  textDirection: TextDirection.rtl,
),

When we run this code we can see that the textAlign property does what you’d expect it to – the text is justified for the TextAlign.justify value and aligned left for TextAlign.left. What’s interesting is that TextAlign also includes values start and end which are important if you’re considering supporting RTL languages within your application. As you can see from the following image with textAlign set to TextAlign.start, if we adjust the textDirection between LTR and RTL we can see that the text changes from left aligned to right aligned.

Text.locale

One of the most common reason for explicitly setting the locale for a Text widget is to adjust the text that is being rendered. Adjusting the locale will change the way certain unicode characters are displayed.

Text.softWrap

Setting the softWrapp property to false will disable wrapping, causing the text to be truncated by the right edge of the parent container.

Text.overflow

The overflow property controls what happens when there is more text than will fit in the space available. For example you can use the predefined value TextOverflow.ellipsis for Flutter to insert … when there isn’t sufficient space. Note that ellipsis will disable wrapping, regardless of how much space there is available (see maxLines discussion below).

Another possible overflow value is TextOverflow.fade

Container(
  height: 30,
  child: Text(
    'This is a very long piece of text designed verylongwordwithnospaces to wrap over multiple lines. This is a very long piece of text designed to wrap over multiple lines.',
    overflow: TextOverflow.fade,
  ),
),

Using TextOverflow.fade in conjunction with a fixed height on the parent Container rejusts in the following effect where the first Test widget fades out before the second Test widget.

Text.textScaleFactor

The textScaleFactor can be set to apply an arbitrary scaling to the text being displayed by the Text widget. Note that this will override any textScaleFactor applied by the current MediaQuery, so may result in incorrect layouts on devices where the textScaleFactor isn’t null or 1.0.

Text.maxLines

Where the text being displayed stretches over multiple lines, the maxLines property defines the maximum number of lines that will be displayed.

When the overflow property is set to TextOverflow.ellipse the maxLines property can be set to increase the number of lines of text that will be displayed before the ellipses are appended.

Text.semanticsLabel    

You can use the semanticsLabel property to improve the way that screen reader and assistive technologies work with your application. Check out this post on Semantics for further information.

Text.textWidthBasis

The textWidthBasis property can control how the width of the Text widget is defined. More information is available via the documentation.

Summary

In this post, I’ve walked through the use of the Text widget. I would highly recommend pressing F12 on a Text widget and go explore the source code. It’s worth to note that other classes, such as RightText and TextSpan, that exist – I’ll leave it to the reader to drill into these and understand how they’re related.

MVVM Navigation with Xamarin.Forms Shell

One of the topics that I find a bit contentious amongst app developers is how navigation is handled, specifically when it comes to MVVM. Should navigation be done in the code behind of the page? or in the ViewModel? and should navigation be based on a path/URL or based on what ViewModel you want to go to. Regardless of where you stand on this discussion, one thing is very clear – in your application, you need to set a standard mechanism for doing this. Prism and MvvmCross are quite prescriptive when it comes to navigation, making them great starting points for your application; Xamarin.Forms Shell attempts to provide some constructs (eg routes) but fails to deliver an entire strategy for handling navigation between all the pages within your application.

Cross-platform Navigation

Back when I started doing Windows Phone development navigation between pages was simply done by calling Navigate on the Frame of the application. iOS and Android had their own way of doing navigation, which was fine back then because cross-platform wasn’t a thing and each platform was typically built by itself. However, with the introduction of Xamarin (back then monoTouch and monoDroid) it was possible to reuse our C# code and so more thought was applied to how we could reuse not just business logic but also our UI logic. It was also about this time that the MVVM pattern was really catching on and there was time being invested by the community into establishing frameworks for associating viewmodels with views/pages.

I’ve been a big advocate for MVVM frameworks because I see them providing the much-needed pattern for instantiating viewmodels, handling dependency injection, and a consistent pattern for navigation that can be invoked from the viewmodel. I believe that this allows us to build viewmodels that are testable and aren’t interconnected with the UI due to the separation imposed by data binding. If you look back at the origins of MvvmCross you can see that it was developed with this in mind and supported iOS, Android and Windows without any additional UI frameworks (such as Xamarin.Forms). To this day, if you want to develop using Xamarin.iOS and Xamarin.Droid and you want to reuse your business logic, MvvmCross has you covered.

ViewModel to ViewModel Navigation

Coming back to the discussion around navigation, one of the concepts added to MvvmCross was the idea of viewmodel to viewmodel navigation. This meant you could navigate between views/pages by simply calling Navigate and providing the viewmodel you wanted to go to. MvvmCross would determine what view/page this corresponded to and would invoke the platform-specific method for navigation.

The abstraction of view/page navigation was one of the features that Xamarin.Forms brought to the table. In addition to having a common model for defining page layouts (ie XAML), there was now a single model for navigating between pages. Does this negate the need for say MvvmCross? Not at all because whilst Xamarin.Forms deals with the platform navigation idiosyncrasies, it doesn’t provide the abstraction that allows for testing. Whether you’re using Prism or MvvmCross, the INavigationService, gives you the ability to mock out navigation for testing purposes.

ViewModel Dependency Injection

We’re starting to get somewhere now – our navigation pattern needs to be platform agnostic (which we get from Xamarin.Forms) and needs to be testable. The last point I’ll make on MVVM frameworks is that both provide sophisticated dependency injection support. In order for our viewmodels to populate themselves with data, they’ll typically rely on other classes (eg service or data manager classes). Again, in order to make things testable, these services implement interfaces, and our viewmodels only depend (often as part of the constructor) on the interfaces. This requires some form of dependency injection in order to construct each viewmodel.

In summary, our requirements for navigation are:

  • Navigation needs to be platform agnostic
  • Viewmodels need to be testable
  • Viewmodels will have dependencies

This post isn’t here to talk up any particular framework; in fact, quite the reverse. We’re going to go framework-free and see where this leads us. One of the motivations here is the lack of support offered by MVVM frameworks for Xamarin.Forms Shell, which is still hampered by the Xamarin.Forms team (see this open issue). Given the above requirements for navigation, let’s see what we can come up with.

MVVM Basics with Xamarin.Forms Shell

I’m going to back-track to when I first used MVVM – Coming from building apps for PocketPC/Windows Mobile I viewed reflection as a luxury that desktop developers could afford, so a lot of what I built used concrete types with minimal code to wire things up. We’re going to start there and see where it leads us.

Note: This is a thought experiment ONLY – for commercial applications I would currently still be advocating the use of Prism or MvvmCross (rather than Shell).

ViewModelLocator

I’m going to start with a brand new Xamarin.Forms Shell application created in Visual Studio 2019 (16.3 Preview 2) using the Shell solution template. After creating the project the first thing to do is to check it runs; followed by upgrading NuGet packages; and then checking it still runs – there’s nothing like wasting hours wondering why your code doesn’t work, only to find out that a NuGet update broke something.

If we take a look at the AboutPage.xaml that’s created by the template we can see that the BindingContext is set to a new instance of the AboutViewModel. This addresses the basics of MVVM, where you have a viewmodel that is data bound to the page, thus making the viewmodel testable. However, it doesn’t solve the issue of how dependencies can be injected into the viewmodel.

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             x:Class="FormsNaviation.Views.AboutPage"
             xmlns:vm="clr-namespace:FormsNaviation.ViewModels"
             Title="{Binding Title}">
    <ContentPage.BindingContext>
        <vm:AboutViewModel />
    </ContentPage.BindingContext>

To give us more control over how the viewmodels are created, we’re going to add in a class that will be responsible for constructing the viewmodels. The ViewModelLocator class will not only be responsible for constructing viewmodels, it will also be responsible for supplying any dependencies the viewmodels may rely on. Our first pass on the ViewModelLocator will be super basic.

public class ViewModelLocator
{
    public AboutViewModel About => new AboutViewModel();
}

To create an instance of the ViewModelLocator that can be referenced throughout the XAML of our application, we can add the instance to the App.xaml file as an Application resource.

<?xml version="1.0" encoding="utf-8" ?>
<Application xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:viewmodels="clr-namespace:FormsNaviation.ViewModels"
             x:Class="FormsNaviation.App">
    <Application.Resources>
        <viewmodels:ViewModelLocator x:Key="ViewModelLocator"/>
    </Application.Resources>
</Application>

And our AboutPage XAML is updated to reference the instance of the ViewModelLocator as a StaticResource.

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
       x:Class="FormsNaviation.Views.AboutPage"
       xmlns:vm="clr-namespace:FormsNaviation.ViewModels"
       BindingContext="{Binding About, Source={StaticResource ViewModelLocator}}"
       x:DataType="vm:AboutViewModel" >

The AboutViewModel is returned using the About property exposed on the ViewModelLocator. In order to create viewmodels for other pages, you simply need to create a new property that returns the viewmodel (or it can return they are around the village.

At this stage, we haven’t really added much value over simply creating new instances of the viewmodel in the XAML on each page. However, as we get into dependencies, you’ll understand why it’s important to have at least some helper items that can assist with the development.

Adding Dependencies

As I mentioned, the use of a ViewModelLocator to create the instance of the viewmodel means that there’s a very clear point during the lifecycle to add and remove dependencies. The following locator code illustrates adding a dependency for the ILoginService, which in turn requires an implementation of the IAuthenticationService. I acknowledge that this is a very contrived example, especially since the verification check is just to compare username/password (clearly not a secure approach), but hopefully, it illustrates how dependencies can be provided and that they can be Lazy created at the point they’re first required.

public class ViewModelLocator
{
    private readonly Lazy<IAuthenticationService> authenticationService;
    private readonly Lazy<ILoginService> loginService;

    public ViewModelLocator()
    {
        authenticationService = new Lazy<IAuthenticationService>(() => new AuthenticationService());
        loginService = new Lazy<ILoginService>(() => new LoginService(authenticationService.Value));
    }

    public AboutViewModel About => new AboutViewModel(loginService.Value);

    public NewItemViewModel NewItem => new NewItemViewModel();
}

public interface ILoginService
{
    bool IsLoggedIn { get; }

    bool Login(string username, string password);
}
public class LoginService : ILoginService
{
    private bool isLoggedIn;
    public bool IsLoggedIn => isLoggedIn;

    private readonly IAuthenticationService authenticationService;
    public LoginService(IAuthenticationService authentication)
    {
        authenticationService = authentication;
    }

    public bool Login(string username, string password)
    {
        isLoggedIn = authenticationService.VerifyCredentials(username, password);
        return IsLoggedIn;
    }
}
public interface IAuthenticationService
{
    bool VerifyCredentials(string username, string password);
}
public class AuthenticationService : IAuthenticationService
{
    public bool VerifyCredentials(string username, string password)
    {
        return !string.IsNullOrWhiteSpace(username) && username == password;
    }
}

Going back to our original requirements, we now have the ability to provision viewmodels in a way that they are testable (i.e. they have no dependencies that can’t be mocked as part of testing the viewmodel) and that we can inject dependencies. The one thing we haven’t dealt with is the need to have a navigation pattern that is platform agnostic.

Event-Based Navigation

As we observed earlier Xamarin.Forms already provides the abstraction to for platform-agnostic navigation using the INavigation instance. So the issue we’re really left with is how to structure a navigation pattern that is consistent and easy to follow.

MvvmCross provides this in the form of ViewModel-to-ViewModel navigation but this introduces a dependency between viewmodels (and to the INavigationService), which actually makes it harder to test and introduces an interdependency between viewmodels, even if it is a loose dependency via navigation.

Rather than making individual viewmodels aware of navigation, what if we simply stipulate that the viewmodel should raise an event when it is “done”. The definition of done might be that the user has clicked the Ok or Cancel button (which would trigger a command on the viewmodel), or it could be that a new item has successfully been saved. The point is that the viewmodel raises one or more events when it wants to notify the application that the viewmodel has been completed, or “done”. At this point the application needs to make a decision on where to send the user (i.e. which page to navigate to).

What we want to avoid is the viewmodel raising an event that is captured by the corresponding page, in order to then navigate to the next page (or to go back to previous page in the case of a cancel style event). Whilst this keeps the viewmodel from taking on additional dependencies, this would result in navigation code being littered throughout the application, making it hard to find, update and maintain.

An alternative would be to have a central mechanism where navigation, based on viewmodel events, is declared. Remembering that this is a thought experiment only, and that this code would require significant refinement if it was to be used in production.

ViewModel Events

When a particular viewmodel raises an event, we want to be able to invoke an Action that will define how the application should respond. In concrete terms, we’re going to add a NewItemViewModel to the application generated from the Shell template (for some reason the NewItemPage doesn’t have its own viewmodel in the template). The NewItemViewModel will have a Cancel event which will be triggered if the user wants to abandon the process of creating a new item.

public class NewItemViewModel : BaseViewModel
{
    public event EventHandler<Item> ItemSaved;
    public event EventHandler Cancel;

    private Item item;
    public Item Item { get => item; set => SetProperty(ref item, value); }

    public ICommand SaveItemCommand { get; }
    public ICommand CancelCommand { get; }

    public NewItemViewModel()
    {
        SaveItemCommand = new Command(() =>
            {
                MessagingCenter.Send(this, "AddItem", Item);
                ItemSaved?.Invoke(this, Item);
            });
        CancelCommand = new Command(() =>
            {
                Cancel?.Invoke(this, EventArgs.Empty);
            });
    }
}

The application needs to intercept the Cancel event from the NewItemViewModel and navigate the application back to the previous page in the application. Normally an event handler would be wired up like the following code example, which you could expect to see in the OnAppearing method of a page (and then hopefully unwired in the OnDisappearing method).

vm.Cancel += (s, args) => Navigation.PopModalAsync();

However, we need to be able to abstract this code in a way that we can define what Action is invoked when a particular event is raised.

AppShell Event Maps

Let me jump forward a bit to where this is going to end up, so that you can see how we can declare the mapping between a viewmodel event and the Action that should be invoked.

public partial class AppShell : Xamarin.Forms.Shell
{
    public static IDictionary<Type, IEventMap[]> Maps { get; } = new Dictionary<Type, IEventMap[]>();

    public AppShell()
    {
        InitializeComponent();

        // When the ItemSaved event is raised on the NewItemViewModel
        // pop the current page off the navigation stack
        Maps.For<NewItemViewModel>()
            .Do(new EventHandler<Item>((s, args) => Navigation.PopModalAsync()))
            .When((vm, a) => vm.ItemSaved += a, (vm, a) => vm.ItemSaved -= a);

        // When the Cancel event is raised on the NewItemViewModel
        // pop the current page off the navigation stack
        Maps.For<NewItemViewModel>()
            .Do(new EventHandler((s, args) => Navigation.PopModalAsync()))
            .When((vm, a) => vm.Cancel += a, (vm, a) => vm.Cancel -= a);

        // When the SelectedItemChanged event is raised on the ItemsViewModel
        // navigate to the ItemDetailPage, passing in a new ItemDetailViewModel
        // with the selected item
        Maps.For<ItemsViewModel>()
            .Do(new EventHandler<Item>(async (s, args) =>
            {
                if (args == null)
                {
                    return;
                }
                await Navigation.PushAsync(new ItemDetailPage(new ItemDetailViewModel(args)));
            }))
            .When((vm, a) => vm.SelectedItemChanged += a, (vm, a) => vm.SelectedItemChanged -= a);
    }
}

Here’s what this code does:

  • I’ve define a dictionary of event to action mappings (i.e. Maps). We’ve declared as a static to make it easy to access throughout the app (disclaimer: this is a thought experiment!!!)
  • I’m using a fluent syntax, which I’ll include the code for later in this post, to link the type of viewmodel (For), the Action to be invoked (Do) and the Event (When).
  • The When method requires Actions for both wiring and unwiring the event handler due to limitations on C# events.

Connecting Event Mappings

Now that we have these mappings, we need to use them in the application itself. The mappings are used in the OnAppearing and OnDisappearing methods to wire and unwire the Action to the viewmodel events. To do this the pages in our application need to inherit from a base page that has the appropriate logic.

public class BasePage : ContentPage
{
    protected override void OnAppearing()
    {
        base.OnAppearing();

        var vm = this.BindingContext as BaseViewModel;
        if (AppShell.Maps.TryGetValue(vm.GetType(), out IEventMap[] maps))
        {
            foreach (var map in maps)
            {
                map.Wire(vm);
            }
        }
    }

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

        var vm = this.BindingContext as BaseViewModel;
        if (AppShell.Maps.TryGetValue(vm.GetType(), out IEventMap[] maps))
        {
            foreach (var map in maps)
            {
                map.Unwire(vm);
            }
        }
    }
}

Adding a Mapping

So far I’ve shown you what an existing mapping looks like and how it connects viewmodel events with an action, typically a navigation of some description. Let me walk you through adding another, so you can see how it works. We’ll add the ability to delete an item to the ItemDetailViewModel. We’ll add a command, DeleteItemCommand, which will be data bound to a button on the ItemDetailsPage. When invoked the DeleteItemCommand will issue a message, specifying the item to delete. After sending a message, the ItemDetailViewModel raises the ItemDeleted event.

public class ItemDetailViewModel : BaseViewModel
{
    public event EventHandler<Item> ItemDeleted;
    public ICommand DeleteItemCommand { get; }

    public Item Item { get; set; }
    public ItemDetailViewModel(Item item = null)
    {
        Title = item?.Text;
        Item = item;

        DeleteItemCommand = new Command(() =>
        {
            MessagingCenter.Send(this, "DeleteItem", Item);
            ItemDeleted?.Invoke(this, Item);
        });
    }
}

When the ItemDeleted event is raised, that’s the indication that this viewmodel is done and that the application should return to the previous page. For this, we just need to add a mapping to the AppShell class.

Maps.For<ItemDetailViewModel>()
    .Do(new EventHandler<Item>((s, args) => Navigation.PopAsync()))
    .When((vm, a) => vm.ItemDeleted += a, (vm, a) => vm.ItemDeleted -= a);

And that’s all there is to it – we can simply add events to the viewmodels; and maps to the AppShell.

Inner Working

Now that I’ve shown you how it works, let’s drill into what makes it work, starting with the EventMap class, which will be used to map an action to a viewmodel event.

public interface IEventMap
{
    void Wire(object viewModel);
    void Unwire(object viewModel);
}

public class EventMap<TViewModel, TDelegate>: IEventMap
{
    public TDelegate Action { get; set; }
    public Action<TViewModel, TDelegate> Arrive { get; set; }
    public Action<TViewModel, TDelegate> Leave { get; set; }

    public EventMap(
            TDelegate action, 
            Action<TViewModel, TDelegate> arrive, 
            Action<TViewModel, TDelegate> leave)
    {
        Action = action;
        Arrive = arrive;
        Leave = leave;
    }

    public void Wire(object viewModel)
    {
        Arrive((TViewModel)viewModel, Action);
    }

    public void Unwire(object viewModel)
    {
        Leave((TViewModel)viewModel, Action);
    }
}

The EventMap class is relatively simple and you can see that the Wire and Unwire methods invoke the corresponding Arrive and Leave methods. And yes, there is some assumptions made here without error handling around the casting of the videwModel object to the appropriate type.

The last bit of magic is the way that the mappings are created using a fluent style of coding. This is actually just a bunch of extension methods with a couple of builder classes added for good measure

public static class EventMapHelpers
{
    public static EventMapBuilderViewModel<TViewModel> For<TViewModel>(this IDictionary<Type, IEventMap[]> maps)
    {
        return new EventMapBuilderViewModel<TViewModel>() { Maps = maps };
    }

    public static EventMapBuilderViewModelEvent<TViewModel, TDelegate>
        Do<TViewModel, TDelegate>(this EventMapBuilderViewModel<TViewModel> viewModelBuilder, TDelegate viewModelEvent)
    {
        return viewModelBuilder.BuildWithEvent(viewModelEvent);
    }
    public static void When<TViewModel, TDelegate>(this EventMapBuilderViewModelEvent<TViewModel, TDelegate> builder, Action<TViewModel, TDelegate> arrive, Action<TViewModel, TDelegate> leave)
    {
        var key = typeof(TViewModel);
        var map = new EventMap<TViewModel, TDelegate>(builder.ViewModelAction, arrive, leave);
        if (builder.Maps.TryGetValue(key, out IEventMap[] existing))
        {
            builder.Maps[key] = existing.Union(new[] { map }).ToArray();
            return;
        }
        builder.Maps[key] = new[] { map };
    }
    public class EventMapBuilderViewModelEvent<TViewModel, TDelegate> :
        EventMapBuilderViewModel<TViewModel>
    {
        public TDelegate ViewModelAction { get; set; }
    }
    public class EventMapBuilderViewModel<TViewModel>
    {
        public IDictionary<Type, IEventMap[]> Maps { get; set; }

        public EventMapBuilderViewModelEvent<TViewModel, TDelegate> BuildWithEvent<TDelegate>(TDelegate viewModelAction)
        {
            return new EventMapBuilderViewModelEvent<TViewModel, TDelegate> { ViewModelAction = viewModelAction, Maps = Maps };
        }
    }
}

MVVM Navigation Summary

Whilst only a thought experiment, I think I’ve shown in this article the importance of having a structured approach to MVVM. Whether you choose to go with Prism or MvvmCross, or perhaps even choose your own pattern, the reality is that you need some structure to your code.

For anyone interested, you can grab my source code from here

Installing Uno as a PWA with WebAssembly

One of the things that I don’t like about the web is that I have to access everything via the browser. Whilst favourites and bookmarks are all well and good, one of the nice things about apps is that I can do things like pin to start/taskbar (Windows) or add to homescreen (Android). Sure I can pin a website, but this leads me to my next point – it still feels like the web, rather than an app experience. There’s still the browser chrome that sits around the edge and it doesn’t feel the same as being in an app. In this post we’re going to look at one aspect of Progressive Web Applications (PWA) which is the ability to “install” the application in order to address some of my feelings towards web applications.

Twitter PWA

Let me start by walking through one of the better PWA experiences offered by Twitter. Over the last couple of months Twitter has changed the layout of their website significantly but what’s interesting is that it appears they’ve been moving towards a single look and feel across both their web and app experience. For example, the experience you see today on the website is basically the same as what you get if you download the Windows Store app. As you’ll see once we install their PWA that the web interface makes sense because it’s designed to work as an app.

Back in 2017, Twitter announced Twitter Lite which they talked about as being a PWA. They talked a lot about how they built it and their post is definitely worth a read. However, I want to focus on the experience they’ve built for users.

Twitter on Mobile Chrome

Let’s look first at their mobile experience. When you go to mobile.twitter.com in Chrome on a mobile device, assuming you’ve signed in, you’ll see a prompt at the bottom of the screen to “Add Twitter to Home screen”. Alternatively if you’re not signed in, or you’ve dismissed the prompt, you can select Add to Home Screen from the side menu in Chrome.

Tapping on Add Twitter to Home screen displays a confirmation prompt

Tapping Add will “install” the mobile website as an application. This includes an icon that you can move around your home screen. Note that the icon is almost indistinguishable from the actual Twitter application that you can download from the store.

When you launch the newly installed Twitter website, what you see is virtually the same as the Twitter application you’d download from the store. There’s no browser chrome and it looks and behaves just like an app.

And in fact if you press and hold on the Twitter icon on the home screen and go to App info, it even displays information in the same way as it would a regular application, including the ability to Uninstall and Force stop.

Twitter on Desktop Chrome

So that’s the mobile experience. Let’s just check what the desktop experience is. It’s not much different, except for there’s no prompt to install the application. However, if you look to the right of the address bar, there’s a little circle with a plus button; hovering over it indicates it will Install Twitter. Similarly if you go to the right side-menu, there’s an option to Install Twitter.

Again, the install process prompts for user confirmation.

Once installed the Twitter website launches without the browser chrome. In addition to the usual minimize, restore and close buttons on the right side of the title bar of the window, there’s a key icon and a vertical dots icon. The key icon allows you to clear all browsing data.

The vertical dots reveals a menu with the option to Uninstall Twitter.

After installing the Twitter website as an application the Twitter icon does appear in the applications list. However, if you attempt to uninstall the Twitter website by right-clicking the icon and selecting uninstall, it’ll just take you to Add/Remove programs in Settings (I suspect Windows thinks you want to uninstall Chrome). You need to launch the Twitter website application and use the Uninstall Twitter option in the menu.

Making Uno a PWA

Now that we’ve seen how a website can be installed and then behaves just like a regular application, let’s take a look at how easily we can do this for an application that uses Uno and WebAssembly.
The firs thing that we’ll need is a manifest file. In my case I’ve called it manifest.json and I’ve put it into the root of the WASM project in my Uno solution. It’s got the following content that defines things like the icon and the name of application etc.

{
    "background_color": "#ffffff",
    "description": "Freakin awesome Uno web app.",
    "display": "standalone",
    "icons": [
      {
        "src": "Icon192x192.png",
        "sizes": "192x192",
        "type": "image/png"
      }

    ],
    "name": "Uno as a PWA",
    "short_name": "Uno PWA",
    "start_url": "/index.html",
    "theme_color": "#ffffff",
    "scope": "/"
}

Of course, we’ll need to add the icon – I’ve added an 192×192 png with the name Icon192x192.png to the wwwroot folder within the WASM project. I also need to connect the manifest file so that the browser knows to look for it. To do this I need to add it as a link to the web page that hosts the Uno WASM application. In the default WASM project generated by the Uno solution template, there’s no html file, since it’s automatically generated. From the Uno documentation it’s possible to add in a template html file (see the Index.html content override section in the Bootstrap readme). However, it’s also possible to just specify the name of the manifest file using the WasmPWAManifestFile attribute in the project file (see Support for PWA Manifest File section in docs).

<WasmPWAManifestFile>manifest.json</WasmPWAManifestFile>

When we now run the WASM project and bring up the developer tools on the Application tab you can select Manifest and see the information contained in the manifest.json.

Also, the plus button is on the right of the address bar (and the option to Install in the side menu), indicating that this website can be installed.

And that’s it – all you need to do to get your Uno website setup to be installed as as PWA.

Create, Build and Publish a Flutter Web App

Using Flutter for building iOS and Android applications brings with it some advantages over other cross platform solutions such as Xamarin.Forms or React but one of the more interesting developments to keep an eye on is the support for running Flutter apps on the web. In this post we’re going to create a basic Flutter app and show it running locally. We’re then going to publish it out to Azure blob storage (and accessed via the Azure CDN) to demonstrate that a Flutter web app runs purely in the browser and can be hosted on a static endpoint (i.e. no server side code!).

Installation and Setup

Currently support for Flutter on web requires the webdev package but before installing the package it’s important to make sure that you’ve upgraded the Flutter SDK to the latest version. Run the upgrade command either from a command prompt, or from the terminal console within Visual Studio Code.

flutter upgrade

Next, we need to activate the webdev package using the following command

flutter packages pub global activate webdev

Note there are a couple of things that I ran into. I’m running this all on Windows 10, and I was trying to activate the webdev from within Visual Studio Code. I had opened a new window, which meant I didn’t have a Flutter project open. When I ran the above command I saw an error because apparently there was no pubsec.yaml file.

PS C:\> flutter packages pub global activate webdev
 Error: No pubspec.yaml file found.
 This command should be run from the root of your Flutter project.
 Do not run this command from the root of your git clone of Flutter.

After hunting around a bit I found that you can omit “packages” from the command in order for the webdev package to be retrieved and activated (only required to be done once thanks to the global parameter):

flutter pub global activate webdev

This command might take a minute or so to run. On completion, I saw an interesting notice regarding the cache folder for packages:

Installed executable webdev.
 Warning: Pub installs executables into C:\Source\tools\flutter\.pub-cache\bin, which is not on your path.
 You can fix that by adding that directory to your system's "Path" environment variable.
 A web search for "configure windows path" will show you how.
 Activated webdev 2.5.0.

Currently, the only folder that I’ve included in the Path environment variable is the bin folder for where I’d extracted the Flutter SDK (in my case c:\source\tools\flutter\bin). This was the first time I’d seen this warning and it made me wonder what other folders I needed to include. Following the direction of this post, coupled with the above warning I added the following paths to the Path environment variable.

[flutter folder]\bin
[flutter folder]\.pub-cache\bin
[flutter folder]\bin\cache\dart-sdk\bin
%APPDATA%\Pub\Cache\bin

Note: Make sure you restart any command prompt, terminal window and Visual Studio Code after saving the changes to the Path environment variable in order for it to take effect.

At this point you should be good to go ahead and start creating your new Flutter for web project.

Flutter: New Web Project

From the command palette in Visual Studio Code you can select Flutter: New Web Project. This will go ahead and create your new project with a folder structure that will seem familiar if you’ve been building Flutter apps already. What is different is that after creating the project, it opens index.html in the code editor – you can close this as you probably don’t need to mess with this page initially.

You’ll also notice that there are two main.dart files, one in the web folder, and one in the lib folder. If you inspect these individually you’ll notice that the one in the web folder is an entrypoint (you can see main.dart.js being invoked from index.html which is located in the same folder) which initializes the web platform before calling the main() function located in main.dart in the lib folder. Inside the main.dart in the lib folder you’ll see the layout of a very basic “Hello World” Flutter app.

Running Flutter for Web

Of course, the first thing you’ll want to do is to run the Flutter app, which can be done easily in Visual Studio Code by pressing F5. The first time you attempt to run the project you may be prompted to Select Environment. Select Dart & Flutter to proceed.

Unfortunately this then greets me with a dialog saying that there are build errors:

NOTE: If you get build errors on a newly created Flutter web project. Run flutter upgrade from the terminal within Visual Studio Code, or from the command prompt.

After upgrading my Flutter web project, pressing F5 and selecting the environment, builds and runs the project. Your default browser, Chrome in my case, will be launched in order to display your Flutter web project. The first run does take a while, so don’t close Chrome if it just seems to be blocked loading the project the first time.

Publishing a Flutter Web App

It’s all great being able to run the Flutter web project from within Visual Studio Code (or via the command line using webdev serve). However, at some point you’ll want to think about publishing the project to the web. The great thing about a Flutter web app is that the compiler generates JavaScript that runs within the browser to power your app. There’s no server side component required in order to serve your app.

Ok, but where is this JavaScript and how do I know what to copy to my web server? When you get around to publishing your web app, instead of running webdev serve you just need to use the build parameter:

webdev build

This command will generate a build folder, which will include index.html and main.dart.js, along with any other files that your Flutter web app will require in order to run. All you need to do is to copy the contents of the folder to the appropriate folder on your web server.

In my case I didn’t want to spin up a web server, or even a new endpoint in Azure, just for my simple Flutter web app. Instead I decided to copy the necessary files up to a folder in an existing Blob storage account (in fact it’s the very same storage account that serves the images for this blog). In front of this storage account I use an Azure CDN endpoint in order to improve performance and caching of the content. The net result is that any file that’s retrieved from https://blogimages.builttoroam.com is served from the CDN cache, if it exists. If it isn’t already in the CDN cache, it’ll be pulled from https://nicksblog.blob.core.windows.net, which is the underlying blob storage endpoint, added to the CDN cache and then served up. The point I’m making is that the following Flutter web app (it’s not an image – you can click on it to launch it in a separate tab) is being served from a static service, rather than an actual web server.

Summary

Whilst Flutter for web is still in preview, the simplicity of using the Flutter layout engine for building apps for the web will be a game changed for those of us who have hated the prospect of developing using CSS and JS. Hopefully as it gets closer to a final release there will be an ability to have a single project that’s able to build for iOS, Android and Web without having to have separate projects.

Just out of interest, here are a couple of other articles worth reading on Flutter for the web:


NDC – Sydney – October 14-18

Workshop: Building Cross-Platform Apps With Flutter
Presenters: Pooja Bhaumik and myself

Register for NDC Sydney and come learn how to build amazing Flutter apps!!


Using C#, XAML, Uno and MvvmCross to Jump Start Your Cross Platform Application

Rukesh has put together a great post, entitled Using C#, XAML + Uno Platform to Build One Codebase, Cross-Platform Apps, that provides a great walk through of getting started with the Uno Platform. It goes through grabbing the Uno extension for Visual Studio and then creating a new multi-platform application using just C# and XAML. The end result is an app that runs on iOS, Android, Windows and of course WebAssembly. In this post I’m going to step through a similar process of creating an app but this time I’m going to reduce the amount of code I need to write by leveraging the MvvmCross framework.

Note: This post makes use of a pre-release version of MvvmCross that has been built specifically to work with Uno. I’ll add a local copy of the NuGet packages to this post but if you want to roll your own you can simply grab the Uno branch from my fork and build your own NuGet packages (right-click on the MvvmCross.Uno project and select Pack to generate the package).

Setting Up the Basic Application

We’re going to start by creating the basic structure of the application which will have: a head or target project for each platform we’re targeting (iOS, Android, UWP and WebAssembly), a shared project which will house the XAML files for our application, and a core library which will house our business logic (in this case just our ViewModels).

Creating the Uno Application

The head projects and the shared project solution structure we pretty much get for free by using the Cross-Platform App (Uno Platform) template.

We’ll give our project a name, MvxUnoStarter, and click Create to generate the solution structure.

Note: At this point I always make sure that each of the head projects, for iOS, Android, Windows and WebAssembly, are able to be run. Currently there are a few open issues that relate mainly to the template that’s part of the Uno extension. I cover most of these in my post https://nicksnettravels.builttoroam.com/uwp-splitview/. In short you need to remove the scale-200 from the image filenames in the shared project, you need to set the Target Android version and the iOS Deployment Target in order to get the projects to build.

Adding Core Library

Next, let’s add a library based on the Class Library (.NET Standard) project template.

We’ll call it MvxUnoStarter.Core

Make sure that you update all the head projects by adding a reference to the newly created core library. Right-click on each project and select Add, Reference from the context menu; then check the box next to the MvxUnoStarter.Core project.

Referencing Local MvvmCross.Uno NuGet Packages

Update 22/9/2019: Before you proceed with adding a reference to the MvvmCross.Uno packages, please make sure you update all Uno package references (Uno.Wasm.Bootstrap, Uno.UI and Uno.Core) to the latest prerelease version. Be careful that you don’t just update all package references as the Logging packages shouldn’t be updated as there’s a known incompatibility with Uno and the latest versions of those libraries.

Support for MvvmCross is still under development – if you want to get started with MvvmCross for Uno today you can either grab the updated source code from my fork (see note above) or you can grab the pre-built MvvmCross.Uno packages:

In order to make use of these packages you’ll need to setup a local package source (if you already have one setup on your machine you can just drop the packages into the folder for them to be available). Open the Options window, from the Tools menu, and locate the Package Sources node. Click the + button to add a new source; give the source a name (eg Local Mvx Packages) and make sure the Source is set to a folder on your computer where you have saved the packages.

Now that you have a local package source, right-click your solution in the Solution Explorer window and select Manage NuGet Packages for Solution. Change the Package source to the local package source and select the MvvmCross.Uno package (we’re not going to use the Plugins package in this post but if you want to try it out, feel free to add it to you projects too) and make sure you check all projects.

Avoiding Linker Issues

A common gotcha with working with WebAssembly is that the compilation process includes a fairly aggressive linker step. Make sure you update the LinkerConfig.xml file to include your core project (i.e. MvxUnoStarter.Core) and the MvvmCross library (i.e. MvvmCross.Uno and if you’ve added the plugins library, make sure MvvmCross.Uno.Plugins is included too).

Build and Run Your Projects

Before continuing I would highly recommend that you make sure all your head projects can be built and run. Go through each one and set them as the startup project and then run the solution. Note that for the Wasm project, don’t attempt to run with the debugger; instead use the Start Without Debugging (Ctrl+F5).

Application Views, ViewModels and Navigation

Now that we’ve done the basic house keeping and we have four head projects that all run, and have appropriate references to both the core project and MvvmCross, we’re all set to start creating some views and viewmodels.

Adding Views

We’ll start by creating two views for our application. Add a folder called Views to the Shared project and then add two new items to the folder, HomePage and SecondPage, both based on the Blank Page item template.

There are a couple of changes we need to make in order to take advantage of the navigation pattern used by MvvmCross. Both views need to inherit from MvxWindowsPage. We’ll update HomePage to include a TextBlock in the centre of the screen, bound to the property HelloWorld.

<views:MvxWindowsPage 
  x:Class="MvxUnoStarter.Shared.Views.HomePage"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  xmlns:views="using:MvvmCross.Platforms.Uap.Views"
  Background="{ThemeResource ApplicationPageBackgroundThemeBrush}" >
  <StackPanel HorizontalAlignment="Center"
              VerticalAlignment="Center">
    <TextBlock Text="{Binding HelloWorld}" />
  </StackPanel>
</views:MvxWindowsPage>

Next we’ll do the same for SecondPage.

<views:MvxWindowsPage 
  x:Class="MvxUnoStarter.Shared.Views.SecondPage"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  xmlns:views="using:MvvmCross.Platforms.Uap.Views"
  Background="{ThemeResource ApplicationPageBackgroundThemeBrush}" >
  <Grid HorizontalAlignment="Center"
        VerticalAlignment="Center">
    <TextBlock Text="{Binding HelloWorld}" />
  </Grid>
</views:MvxWindowsPage>

Adding ViewModels

The next thing to do is to create the corresponding viewmodels for the two pages: HomeViewModel and SecondViewModel. HomeViewModel will expose a property HelloWorld that simply returns a string to be displayed on the HomePage.

public class HomeViewModel : MvxNavigationViewModel
{
    public string HelloWorld => "Hello World!!!";
    public HomeViewModel(
        IMvxLogProvider logProvider,
        IMvxNavigationService navigationService) 
        : base(logProvider, navigationService)
    { }
}

SecondViewModel is essentially the same but in this case the string is slightly different to indicate it’s on the second page.

public class SecondViewModel : MvxNavigationViewModel
{
    public string HelloWorld => "Hello World - Page 2!!!";

    public SecondViewModel(
        IMvxLogProvider logProvider, 
        IMvxNavigationService navigationService) 
        : base(logProvider, navigationService)
    {
    }
}

Registering the AppStart ViewModel

In order for MvvmCross to know which page to start on it’s necessary to register a viewmodel as the first viewmodel of the application. This is done by creating a class in the core project that inherits from MvxApplication and then calling RegisterAppStart in the Initialize method. In our case we’ll call this class App, with the following code:

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

Updating App.xaml to Use MvvmCross

The last update to be made in order for the application to use MvvmCross, which is essentially the entry point for MvvmCross, is to update App.xaml (and the corresponding codebehind file App.xaml.cs) to inherit from MvxApplication. Note that due to a limitation with the XAML compiler’s ability to handle generics we actually need to create an intermediary class that defines the generic parameters, in this case called StartApp.

public abstract class StarterApp : MvxApplication<MvxWindowsSetup<Core.App>, Core.App>
{
}

Next we need to update App.xaml to inherit from StarterApp

<local:StarterApp
    x:Class="MvxUnoStarter.App"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:MvxUnoStarter"
    RequestedTheme="Light">
</local:StarterApp>

And then lastly, update the constructor of App.xaml.cs. As both MvvmCross support for Uno and the Uno support for Wasm are both prerelease there are a couple of changes, wrapped in the __WASM__ conditional block that are required in order for an MvvmCross based application to run using WebAssembly.

public App()
{
#if __WASM__
    Windows.UI.Core.CoreDispatcher.HasThreadAccessOverride = true;
    MvxSetupSingleton.SupportsMultiThreadedStartup = false;
#endif
    ConfigureFilters(LogExtensionPoint.AmbientLoggerFactory);
    InitializeComponent();
    UnhandledException += App_UnhandledException;
}

Navigation in MvvmCross

Currently, running the application will only show Hello World in the middle of the screen, as shown below. What we really want to do is to be able to navigate to the second view.

To do this, let’s add a button to the HomePage XAML.

<views:MvxWindowsPage
  x:Class="MvxUnoStarter.Shared.Views.HomePage"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  xmlns:views="using:MvvmCross.Platforms.Uap.Views"
  Background="{ThemeResource ApplicationPageBackgroundThemeBrush}" >
  <StackPanel HorizontalAlignment="Center"
              VerticalAlignment="Center">
    <TextBlock Text="{Binding HelloWorld}" />
    <Button Content="Next"
            Command="{Binding ShowSecondPageCommand}" />
  </StackPanel>
</views:MvxWindowsPage>

And then we need to add the ShowSecondPageCommand to the HomeViewModel that the button is data bound to.

public class HomeViewModel : MvxNavigationViewModel
{
    public string HelloWorld => "Hello World!!!";
    public IMvxAsyncCommand ShowSecondPageCommand { get; }
    public HomeViewModel(
        IMvxLogProvider logProvider,
        IMvxNavigationService navigationService) : base(logProvider, navigationService)
    {
        ShowSecondPageCommand =
            new MvxAsyncCommand(async () => await NavigationService.Navigate<SecondViewModel>());
    }
}

The ShowSecondPageCommand is instantiated with an MvxAsyncCommand object with the action to navigate to SecondViewModel. This is where the true power of MvvmCross comes in as the navigation to SecondPage is automatically handled behind the scenes.

MvvmCross for Uno

Whilst it feels like there’s a lot of hoops to jump through, once Uno and MvvmCross are setup, we get all the benefits of ViewModel to ViewModel navigation including dependency injection across iOS, Android, Windows and of course WebAssembly. Now you can rapidly build applications that target any platform.

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

How to Create a Flutter Widget Using a RenderObject

There is plenty of documentation on how to build a Flutter application and more importantly the significance of widgets. Most articles you’ll read about widgets talk about what a StatelessWidget is and what a StatefulWidget is and how you can inherit from these to create your own widgets. However, what happens when you want to create a widget that perhaps does some custom painting, or can’t easily be represented by a combination of existing widgets?

One option is that you add your custom painting to the widget hierarchy. For example in this StackOverflow posting the author, Collin Jackson, creates a class called ProgressPainter that inherits from CustomPainter. An instance of the ProgressPainter is added to the widget hierarchy in order to paint directly to the canvas.

An alternative is to create a class that inherits from RenderObject in order to encapsulate the painting as part of the rendering of a widget. This type of encapsulation is used by the built in Flutter widgets. The example we’ll walk through in a second actually mirrors the code for the Opacity widget, a built in Flutter widget that allows you to control the opacity of child widgets.

What’s a Widget, Element and RenderObject

Before we get into the example, it’s worth understanding the relationship between a widget and its corresponding RenderObject. There are a couple of other posts that are worth a quick read that cover the difference between a Widget, an Element and a RenderObject:

As you start to get familiar with Flutter, you’ll find yourself defining what you want to appear on the screen using a mixture of widgets. Each time you use a widget you’re setting properties on it or nesting other widgets within it. You use widgets to describe what the visual hierarchy should look like at a point in time. You can think of widgets holding configuration information about the visual hierarchy, or being a template for the visual hierarchy.

When your application runs and an instance of a widget is created it’s associated with an Element. Where widgets are immutable and may be recreated based on changing state of the application, Elements mutate based on the widget that they’re associated with. Elements combine in a tree structure to define the current layout of the application.

You’d think that if an Element defines the current layout of the application it would be what draws, or renders, each widget. This role actually resides with the RenderObject which is attached to a RenderObjectElement, a sub-class of Element (in contrast to a ComponentElement which is predominantly responsible for composition of other Elements).

Element and RenderObject By Example

I’m sure at this point this is all sounding very theoretical, so let’s walk through a basic example. After creating a new Flutter project, simple_widgets, I’ve stripped back all the default source code to a minimum app that simply displays Hello World! in the Center of the screen. In the following code we can see that the structure of the app uses a Center widget with a Text widget nested via the child property.

void main() => runApp(
      Center(
        child: Text(
          'Hello World!',
          textDirection: TextDirection.ltr,
        ),
      ),
    );

In VS Code if you press Ctrl+Alt+D the Dart DevTools will be displayed attached to the currently running Flutter app. As you can see from the left tree on the following image, our App is indeed made up a Center with Text nested within it. However, if we look on the right side we can see that there’s actually an additional node on the tree, RichText. The RichText node is actually a nested widget that is returned by the build method of the Text widget and actually does the heavy lifting for the Text widget.

What we’re really seeing in the Dart DevTools are actually the elements that have been created and correspond to what’s being displayed on the screen. To see this clearly you can click the “Debug Paint” button (to the right of the clock button in the toolbar of the Dart DevTools) and then select the RichText node in the right tree. In the running app you’ll see markers similar to the following image identifying the element that’s being displayed on the screen.

If a Widget is the template, or cookie-cutter, and an Element is the instantiation of a widget, the question is where does all the work get done to display content on the screen. This work happens within a RenderObject, or in most cases a derivative of the RenderObject class. In the case of the RichText widget, it creates a RenderParagraph which is solely responsible for rendering out the text associated with the RichText widget.

As you can see from the earlier image taken from the Dev DartTools, the RenderParagraph includes properties such as constraints and size, that are common to all RenderObjects, as well as textStart, textDirection etc. The implementation of the RenderParagraph is too complex to go into here but needless to say it’s responsible for rendering the text associated with the RichText widget to the screen.

Creating a Tint Widget

The purpose of the widget is to overlay a tint across the content specified via the child property of the widget. The code for this widget isn’t too dissimilar to the built in Opacity widget. There going to be two classes involved:

  • Tint – this is a widget that a developer can use in their widget hierarchy in order to provide a coloured overlay, specified by the color property.
  • RenderTint – this is the descendent of RenderObject and will be responsible for painting the tint overlay.

Tint Widget

The Tint widget itself is relatively straightforward, mainly because we inherit from SingleChildRenderObjectWidget that does most of the heavy lifting for us. The important aspects of the class are that it accepts a Color as a required parameter for its constructor and that it overrides both the createRenderObject and the updateRenderObject. In the createRenderObject it returns an new instance of the RenderTint class.

class Tint extends SingleChildRenderObjectWidget {
  const Tint({
    Key key,
    @required this.color,
    Widget child,
  })  : assert(color != null),
        super(key: key, child: child);

  final Color color;

  @override
  RenderTint createRenderObject(BuildContext context) {
    return RenderTint(
      color: color,
    );
  }

  @override
  void updateRenderObject(BuildContext context, RenderTint renderObject) {
    renderObject
      ..color = color;
  }

  @override
  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
    super.debugFillProperties(properties);
    properties.add(ColorProperty('color', color));
  }
}

One thing you’ll notice in the code for the Tint widget is that there’s no code relating to the creation, mounting or unmounting of any element. This is because this is all handled by the SingleChildRenderObjectWidget which overrides the createElement method to return a new instance of the SingleChildRenderObjectElement class.

As a side note, one of the amazing things about Flutter is that as you’re developing your app or creating a widget, you can always drill into the various classes that make up the built-in widgets. In VS Code, pressing F12, when you have a class name selected or the cursor positioned within the class name, will take you to the definition of that class. The Flutter code is extremely well documented and has been split out into various smaller classes to make it easier to identify the purpose of each class.

RenderTint class

Where the purpose of the Tint widget is to allow developers to apply a Tint in their widget hierarchy, the responsibility of the RenderTint class it to draw the tint. The RenderTint is a descendent of the RenderObject. However, rather than implementing all the requirements of a RenderObject, such as calculating size etc, the RenderTint inherits from the RenderProxyBox which handles nearly everything related to layout and positioning.

At the beginning of the class there is some boilerplate code that includes the constructor, that accepts a Color, and the property color that allows the tint colour to be updated. It’s important to note that the setter of the color property does call markNeedPaint, which is important, otherwise the paint method won’t get invoked again.

class RenderTint extends RenderProxyBox {
  RenderTint({
    Color color = Colors.transparent,
    RenderBox child,
  })  : assert(color != null),
        _color = color,
        super(child);

  Color get color => _color;
  Color _color;
  set color(Color color) {
    assert(color != null);
    if (_color == color) return;
    _color = color;
    markNeedsPaint();
    markNeedsSemanticsUpdate();
  }

  @override
  void paint(PaintingContext context, Offset offset) {
    if (child != null) {
      context.paintChild(child, offset);
    }
    context.canvas.drawColor(color, BlendMode.srcOver);
  }

  @override
  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
    super.debugFillProperties(properties);
    properties.add(ColorProperty('color', color));
  }
}

The paint method is where the actual drawing takes place. In this case it uses the paintChild method on the context to draw any child widgets and then it uses drawColor to apply a fill colour over the top of the canvas.

Tint in Action

That’s pretty much it for defining a simple widget that is responsible for rendering its own content, rather than just composing other widgets. All we need to do now is make use of it.

void main() => runApp(
      Center(
        child: Tint(
          color: Color.fromARGB(40, 255, 0, 0),
          child: Text(
            'Hello World!',
            textDirection: TextDirection.ltr,
          ),
        ),
      ),
    );

And of course, the final output

Hopefully in this post you’ve got an appreciation for the differences between a Widget, Element and RenderObject. I’d highly recommend spending some time walking through some of the Flutter code and understanding how the classes fit together and how you can build better widgets by understanding the relationship these classes have.

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.

How to Get Started with Flutter

This post will cover the basics of how to get started with Flutter. It’ll provide you with an overview of your first Flutter app and will give you some pointers on how to get familiar with Flutter, Dart, the tools and ecosystem around building apps using the Flutter SDK. Enough about what you’ll take out of this post, let’s get into it by starting at the beginning.

What is Flutter?

According to the technical overview provide that’s part of the documentation at Flutter.dev, Flutter is a “mobile app SDK for building high-performance, high-fidelity, apps for iOS and Android, from a single codebase”. However, there has been a lot of discussion about Flutter being used for Web and embedded as a solution for desktop apps on MacOS, Windows and Linux.

Before we get too much further into what Flutter is, let’s back up a bit and try to understand why Flutter is needed. A number of years ago it became evident that for a mobile app to be successful it needed to be available on at least iOS and Android. The sheer cost and difficulty of keeping two entirely separate codebases up to date meant that most companies look to some form of cross platform solution. Unfortunately the cross platform, until recently has been a bit hit and miss. On one end you had solutions such as PhoneGap / Cordova that provided a native shell around a web app – these apps typically suffered from poor usability, were often slow and felt like they were a mobile website. Then you had solutions like Xamarin that offered native-like performance but without the ability to share the user interface layer. More recently Xamarin.Forms and ReactNative both offer an abstraction for the user interface that relies heavily on the native platform controls. The abstraction layer is often a point of contention amongst developers who often find themselves spending considerable time adapting the platform controls to get consistency in their apps across the different platforms and devices. In addition, the abstraction layer can become a bottleneck resulting in performance and at times stability issues.

What’s interesting about Flutter is that it takes ownership of the entire page and is solely responsible for rendering each widget that makes up the app using Skia. This approach frees developers up from a lot of the platform idiosyncrasies whilst still giving them the a uniform developer experience for defining the user interface, navigation and behaviour of the app in a consistent manner using Dart.

Before we discuss Dart, it’s worth pointing out a couple of resources that help understand the background of Flutter:

What is Dart?

Flutter apps are written almost entirely in Dart, which is a modern programming language with clear similarities to other object-oriented languages such as Java, C#, JavaScript, Objective-C, Swift. According to the official language website dart.dev, Dart is “a client-optimised language for fast apps on any platform” but this doesn’t specifically address why we need yet another programming language.

An interesting article, entitled Why Flutter Uses Dart, from back in 2017 provides a number of points as to why Flutter uses Dart:

  • Dart supports AOT allowing it to optimise down to native code in order to perform well in production.
  • Dart supports JIT, allowing a fast developer cycle, namely hot reload.
  • Dart supports the declaration of layout for Flutter apps without the need of a separate markup language (the article Making Dart a Better Language for UI delves into this in more detail).
  • Dart borrows many existing static and dynamic language features from other languages that developers are already familiar with.

As you get into developing Flutter apps it’s worth spending a bit of time exploring the Dart programming language – there are a number of features that have been added to make it easier to declare the user interface for Flutter apps.

To get familiar with Dart, head over to DartPad where you can experiment with different language features. On the left side you can write out some Dart code; hit the Run button; and see the output displayed in the right pane.

DartPad for Experimenting and Learning Dart

For those moving to Flutter/Dart from C#, Adam Pedley put together a post on Moving From C# to Dart: Quick Start. There’s also a C# to Dart transpiler Windows app available in the Microsoft Store. For Java developers there’s a Codelab entitled Intro to Dart for Java Developers. However, having gone through the codelab I would encourage anyone wanting to learn more about Dart to give it a go, regardless of what language/technology you’re coming from.

Get Started with Flutter – Your First App

At this point I’m going to jump right in and create an app. I’ll walk you through the process of creating and running an app, highlighting some of the tools and steps you should be aware of. I’ll be doing on a Windows PC using Visual Studio Code (VS Code) but the general process is the same if you’re on a different platform, or using a different IDE.

Talking of IDEs, the Flutter SDK works well with both Android Studio and Visual Studio Code. Rather than step you through getting the tools setup, head over to the installation instructions, pick your platform and work your way through the steps to download and install both your IDE (Android Studio or VS Code) and the Flutter SDK.

Flutter: New Project

Let’s get started with a new Flutter project – I’m going to walk through using VS Code but the process should be similar if you’re working with Android Studio. We’ll get started with Flutter using the VS Code Command Palette (Use Ctrl+Shift+P or from the View menu select Command Palette). Assuming you’ve already got the Flutter and Dart extensions installed (go here if you haven’t set these up) if you type ‘flutter’ you’ll see a list of commands that you can invoke that relate to Flutter.

Flutter Commands in the Command Palette

Select “Flutter:New Project” either using the mouse or since it’s the first item you can just press Enter. Next you’ll be prompted to give you app/project a name. Flutter requires it to be in lowercase and use underscore to separate words (i.e. no spaces). In this case we’re just going to be walking through the default project that the Flutter SDK creates which is a simple counter, so we’ll call our project flutter_counter.

Naming Your Flutter Project

After naming your Flutter project you’ll be prompted for a location on your computer where you want the source code to be located. Once you’ve specified a folder, VS Code will go ahead and create your Flutter project. You’ll see the project structure, including files and folders appear in the Explorer on the left, and you’ll see a progress notification appear in the bottom right corner.

Creating Your Flutter Project

Once your project has been created the notification in the bottom right corner of the screen will update to indicate that “Your Flutter project is ready!”

Your Flutter Project is Ready!

A couple of things to note here:

  • The notification also includes instructions on how to run your Flutter app – You can press F5 to start running.
  • Behind the notification you can see in the Output window that there are some additional instructions. It’s worth scrolling back up through the output and having a read. For example, and we’ll come back to this later, the actual command executed to create the Flutter project was “flutter create –ios-language objc –android-language java” which specifies the use of Objective-C and Java for any platform specific code.
  • At the end of the create process it also checked to see whether all the Flutter tooling is correctly setup. In the above screenshot you can just see the last of these checks where it indicates that there is no connected device – we’ll resolve this in a second when we launch the Android emulator. These checks are useful for diagnosing if anything is wrong with your setup and you can run Flutter Doctor for more information/assistance.

Show Me It Running

In VS Code you can press F5 to run the Flutter app. As I mentioned in the previous section, currently I don’t have any devices attached. This means that I’ll see the following prompt appear at the top of the screen. If you have a connected physical device you’ll be able to select

that device at this point.

Connect a Device or Emulator

Picking one of the existing emulator images will launch that emulator and will subsequently build and deploy the flutter_counter app to the emulator. Note that this process can take a few minutes the first time you do it, so be patient and keep an eye on both the Debug Console window and the notifications – these will update to let you know what VS Code is doing. Once your application is deployed and running you’ll see a mini toolbar appear at the top of the VS Code window.

Debugging Toolbar in VS Code

The toolbar allows you to perform typical debugging actions such as pausing execution, step over, step into and step out. It also allows you to trigger a hot reload (we’ll come to this shortly), restart the app and stop the debugging session. In addition to the toolbar you might also see a notification to Open DevTools – don’t worry if you miss this, or any, notification as you can access them again from the status bar at the bottom of the VS Code window by clicking on the bell icon on the far right side.

You can click on the Open DevTools button in the notification to bring up the Dart DevTools. At any stage in the future if you want to bring up the Dart DevTools you can just type “devtools” into the Command Palette to find the appropriate command. The Dart DevTools are incredibly useful as they give you an immediate view of all the widgets that make up your app at any given point in time. You can click on widgets in the tree on the left and see various properties of that widget in the right pane.

Widget Hierarchy Dart DevTools

There are a host of different features of the Dart DevTools that you should explore and will assist you diagnosing and improving your app. Switching back to VS Code, if you look at the editor window you’ll see the code for the flutter_counter app. As this is a very basic Flutter app, all the Dart code is in a single file. However, you’ll want to make sure you establish the structure of your project so that as your application grows it’s easy to navigate the file/folder structure.

VS Code Editor for Dart Code

A couple of things you’ll notice about the editor window:

  • The code is coloured to highlight Dart keywords, class and property names
  • There are vertical lines, making it easy to identify where code blocks start and end
  • Code is nicely indented to again make it easy to identify code blocks

As you start to write your application, you’ll want to make sure you keep your code nicely formatted. The Flutter extension for VS Code comes with a great code formatter which you can invoke either from the menu/context menu or using the Shift+Alt+F keyboard shortcut.

Breakpoints

As with most modern programming languages, Dart/Flutter offers a high fidelity debugging experience. By this I mean that you can set breakpoints where the code execution will be paused, allowing you to inspect variables and the call stack, before stepping forward line by line.

To set a breakpoint in VS Code you simply need to click in the empty space immediately to the left of the line number in the editor window. Alternatively put the cursor on the line where you want to set a breakpoint and press F9 (or set a breakpoint via the Debug menu). In each case a red dot will appear to the left of the line number indicating that a breakpoint has been set. The following image shows the state when a breakpoint has been hit.

Breakpoint in VS Code

The main editor window highlights the line that execution has been paused at (i.e. just prior to that line being executed). At this point you can use the left windows to inspect local variables and observe the call stack. You can then use familiar debugging actions such as Step Over (F10), Step Into (F11) and Step Out (Shift+F11) to progress through the code.

Hot Reload

One of the big features that everyone loves to talk about is the ability to make changes and for them to be reflected immediately in the running app. Coming from a Windows/.NET background the big fuss around hot reload seems to be blown out of proportion – we’ve had Edit-and-Continue for as long as I can recall. Admittedly this hasn’t been available, until recently, for Xamarin.Forms developers.

Anyhow, to give you an idea of what’s possible, lets change the text that displays the counter value in our flutter_counter app. Here’s the current code.

Text(
  '$_counter',
  style: Theme.of(context).textTheme.display1,
),

With the flutter_counter app running, let’s change the code to the following:

Text(
  'Current count is $_counter',
  style: Theme.of(context).textTheme.display1,
),

When you save the file, you’ll see that the app automatically picks up the changes and updates the running app. On the left is the original app; on the right is the app with the updated Text – updated without having to stop and restart the app. Note that the count, which is part of the state of the app, remains the same.

How Should You Get Started With Flutter?

So far we’ve covered an introduction to Flutter and Dart, and walked through some of the tools as part of creating a simple counter app. The question is where should you go next to learn more about Flutter?

The best way to build skills and familiar is just to get started with Flutter – get the tools and start building an app. Of course, you’ll want some assistance along the way, so here’s just a few resources that you may want to take advantage of.

Online Courses on Flutter

If you want to follow more structured learning, there are a bunch of online courses that cover the basics and will help you get started with Flutter. The following is a selection of some of the more popular courses.

Udemy
Getting Started With Flutter 1.0
Learn Flutter & Dart to Build iOS & Android Apps
The Complete Flutter App Development Course for Android, iOS

Pluralsight
Flutter: Getting Started

Udacity
Build Native Mobile Apps with Flutter

Flutter Crash Course
4 modules: The Basics, Flutter for Junior Devs, The Tourism & Co.App, Pro Flutter Essentials

App Brewery
The Complete Flutter Development Bootcamp Using Dart

Flutter Learn
https://flutterlearn.com

Raywenderlick
Getting Started with Flutter

Labs and Challenges

For those who prefer to explore and learn through trying, there are a bunch of coding labs and challenges that you can take on.

Google
Flutter CodeLabs

Coding Challenges
Dart Codewars
Uplabs design challenges
Try reproducing any of the designs on Dribbble

Community Resources

The Flutter community has an amazing set of contributor producing blogs, videos and collecting lists of resources that are useful for Flutter developers at every level.

YouTube / Video
Mtechviral
VoidRealms
Raja Yogan
Google Developers – Flutter
Fireship.io – Flutter

Resources
Awesome flutter – resources list
PoojaB26 – AwesomeFlutterPlaylist

News, Chat and People

Lastly, there are some great sites, mailing list, chats and people that you can participate or follow in order to ask for help, or just keep up with what’s going on in the Flutter-verse.

News
Flutter weekly
Medium – Flutter

Chat
Gitter – Flutter
MindOrks Slack channel

Twitter
@FlutterWk
@flutterio
@
r_FlutterDev
Tim Sneath @timsneath
Eugenio Marletti @workingkills
Seth Ladd @sethladd
Nilay Yener @nlycskn
Pooja Bhaumik @pblead26
Brett Morgan @DomesticMouse
Thomas Burkhart @ThomasBurkhartB

Get Started With Flutter

As you can see the world of Flutter is rapidly evolving – there are a ton of great resources produced by Google and the community alike. Now’s the time to get familiar with Flutter and start building amazing cross-platform applications.

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.

Navigate Flutter Apps with Routes

One of the most important aspects of an app is the flow or journey that the user takes through the app. Apps are often described in terms of pages, or screens, and navigating between them. In this post I’m going to cover dividing your application into routes, and how to work with the Flutter navigation system.

Whilst a Flutter app is constructed out of widgets, there still needs to be a mechanism for the app to respond to user interaction. For example, tapping on an row in a list of items might display the details of that item. Subsequently tapping on the back button should return the user to the list of items. In order to support navigating between different pages, Flutter includes the Navigator class. According to the documentation the Navigator class is:

 A widget that manages a set of child widgets with a stack discipline. 

For someone who’s just got their head around how to layout widgets, this statement makes no sense. In this post we’re going to ignore this definition and cover the basics of how to navigate between pages. I’m also going to ignore the Flutter definition of a Route. For now we’ll assume that a route is roughly equivalent to a page or a screen in your app. As you can imagine, all but very basic apps have multiple pages that the user is able to navigate between. Flutter apps are no different except that you navigate Flutter apps with routes.

Flutter Navigation

Let’s get into navigating between routes. We’ll start with almost the most basic Flutter app you can create. Technically you can create a Flutter app that doesn’t start with the MaterialApp but then you’re left doing a lot of heavy lifting yourself. The following code sets up a basic app that is comprised of two classes that inherit from StatelessWidget. I could have combined these into a single widget by simply setting the value of the home attribute in the MaterialApp to be the new Container widget. However, I’ve created a separate widget, FirstNoRoutePage, to help make it easy to see how the pages of the app are defined.

void main() => runApp(MyNoRouteNavApp());

class MyNoRouteNavApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: FirstNoRoutePage(),
    );
  }
}

class FirstNoRoutePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Container(
      alignment: Alignment.center,
      child: Text('First Page'),
    );
  }
}

Ad-Hoc Routes with Navigator.push()

Now that we have the basic app structure, it’s time to navigate to our first route. We’re going to use the Navigator widget to push a newly created, or ad-hoc, route onto the stack. The following code:

  • Wraps the Text widget in a FlatButton
  • The onPressed calls the push method on the Navigator, passing in a newly constructed MaterialPageRoute.
  • The MaterialPageRoute builder is set to return a new instance of the SecondNoRoutePage widget
class FirstNoRoutePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Container(
      alignment: Alignment.center,
      child: FlatButton(
        child: Text(
          'First Page',
          style: TextStyle(color: Colors.white),
        ),
        onPressed: () {
          Navigator.push(
              context,
              MaterialPageRoute(
                builder: (BuildContext context) => SecondNoRoutePage(),
              ));
        },
      ),
    );
  }
}

class SecondNoRoutePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Container(
      alignment: Alignment.center,
      child: Text(
        'Second Page',
        style: TextStyle(color: Colors.white),
      ),
    );
  }
}

We now have an application that has two pages that the user can navigate between. On the first page, clicking on the “First Page” text will push a new route that shows the second page (i.e. the SecondNoRoutePage widget). Pressing the device back button (Android only) will navigate the user back to the first page. It does this by popping the route off the stack maintained by the Navigator.

Go Back with Navigator.pop()

For the purpose of this post I’ve kept the pages simple, showing only elements necessary to show which page the user is on, or to allow the user to perform an action. I’ve chosen not to use a Scaffold, which means no AppBar. This also means no back button shown in the AppBar. On iOS this makes it impossible for the user to navigate to the previous page as there is no dedicated back button, unlike Android. If you use a Scaffold in your widget, you’ll see the AppBar. The AppBar adjusts to include a back button if there’s more than one route on the stack.

Flutter provides built in support for navigating back to the previous route via the AppBar back button or, in the case of Android, the device back button. In addition, the pop method on the Navigator can be used to pop the current route off the stack. This will return the user to the previous route.

class SecondNoRoutePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Container(
      alignment: Alignment.center,
      child: FlatButton(
        child: Text(
          'Second Page',
          style: TextStyle(color: Colors.white),
        ),
        onPressed: () {
          Navigator.pop(context);
        },
      ),
    );
  }
}

What the Current Route?

Earlier in the post we created a new route when navigating to the second page of the app. However, what we glossed over is the fact that the first page of the app is also a route. To show this, let’s add some code that adds the name of the current route to both pages. This will highlight where in the Flutter navigation stack the user is currently at.

class FirstNoRoutePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    var route = ModalRoute.of(context).settings.name;
    return Container(
      alignment: Alignment.center,
      child: FlatButton(
        child: Text(
          'First Page - $route',
          style: TextStyle(color: Colors.white),
        ),
        onPressed: () {
          Navigator.push(
              context,
              MaterialPageRoute(
                builder: (BuildContext context) => SecondNoRoutePage(),
              ));
        },
      ),
    );
  }
}

class SecondNoRoutePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    var route = ModalRoute.of(context).settings.name;
    return Container(
      alignment: Alignment.center,
      child: FlatButton(
        child: Text(
          'Second Page - $route',
          style: TextStyle(color: Colors.white),
        ),
        onPressed: () {
          Navigator.pop(context);
        },
      ),
    );
  }
}

What gets displayed on the two pages are ‘First Page – /’ and ‘Second Page – null’. This indicates that the first route is assigned a name of ‘/’, whilst the second route does not have a name (i.e. null value). The first route is created from the home property being set on the MaterialApp. It is assigned a name equal to the defaultRouteName constant from the Navigator class (i.e. ‘/’) to indicate it is the entry point for the application.

As we created the route for the second page, it’s our responsibility to name the route. In the above scenario we’re creating the route at the point where it is required (i.e. when navigating to the second page), and as such we don’t need to give it a name. That is, unless we want to see that the current route is by inspecting it’s name. Let’s update the call to Navigator.push to include a name for the second route.

onPressed: () {
  Navigator.push(
      context,
      MaterialPageRoute(
        builder: (BuildContext context) => SecondNoRoutePage(),
        settings: RouteSettings(name: '/second')
      ));
},

There’s no requirement for the route name to include the ‘/’. However, it is a convention to do so, and there are some scenarios where the ‘/’ has some implied behaviour.

Named Routes in Flutter Navigation

In the previous section we added a name to the ad-hoc route that we created. An alternative to creating routes as they’re required, is to define the routes for the app up front. For example the following code shows three routes that have been defined for an app and can be used for Flutter navigation.

class Routes {
  static const String firstPage = '/';
  static const String secondPage = '/second';
  static const String thirdPage = '/third';
}

class MyNavApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      routes: {
        Routes.firstPage: (BuildContext context) => FirstPage(),
        Routes.secondPage: (BuildContext context) => SecondPage(),
        Routes.thirdPage: (BuildContext context) => ThirdPage(),
      },
    );
  }
}

The Routes class includes the names of each of the three routes. Inside the constructor of the MaterialApp widget the three routes have been declared. Each route is an association between the route name and the builder method that’s used to construct the widget.

Navigate.pushNamed()

When the user clicks the button, the Navigate.pushNamed method is used to navigate to the corresponding route:

onPressed: () {
  Navigator.pushNamed(context, Routes.secondPage);
},

Skipping Over Routes with Navigator.popUntil

Using named routes not only makes it easier to manage the pages in your app, it also means that you can create conditional logic that is dependent on the route name. For example, the Navigator.popUntil allows you to keep popping routes off the stack until the predicate is true. In the following code the predicate looks at the name property of the route to determine whether it is the first page of the app.

class ThirdPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Container(
      alignment: Alignment.center,
      child: FlatButton(
        child: Text(
          'Third Page',
          style: TextStyle(color: Colors.white),
        ),
        onPressed: () {
          Navigator.popUntil(context, (r) => r.settings.name == Routes.firstPage);
        },
      ),
    );
  }
}

Setting the Initial Route

Initially the first page of the app was defined by setting the home property. This was used to define the initial route for the app. When the app was updated to use a set of predefined routes, the initial route was inferred by looking for the route where the name matches the defaultRouteName (i.e. ‘/’). If there wasn’t a route with that name isn’t found, an error will be raised and the app will fail to start.

It is also possible to set the initialRoute property on the MaterialApp. However, this doesn’t negate the need to set either the home property, or have a route declared with a name of ‘/’. What’s really interesting about the initialRoute property is that it can be used to launch the app on a route that has other routes in the navigation stack. For example in the following code the initialRoute is set to the thirdPage, which is defined as ‘/second/third’. When this app launches, it will launch showing ThirdPage but if the user taps the back button, they will go to SecondPage and then FirstPage if they tap again.

class Routes {
  static const String firstPage = '/';
  static const String secondPage = '/second';
  static const String thirdPage = '/second/third';
}

class MyNavApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      initialRoute: Routes.thirdPage,
      routes: {
        Routes.firstPage: (BuildContext context) => FirstPage(),
        Routes.secondPage: (BuildContext context) => SecondPage(),
        Routes.thirdPage: (BuildContext context) => ThirdPage(),
      },
    );
  }
}

On app startup the initalRoute is split on ‘/’ and then any routes that match the segments are navigated to. In this case the app navigates to FirstPage, then SecondPage and finally ThirdPage.

Intercepting Back Button

The last topic for this post looks at how to intercept the back button. This can be done by including the WillPopScope widget and returning an appropriate value in the onWillPop callback.

class ThirdPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return WillPopScope(
      onWillPop: () {
        return new Future.value(true);
      },
      child: Container(
        alignment: Alignment.center,
        child: Text('Third Page'),
      ),
    );
  }
}

Note that the onWillPop expects a Future to be returned, allowing you to return an asynchronous result. In this case the code is simply returning the value of true to allow the current route to be popped.

Summary of Flutter Navigation

This post on Flutter navigation has covered a number of the navigation related methods on the Navigator class. As part of defining how your app looks, you should define all the routes and how the user will navigate between them. If you do this early in the development process you’ll be able to validate the flow of your app as you continue development.

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.