Building a Bicep from an ARM Template with Parameters

As I start to work with Azure Bicep I figured I’d share some of my experiences here. First up, is just a quick recap of how to generate bicep code from an ARM template. Actually, the problem I initially started with was how to start writing bicep code since I wasn’t really familiar with either … Continue reading “Building a Bicep from an ARM Template with Parameters”

As I start to work with Azure Bicep I figured I’d share some of my experiences here. First up, is just a quick recap of how to generate bicep code from an ARM template.

Actually, the problem I initially started with was how to start writing bicep code since I wasn’t really familiar with either the bicep syntax, or how to define each of the resources I wanted to create. I had skimmed through the docs that the team have put together, which helped my understand the basic syntax. Then I took a look through the various examples that the team had posted. In fact, they even have an interactive playground where you can enter bicep code and have it generate the corresponding ARM template. In the top right corner, they have a list of sample templates, along with the corresponding bicep code.

Now that I’d brushed up on the syntax and had trawled through a dozen or so of the examples, I figured I was ready to write my first bicep file. Hold up…. before I get into writing some bicep code, I actually decided to make sure I could build and deploy my bicep code locally (if you want to build and run your bicep code in an Azure DevOps pipeline, check out this post).

Build and Deploy Bicep Code

Here’s a quick summary of working with Visual Studio Code to write, compile and deploy your Bicep code.

  • Install latest Bicep version (v0.1.37-alpha at time of writing) – I’d recommend using the installer as this will correctly setup path variables etc
  • Install the Visual Studio Code extension – this isn’t available via the extensions marketplace, so you’ll need to download it from the release page on github. Make sure you follow the instructions to install the extension by selecting the downloaded VSIX from within Visual Studio Code (rather than double-clicking the downloaded file)
  • Launch Visual Studio Code and create your first, empty, bicep file eg myfirstbicep.bicep. Note that Visual Studio Code will recognise the file type and show “Bicep” as the language in the bottom right corner of the window.
  • Make sure you have the Azure CLI Tools extension installed for Visual Studio Code
  • Create an azcli file, eg myfirstbicep.azcli that will be used to run Azure CLI commands. Alternatively you can just enter the commands into a Powershell terminal (assuming you have the Azure CLI installed).
  • Add the following code to the azcli file – comments should explain what each line does.
# generates myfirstbicep.json (ie compiles the bicep file to ARM template)
bicep build myfirstbicep.bicep 

# create a resource group to deploy the bicep code to
# this is ignored if resource group already exists
az group create -n rgbicepexample -l westus 

# deploy the ARM template that was generated from bicep file
az deployment group create -f myfirstbicep.json -g rgbicepexample

# delete the resource group
# this will prompt to confirm deletion
az group delete -n rgbicepexample
  • Execute each line in the azcli file by right-clicking and selecting Run Line in Terminal

If you run each of the lines you should see output similar to the following

In this scenario I ran the final command to clean up the resource group. If you’re in the process of writing your bicep code, chances are that you’ll simply create the resource group once and then keep compiling the bicep code to the ARM template (first command in the azcli file) and deploying the updated ARM template.

First Bicep Resource

Now that we have the tooling setup so that we can compile and deploy our bicep file, it’s time to dig in and start to create resources. Despite having some examples to follow, I was still a bit bewildered by not knowing what options I needed to include for any given resource. I decided to take a rather pragmatic approach by using the Azure portal to generate resources, export the ARM template and then convert that to bicep code. The last step, as you’ll see, is very manual and repetitive – hopefully this will get easier when this GitHub issue is addressed, providing us with a tool to at least automatically generate bicep code from an ARM template.

Let’s step through this process and I’ll point out a couple of things along the way. I’ll create a Storage Account that can be deployed to a resource group. This should be enough to give you a flavour for the approach.

  • Start by invoking the “az group create” command in Visual Studio code to make sure you have a resource group to work with. Alternatively you can work with any existing resource group if you’d prefer (I tend to avoid doing this to ensure I don’t accidentally overwrite, or delete, other resources I might have)
  • Head to the Azure Portal and open the resource group. Click the Add button to launch the New resource wizard.
  • Search for Storage Account and provide the necessary details to create a Storage Account.
  • I’m not going to go through the various settings here but once you get to the Review + Create tab, you’ll see that there is a link at the bottom to Download a template for automation.
  • When you click through to the template, you’ll see that it’s nicely presented with a tree view to allow for each navigation around the ARM template
  • Rather than simply copying the entire ARM template into our bicep file (and then having to change the syntax from ARM to Bicep), we’re going to do this in steps. We’re going to start with the parameters but instead of using the Parameters in the ARM template, we’re going to grab the json from the Parameters tab.
{
    "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentParameters.json#",
    "contentVersion": "1.0.0.0",
    "parameters": {
        "location": {
            "value": "westus2"
        },
        "storageAccountName": {
            "value": "stbicepexample"
        },
        "accountType": {
            "value": "Standard_RAGRS"
        },
        "kind": {
            "value": "StorageV2"
        },
        "accessTier": {
            "value": "Hot"
        },
        "minimumTlsVersion": {
            "value": "TLS1_2"
        },
        "supportsHttpsTrafficOnly": {
            "value": true
        },
        "allowBlobPublicAccess": {
            "value": true
        },
        "networkAclsBypass": {
            "value": "AzureServices"
        },
        "networkAclsDefaultAction": {
            "value": "Allow"
        }
    }
}
  • Copy the json for the parameters into the bicep file and then start trimming the bits we don’t need – remember the bicep syntax is more succinct, so in a lot of cases the change to syntax is simply removing braces and the verbose ARM template code.

From the parameters json, the main things we need are the parameters and their default values, everything else can go. You’ll need to insert the keyword “param” and the type for each parameter – most are obvious from the default value but if in any doubt, you can go back to the ARM template and look in the parameters list. You’ll also need to change all strings from using ” to using ‘. What we’re left with is a very compact list of parameters, with their default values.

param location string = 'westus2'
param storageAccountName string = 'stbicepexample'
param accountType string = 'Standard_RAGRS'
param kind string = 'StorageV2'
param accessTier string = 'Hot'
param minimumTlsVersion string = 'TLS1_2'
param supportsHttpsTrafficOnly bool = true
param allowBlobPublicAccess bool = true
param networkAclsBypass string = 'AzureServices'
param networkAclsDefaultAction string = 'Allow'

By providing default values for each of the parameter, we’re making them optional – if a parameter value is provided as part of running the deployment, it will be used, otherwise the default value will be used. If you want to require a parameter to be provided as part of the deployment, simply remove the default value (eg change “param location string = ‘westus2′” to just “param location string”)

  • Next up is to copy across the json from the ARM template for the resource itself. I simply grab the resources block of json
    "resources": [
        {
            "name": "[parameters('storageAccountName')]",
            "type": "Microsoft.Storage/storageAccounts",
            "apiVersion": "2019-06-01",
            "location": "[parameters('location')]",
            "properties": {
                "accessTier": "[parameters('accessTier')]",
                "minimumTlsVersion": "[parameters('minimumTlsVersion')]",
                "supportsHttpsTrafficOnly": "[parameters('supportsHttpsTrafficOnly')]",
                "allowBlobPublicAccess": "[parameters('allowBlobPublicAccess')]",
                "networkAcls": {
                    "bypass": "[parameters('networkAclsBypass')]",
                    "defaultAction": "[parameters('networkAclsDefaultAction')]",
                    "ipRules": []
                }
            },
            "dependsOn": [],
            "sku": {
                "name": "[parameters('accountType')]"
            },
            "kind": "[parameters('kind')]",
            "tags": {}
        }
    ],
  • Converting this code is a little trickier but essentially, you need to start with declaring the resource which will look like the following, where the [type] and [apiVersion] need to be replaced by the values from the ARM template.
resource myStorage '[type]@[apiVersion]' = {
    properties: {
    }
}

Essentially the resource declaration is just a key-value pair object graph. To covert the ARM template to the corresponding object graph you mainly just need to remove the parenthesis and trailing commas. You’ll also need to replace the parameter references with a simple reference to one of the param variables we declared earlier (eg “parameters(‘kind’)” becomes just kind). The converted resource (including the param lines we converted earlier) then looks like.

param location string = 'westus2'
param storageAccountName string = 'stbicepexample'
param accountType string = 'Standard_RAGRS'
param kind string = 'StorageV2'
param accessTier string = 'Hot'
param minimumTlsVersion string = 'TLS1_2'
param supportsHttpsTrafficOnly bool = true
param allowBlobPublicAccess bool = true
param networkAclsBypass string = 'AzureServices'
param networkAclsDefaultAction string = 'Allow'


resource myStorage 'Microsoft.Storage/storageAccounts@2019-06-01' = {
    name: storageAccountName
    location: location
    sku: {
        name: accountType
    }
    kind: kind
    properties: {
        accessTier: accessTier
        minimumTlsVersion: minimumTlsVersion
        supportsHttpsTrafficOnly: supportsHttpsTrafficOnly
        allowBlobPublicAccess: allowBlobPublicAccess
        networkAcls: {
            bypass: networkAclsBypass
            defaultAction: networkAclsDefaultAction
        }
    }
}

This bicep code is good to go – you can compile and deploy this to your resource group. Don’t forget, once your code is finished and you’ve tested it locally, make sure you commit it to Azure DevOps and deploy it to Azure as part of a CI/CD process.

Important Note: A lot of Azure resources are pay for use, so you won’t rack up the dollars just by creating resources. However, there are some resources that will start to cost you as soon as they’re created. I would highly recommend deleting your development resource group whenever you’re done for the day, that way you can be sure you’re not going to continue to be charged.

Thinking Out Loud: Events, Messaging and Mvvm Navigation with XAML Frameworks

This post will explores mvvm navigation further, employing the latest c# 9 code generator to reduce the boilerplate code that developers have to write.

In my previous post on this topic, Thinking Out Loud: Mvvm Navigation for XAML Frameworks such as Xamarin.Forms, UWP/WinUI, WPF and Uno, I explored using events emitted by a ViewModel to drive page navigation. This post will explore this concept further, employing the latest c# 9 code generator to reduce the boilerplate code that developers have to write.

Before we go on, let’s just recap of where we got to previously:

  • ViewModels are independent, not knowing what’s before or after them in the navigation flow of the application
  • Use events to signify when a ViewModel is complete
  • ViewModel events are converted to navigation methods at an application level

The upshot is that you can have a simple ViewModel that simply raises an event to indicate that it’s complete (for example when the user clicks a submit button on a form).

public class MainViewModel
{
    public event EventHandler ViewModelDone;
    public async Task<int> DoSomething()
    {
        var rnd = new Random().Next(1000);
        await Task.Delay(rnd);
        if (rnd % 2 == 0)
        {
            ViewModelDone?.Invoke(this, EventArgs.Empty);
        }
        ... 
    }
}

There were a couple of pieces of feedback following my previous post:

  • Use an Observable instead of an event
  • Bypass the ViewModel event completely and simply raise a message that could be handled by the application. For example a developer could attach a Behavior to a Button that would send a message to an application wide dispatcher that could determine where to navigate to based on the message type, or perhaps the parameter set.

Whilst I like the first idea of a ViewModel exposing an Observable, I think that this is an idea we’ll explore sometime in the future. Using an event is incredibly simple and gives us the clear separation we’re after. The only downside is that the add/remove handler code required for events is somewhat nasty.

The idea of using messages, and having a central dispatcher for messages, is a great idea and one that I wanted to explore. I didn’t want to change the ViewModel to have to emit a message, since again this just adds additional complexity to the ViewModel. This means that there needs to be some sort of conversion between events and messages. As you can imagine, this is simply adding more code that developers need to write in order to get everything to work.

I’ve just pointed out two areas where developers will have to write unnecessary code: add/remove event handlers and converting between events and messages. I’ll be using the c# 9 code generators to help eliminate this excess code.

TL;DR

In this post I’m not going to I walk through the complexities of events, messaging and code generation because that would make for a long post. Instead, let me walk through a scenario where we’re going to add a new page, FifthPage, to our existing application (which as you can probably guess, already has four pages). Here’s what we need:

  • Add FifthPage
  • Add a Button to MainPage that navigates to FifthPage when clicked
  • Add Button to FifthPage that navigates back to MainPage when clicked
  • Add FifthViewModel that will be the DataContext for FifthPage
  • Add string property, Title, to FifthViewModel that returns a page title
  • Add TextBlock to FifthPage that is bound to the Title property on the FifthViewModel.

Create Page and ViewModel

The first step is to create the FifthPage and FifthViewModel. I’ve separated out the application code into different projects, so I have my view models in a project called MvvmNavigation.Core. My pages are still in the MvvmNavigation.Shared project that was created by the Uno solution template.

Whilst we’re creating these classes, we’ll add some of the basics that we’re going to need. On the FifthPage, we’ll add a TextBlock, for the Title, and a Button, to trigger navigation back to the MainPage.

<Page
    x:Class="MvvmNavigation.FifthPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">

    <StackPanel VerticalAlignment="Center"
                HorizontalAlignment="Center">
        <TextBlock Text="page title" />
        <Button Content="Go Back" />
    </StackPanel>
</Page>

In the FifthViewModel we’ll add a Title property and a simple event, FifthDone, that will indicate to the application that the FifthViewModel is done. The application will be responsible for determining how to handle this; which according to our specification, should navigate back to MainPage. We’ll also create a RaiseFifthDone method that can be called to invoke the FifthDone event.

public class FifthViewModel
{
    public event EventHandler FifthDone;

    public string Title => "Page 5";

    public void RaiseFifthDone()
    {
        FifthDone?.Invoke(this, EventArgs.Empty);
    }
}

Navigation to FifthPage

To navigate to the FifthPage we need a new Button on the MainPage that, when clicked, will raise a message, PleadTheFifthMessage, that the application will handle in order to navigate to the FifthPage. Let’s unpack this into the steps:

Add Button

Add a Button to the MainPage XAML, along with the NavigationMessageAction behavior which will raise the PleadTheFifthMessage

<Button Content="Go To Page 5">
    <Interactivity:Interaction.Behaviors>
        <Interactions:EventTriggerBehavior EventName="Click">
            <builditbehaviors:NavigationMessageAction MessageType="localmessages:PleadTheFifthMessage" />
        </Interactions:EventTriggerBehavior>
    </Interactivity:Interaction.Behaviors>
</Button>

PleadTheFifthMessage

Add a class, PleadTheFifthMessage, that inherits from CompletedMessage.

public class PleadTheFifthMessage : CompletedMessage
{
    public PleadTheFifthMessage() : base() { }
    public PleadTheFifthMessage(object sender) : base(sender) { }
}

Map PleadTheFifthMessage to FifthViewModel

As part of the MvvmApplicationService, we need to register a navigation to the FifthViewModel for the PleadTheFifthMessage. In this case, we’re only handling the PleadTheFifthMessage for when it’s raised by the MainViewModel.

serviceRegistrations.AddSingleton<INavigationMessageRoutes>(sp =>
{
    var routes = new NavigationMessageRoutes()
        .RegisterNavigate<MainViewModel, PleadTheFifthMessage, FifthViewModel>()
        .RegisterNavigate<MainViewModel, CompletedMessage, SecondViewModel>()
	// ... omitted for brevity
        .RegisterGoBack<CloseMessage>();

    return routes;
});

FifthPage – FifthViewModel Mapping

Whilst we’ve defined a mapping from the PleadTheFifthMessage to the FifthViewModel, there needs to be a way for the application to connect the FifthViewModel to the FifthPage. Rather than rely on naming convention, we’re going to apply an Attribute to the FifthPage.

[ViewModel(typeof(FifthViewModel))]
public sealed partial class FifthPage 
{
    public FifthPage()
    {
        this.InitializeComponent();
    }
}

ViewModel Binding

So far we’ve wired up the navigation from MainPage to FifthPage. However, when arriving at FifthPage it’s clear than neither the Title, nor the Button event handler, has been wired up. The title could be easily wired up by simply creating an instance of the FifthViewModel in XAML and setting it as the DataContext.

However, this doesn’t scale well for real world applications where a view model may be dependent on any number of services that are required (eg for fetching and/or saving data). It’s preferable to have some sort of depedency injection framework that can be used to instantiate the view model.

ViewModel Instantiation

To this end, we’re going to add a ViewModel property to our FifthPage, along with a partial method, InitiViewModel (note also that we’ve added a second parameter to the ViewModel attribute). The implementation of the partial method will be done by our code generator that will generate the code necessary to instantiate, along with any dependent services, the FifthViewModel.

[ViewModel(typeof(FifthViewModel), nameof(InitViewModel))]
public sealed partial class FifthPage 
{
    partial void InitViewModel();
    public FifthViewModel ViewModel => this.ViewModel(() => DataContext as FifthViewModel, () => InitViewModel());

    public FifthPage()
    {
        this.InitializeComponent();
    }
}

FifthViewModel Registration

Despite providing the mapping between FifthPage and FifthViewModel, there’s currently no way for the dependency injection container to create an instance of FifthViewModel, since we haven’t registered the FifthViewModel type. Rather than have the developer work out where to add the code to register the FifthViewModel type, we can simply attribute the FifthViewModel:

[Register]
public class FifthViewModel
{
    // ... 
}

Bind FifthViewModel With x:Bind

We can then update the XAML of our FifthPage to data bind both the Title property and the RaiseFifthDone method on the FifthViewModel.

<Page x:Class="MvvmNavigation.FifthPage"
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
      xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
      mc:Ignorable="d"
      Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">

    <StackPanel VerticalAlignment="Center"
                HorizontalAlignment="Center">
        <TextBlock Text="{x:Bind ViewModel.Title}" />
        <Button Content="Go Back"
                Click="{x:Bind ViewModel.RaiseFifthDone}" />
    </StackPanel>
</Page>

Navigation Back to MainPage

When we created the FifthViewModel we already created the FifthDone event which will be invoked by the RaiseFifthDone method. However, clicking the Button on the FifthPage currently does nothing – actually it does indeed raise the FifthDone event but currently nothing is listening to that event.

Let’s add the EventMessage attribute to the FifthDone event. In this case the attribute references the existing CloseMessage which is the message that will be dispatched when the FifthDone event is raised.

[Register]
public class FifthViewModel
{
    [EventMessage(typeof(CloseMessage))]
    public event EventHandler FifthDone;

    // ...
}

We already have a handler for the CloseMessage for all pages that will simply close the current page.

That completes all the steps necessary to add a new page along with navigation too and from the page. It seems there’s a lot of steps, but actually there’s only minimal code required and it facilitates a high degree of separation between the elements of the application.

If you want to check out the code for this example app, feel free to check out the MvvmNavigation GitHub repo. Note that this is a work in progress and that there will most likely be quite a bit of refactoring over the coming weeks, after which I’ll post about more of the details on how the various mappings work and the code generation that’s used behind the scenes.

How to Debug C# 9 Source Code Generators

Build and debug a source code generator with C# 9 and Visual Studio.

I’ve been working on a follow up to my previous post on a different take on MVVM (Thinking Out Loud: Mvvm Navigation for XAML Frameworks such as Xamarin.Forms, UWP/WinUI, WPF and Uno) and I’ve got the code to a point that I’m mostly happy with it. However, there’s a couple of things that are just tedious for developers to have to do and something that’s perfect for source code generation. More about that in a later post; in this post I want to cover how you can start to generate code and how you can debug the source code generation process.

Before we get started, for a more detailed set of instructions on how to get started with code generation, the dotnet team has a great post on this from earlier this year – Introducing C# Source Generators.

Let’s jump in and create a simple code generator (remember the point of this post isn’t to talk in detail about code generation, it’s to show you how to debug the generation process).

We’ll start with just a vanilla .NET standard 2.0 class library, GenerationSample – this is the assembly where we’ll be doing the code generation. I’ve specifically picked a .NET Standard 2.0 class library to point out that even though the code generation is a feature of the C# 9 feature set, you can still use it to inject code into existing libraries. We’ll come back to this library in a minute, once we’ve created our code generator.

Next up, we need to create a new project that will house our code generator. Again, I’ll create a .NET Standard 2.0 library, called Generators. Creating a generator only requires us to reference the appropriate NuGet packages and then create a class that implements ISourceGenerator (and has the Generator attribute).

Here’s the updated csproj file with both the NuGet package references we need, as well as LangVersion set to preview

<Project Sdk="Microsoft.NET.Sdk">
	<PropertyGroup>
		<TargetFramework>netstandard2.0</TargetFramework>
		<LangVersion>preview</LangVersion>
	</PropertyGroup>
	<ItemGroup>
		<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="3.8.0-3.final" PrivateAssets="all" />
		<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.0" PrivateAssets="all" />
	</ItemGroup>
</Project>

To do the code generator we’ll create a class called DebuggableGenerator that implements the ISourceGenerator interface. Note: If you copy the code from the introductory post from April 2020 by the dotnet team, you’ll see a bunch of errors relating to the ISourceGenerator interface.

These errors can easily be fixed by clicking on the dropdown next to the error and selecting Implement Interface, and then removing the old methods – it looks like the ISourceGenerator interface changed since the post back in April. Our code generator should look similar to the following code at this point.

using Microsoft.CodeAnalysis;
using System;
using System.Diagnostics;

namespace Generators
{
    [Generator]
    public class DebuggableGenerator : ISourceGenerator
    {
        public void Execute(GeneratorExecutionContext context)
        {
            Debug.WriteLine("Execute code generator");
        }
        public void Initialize(GeneratorInitializationContext context)
        {
            Debug.WriteLine("Initalize code generator");
        }
    }
}

The last thing to do, to complete the setup, is to add a reference to the Generators library into the GenerationSample library. The csproj should now look similar to the following.

<Project Sdk="Microsoft.NET.Sdk">
	<PropertyGroup>
		<TargetFramework>netstandard2.0</TargetFramework>
		<LangVersion>preview</LangVersion>
	</PropertyGroup>
	<ItemGroup>
		<ProjectReference 
			Include="..\Generators\Generators.csproj"
			OutputItemType="Analyzer"
			ReferenceOutputAssembly="false" />
	</ItemGroup>
</Project>

A couple of things to note: We’ve set the LangVersion to preview and added a couple of additional attributes to the ProjectReference – these are required to link the code generator into the compilation process.

At this point, you’re good to start implementing your code generation. However, as pointed out by the dotnet team in the FAQ section of their post, there’s currently no built in support for debugging. You can set a breakpoint in Visual Studio but it won’t be hit. You can write Debug.WriteLine or Console.WriteLine statements but they won’t appear in the Output tool window (not even if you set the verbosity to Diagnostic).

Luckily, there’s quite a simple hack to give you a mostly-fully featured debugging experience. At the start of the Initialize methods (you can think of this as the entry point for code generation) add code to launch the debugger.

using Microsoft.CodeAnalysis;
using System.Diagnostics;

namespace Generators
{
    [Generator]
    public class DebuggableGenerator : ISourceGenerator
    {
        public void Execute(GeneratorExecutionContext context)
        {
            Debug.WriteLine("Execute code generator");
        }
        public void Initialize(GeneratorInitializationContext context)
        {
#if DEBUG
            if (!Debugger.IsAttached)
            {
                Debugger.Launch();
            }
#endif 
            Debug.WriteLine("Initalize code generator");
        }
    }
}

To ensure this code doesn’t accidentally end up in my Release code I’ve wrapped it using conditional compilation. The code also tests to see if the Debugger is already attached, otherwise you’ll find that it will continually prompt to launch a new debugging session.

Now if we force a rebuild of our GenerationSample library, we’ll see a prompt asking to specify where the code should be debugged.

From this dialog you can either select the existing instance of Visual Studio, or open a new instance. My preference it to open up a new instance of Visual Studio – it feels a bit to much like inception to debug the code generation in the same instance of Visual Studio but this comes down to whatever works for you.

Once the debugger is attached, you can step through the code, view variables, set breakpoints etc. Unfortunately it appears that Edit and Continue doesn’t work, so it does mean that in order to make changes you need to stop debugging, make changes and then rebuild the project in order to trigger the code generator to run.

And there you have it -the ability to debug your code generation library using Visual Studio.

Pipeline Templates: Using Project Bicep with Azure DevOps Pipeline to Build and Deploy an ARM Templates

This post covers how you can use a .bicep file in your Azure DevOps pipeline to deploy resources to Azure via an ARM template

It’s great to see that Microsoft listens to the pain that developers and devops professionals go through in working with Azure. Specifically that ARM templates, whilst very powerful, are insanely verbose and cumbersome to work with. Hence Project Bicep which is at it’s core a DSL for generating ARM templates. In this post I’m not going to go into much details on how to work with .bicep files but rather on how you can use a .bicep file in your Azure DevOps pipeline to deploy resources to Azure.

If you are interested in learning more about Project Bicep and the .bicep file format, here are some posts that provide some introductory material:

In order to deploy a .bicep file to Azure, you first need to use the bicep command line to generate the corresponding ARM template. You can then deploy the ARM template to Azure. Rather than having to add multiple steps to your build pipeline, wouldn’t it be nice to have a single step that you can use that simply takes a .bicep file, some parameters and deploys it to Azure. Enter the bicep-run template that is part of the 0.7.0 release of Pipeline Templates.

If you haven’t worked with any of the templates from the Pipeline Templates project, here’s the quick getting started:

Add the following to the top of your pipeline – this defines an external repository called all_templates that can be referenced in your pipeline.

resources:
  repositories:
    - repository: all_templates
      type: github
      name: builttoroam/pipeline_templates
      ref: refs/tags/v0.7.0
      endpoint: github_connection

Next, we’re going to use the bicep-run template to deploy our .bicep file to Azure.

      - template: ../../steps/bicep/bicep-run.yml
        parameters:
          name: BicepRun
          az_service_connection: $(service_connection)
          az_resource_group_name: $(resource_group_name)
          az_resource_location: $(resource_location)
          bicep_file_path: '$(bicep_filepath)'
          arm_parameters: '-parameter1name $(parameter1value) -parameter2name $(parameter2value)'

The bicep-run wraps the following:

  • Downloads and caches the Project Bicep command line. It currently references the v0.1.37 release but you can override this by specifying the bicep_download_url – make sure you provide the url to the windows executable, not the setup file.
  • Runs the Bicep command line to covert the specified .bicep file (bicep_file_path parameter) into the corresponding ARM template
  • Uses the Azure Resource Group Deployment Task to deploy the ARM template into Azure. The arm_parameters are forwarded to the overrideParameters parameter on the Azure Resource Group Deployment task.

Would love feedback on anyone that takes this template for a spin – what features would you like to see added? what limitations do you currently see for Project Bicep and the ability to run using this task?

Note: The bicep-run template is designed to run on a windows image.

XAML Back to Basics #16: Custom Grouping

How to implement custom grouping


XAML Basics Series Index Page

The next post in the series originally written by Beatriz Stollnitz. Original post available on Github.

How to implement custom grouping

The previous post in this series shows how to group items based on the value of a certain property. In a real-world scenario you may want to group your items based on some other logic. With this in mind, WPF Data Binding provides a way for you to write custom code and specify how you want to group your items. This allows maximum flexibility; you can group your items pretty much any way you can think of.

In this post I’ll look at how to group items based on their type and an example of how to do custom Grouping.

My data source in this sample is of type ObservableCollection<object>, and contains some objects of type GreekGod and others of type GreekHero. My goal is to group all the items of type GreekGod in a group called “Greek Gods” and group all GreekHero items under the group “Greek Heroes”. This is what the markup looks like:

<Window.Resources>
    <local:GreekGodsAndHeroes x:Key="GodsAndHeroes" />
    <local:GroupByTypeConverter x:Key="GroupByTypeConverter"/>

    <CollectionViewSource x:Key="cvs" Source="{Binding Source={StaticResource GodsAndHeroes}}">
        <CollectionViewSource.GroupDescriptions>
            <PropertyGroupDescription Converter="{StaticResource GroupByTypeConverter}"/>
        </CollectionViewSource.GroupDescriptions>
    </CollectionViewSource>
</Window.Resources>

Notice that this time, instead of setting PropertyName in PropertyGroupDescription, I set the Converter property. This Converter is defined in the code behind and contains the logic to divide the data items in groups.

public class GroupByTypeConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        if (value is GreekGod)
        {
            return "Greek Gods";
        }
        else if (value is GreekHero)
        {
            return "Greek Heroes";
        }
        return null;
    }
}

All the items that return the same value in the Converter will be grouped together. In this scenario I am grouping the items based on their type and my groups are of type string. Remember that you can use a Converter to group your items some other way. Notice also that the groups don’t have to be a string, they can be any object you want.

Just like in the previous post, I want to display the groups and items in a TreeView.

<TreeView ItemsSource="{Binding Source={StaticResource cvs}, Path=Groups}" Width="200">
</TreeView>

In this case, however, templating the items is not as obvious. When the items are all of the same type this is really easy to achieve with a chain of HierarchicalDataTemplates and a DataTemplate for the leaf nodes. In this scenario we need a HierarchicalDataTemplate for the groups and one of two DataTemplates for the leaf nodes, depending on their type.

My first approach to this was to have those 3 templates in the resources and set their DataType property instead of giving them a key (with x:Key). This does not work because when you use a HierarchicalDataTemplate to template a group and do not set its ItemTemplate property, that same template is used for the lower levels of the hierarchy. This behavior is useful when all the levels have items of the same type (for example, when using a TreeView to display a hierarchy of directories in a computer).

My second approach was to set the ItemTemplateSelector property of the HierarchicalDataTemplate to a template selector that decides the correct template to use based on the type of the leaf item. Unfortunately there is a bug in the ItemTemplateSelector property of HierarchicalDataTemplate that prevents this from working. Once the bug is fixed, this will be the correct way to specify the templates.

My third and final approach was to move the template selector to the TreeView and add one more “if” branch to deal with deciding what type to return for the groups (which are of type CollectionViewGroup).

public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
    string templateKey;
    if (item is CollectionViewGroup)
    {
        templateKey = "GroupTemplate";
    }
    else if (item is GreekGod)
    {
        templateKey = "GreekGodTemplate";
    }
    else if (item is GreekHero)
    {
        templateKey = "GreekHeroTemplate";
    }
    else
    {
        return null;
    }
    return (DataTemplate)((FrameworkElement)container).FindResource(templateKey);
}

<Window.Resources>
    <local:GodHeroTemplateSelector x:Key="GodHeroTemplateSelector" />
    (...)
</Window.Resources>

<TreeView ItemsSource="{Binding Source={StaticResource cvs}, Path=Groups}" ItemTemplateSelector="{StaticResource GodHeroTemplateSelector}" Width="200">
</TreeView>

For each of the items displayed in the TreeView, this template selector looks up the appropriate (Hierarchical)DataTemplate in the resources.

Here is a screenshot of the completed sample:

WPF Source Code

WPF

UWP/WinUI Notes
GroupDescriptions aren’t supported on UWP/WinUI so the Greek Gods and Heros are grouped using Linq in the codebehind of the page.

The TreeView isn’t supported on Uno at the moment – due in v3.1

The Treview on UWP/WinUI doesn’t use a HierarchicalDataTemplate to define the hierarchy. Instead it uses TreeViewItem within a DataTemplate to define where there are child nodes.

UWP Source Code

UWP

WinUI with Uno and WinUI Desktop Source Code

WinUI – Desktop

Zero Installer, Zero MSIX, Zero Packaging with .NET Single File Apps

A feature that was added to .NET Core apps was the ability to publish as a single file. As we approach the release of .NET 5 I thought it worthwhile taking a look at the options for publishing Windows Forms and WPF applications with dotnet publish. For this post we’re going to work with a … Continue reading “Zero Installer, Zero MSIX, Zero Packaging with .NET Single File Apps”

A feature that was added to .NET Core apps was the ability to publish as a single file. As we approach the release of .NET 5 I thought it worthwhile taking a look at the options for publishing Windows Forms and WPF applications with dotnet publish.

For this post we’re going to work with a very basic Windows Forms and WPF application: WinFormsSingleFileSample and WpfSingleFileSample. To create these applications I used the Windows Forms (WinForms) Application and WPF Application project templates in Visual Studio 16.8 preview 2 (with .NET 5 RC1 installed). After creating the applications I ran them from within Visual Studio just to confirm there was no issue with the creation process – I always recommend doing this otherwise you end up chasing your tail if something is broken and you only realise after doing hours of work.

dotnet publish

We’ll start of by calling dotnet publish using the Release configuration (typically the Debug configuration will be used if the Configuration parameter isn’t specified):

dotnet publish -r win-x64 /p:Configuration=Release

Here’s the output for this command. Note that if this is the first time you’ve called dotnet build/publish for a specific architecture (using the -r win-x64 flag), it may take a few minutes to restore some of the dependencies.

C:\temp\WinFormsSingleFileSample>dotnet publish -r win-x64 /p:Configuration=Release
Microsoft (R) Build Engine version 16.8.0-preview-20451-02+51a1071f8 for .NET
Copyright (C) Microsoft Corporation. All rights reserved.
Determining projects to restore…
Restored C:\temp\WinFormsSingleFileSample\WinFormsSingleFileSample\WinFormsSingleFileSample.csproj (in 86 ms).
You are using a preview version of .NET. See: https://aka.ms/dotnet-core-preview
WinFormsSingleFileSample -> C:\temp\WinFormsSingleFileSample\WinFormsSingleFileSample\bin\Release\net5.0-windows\win-x64\WinFormsSingleFileSample.dll
WinFormsSingleFileSample -> C:\temp\WinFormsSingleFileSample\WinFormsSingleFileSample\bin\Release\net5.0-windows\win-x64\publish\

The final line includes the output folder for published application. If we look in this folder there are 293 items (280 files and a number of language folders). You can easily zip this folder and copy it to a different machine and run the application but it would be nice to have just a single output.

Self Contained

One option to reduce the number of files to be deployed is to provide the SelfContained parameter. The default value for the SelfContained parameter is true which means that you can copy the output folder to any machine without worrying about whether .NET has been installed.

dotnet publish -r win-x64 /p:Configuration=Release /p:SelfContained=false

Setting SelfContained to false results in a folder containing only 5 items.

Of course, you now need to manage the deployment of .NET dependencies (More information at https://docs.microsoft.com/en-us/dotnet/core/deploying/deploy-with-cli#self-contained-deployment)

PublishSingleFile

Let’s back up and remove the SelfContained parameter, since we’re really interested in distributing an app and not having to worry about any dependencies. This time we’re going to include the PublishSingleFile parameter

dotnet publish -r win-x64 /p:Configuration=Release /p:PublishSingleFile=true

The output on Windows isn’t exactly a single file. There are 11 files in the output folder – these are the native dependencies required in order to run the application.

For more information on why there are multiple files on Windows, check out the preview 8 blog post by Richard Lander from the dotnet team. This is also covered in the comments of this github issue.

IncludeAllContentForSelfExtract

There is still a mechanism where you can achieve a single file output, which is to include the IncludeAllContentForSelfExtract parameter.

dotnet publish -r win-x64 /p:Configuration=Release /p:PublishSingleFile=true /p:IncludeAllContentForSelfExtract=true

The output of this command is two files, one of which is the symbols file (i.e. the pdb file). To deploy this application you can just copy the .exe to the target machine.

One thing to note about the IncludeAllContentForSelfExtract parameter is that when you run the application for the first time, the native dependencies will be extracted which can result in a small delay in launching the app. The managed libraries will be loaded directly from the exe.

PublishTrimmed

Our single file application is currently ~146Mb, which seems a lot for an application that is literally an empty window. Let’s include the PublishedTrimmed file to see if we can optimise the output.

dotnet publish -r win-x64 /p:Configuration=Release /p:PublishSingleFile=true /p:IncludeAllContentForSelfExtract=true /p:PublishTrimmed=true

This command takes a little longer to run but now the output size is just ~83Mb which is a significant improvement. More information on application trimming

PublishReadyToRun

The last parameter we’re going to add is PublishReadyToRun. This essentially invokes AOT compilation, which in theory improves startup performance for the app. I say in theory because given the incredibly simple app we’re dealing with here, it’s unlikely we’ll see any actual performance improvement.

dotnet publish -r win-x64 /p:Configuration=Release /p:PublishSingleFile=true /p:IncludeAllContentForSelfExtract=true /p:PublishTrimmed=true /p:PublishReadyToRun=true  

Now our application has ballooned back up to ~138Mb.

Hopefully in this post you’ll have seen some of the options available to you when publishing Windows Forms (WinForms) and/or WPF applications.

Note: At the time of writing there is a bug with WPF applications where the PublishSingleFile parameter generates a binary that won’t launch in some cases. According to the dotnet team this is to be fixed with RC2 of .NET 5.

Experimenting with .NET 5 Target Framework Names and the Windows platform

Firstly, if you haven’t been following the development of .NET 5 then you should definitely download the latest Visual Studio preview and .NET 5 preview SDK today. Next, you should follow the blogs from the dotnet team and specifically the post by Immo that discusses the future of .NET Standard. The post doesn’t just cover … Continue reading “Experimenting with .NET 5 Target Framework Names and the Windows platform”

Firstly, if you haven’t been following the development of .NET 5 then you should definitely download the latest Visual Studio preview and .NET 5 preview SDK today. Next, you should follow the blogs from the dotnet team and specifically the post by Immo that discusses the future of .NET Standard. The post doesn’t just cover .NET Standard, it also covers the basics of how target framework names (TFMs) work in the .NET 5+ era. In this post we’re going to play around with this and take a look at some examples of different TFMs in action.

NetCoreApp

Let’s start by creating a new project based on the Console Application project template (formerly the Console App (.NET Core) template).

When prompted we’ll select the .NET 5 (Preview) target framework.

Out of the box, this gives use the following project, which as you’d expect targets the net5.0 target framework.

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net5.0</TargetFramework>
  </PropertyGroup>
</Project>

What does this actually mean? Well, let’s add some debugging output to this project file.

<Project Sdk="Microsoft.NET.Sdk"  InitialTargets="Init">
	<PropertyGroup>
		<OutputType>Exe</OutputType>
		<TargetFramework>net5.0</TargetFramework>
	</PropertyGroup>
	<Target Name="Init">
		<Warning Text="$(TargetFrameworkMoniker)" />
		<Warning Text="$(TargetPlatformMoniker)" />
	</Target>
</Project>

When we build the project we’ll see two additional lines in the output that will show the actual TargetFrameworkMoniker and the TargetPlatformMoniker.

1>------ Rebuild All started: Project: TFMSample, Configuration: Debug Any CPU ------
1>c:\temp\TFMSample\TFMSample\TFMSample.csproj(7,3): warning : .NETCoreApp,Version=v5.0
1>c:\temp\TFMSample\TFMSample\TFMSample.csproj(8,3): warning : (No message specified)
1>You are using a preview version of .NET. See: https://aka.ms/dotnet-core-preview
1>TFMSample -> c:\temp\TFMSample\TFMSample\bin\Debug\net5.0\TFMSample.dll
1>Done building project "TFMSample.csproj".
========== Rebuild All: 1 succeeded, 0 failed, 0 skipped ==========

What’s interesting here is that the TargetFrameworkMoniker is actually .NETCoreApp. This means that net5.0, is actually the same as writing netcoreapp5.0 (e.g. <TargetFramework>netcoreapp5.0</TargetFramework >).

Windows 7: WindowsForms and WPF

According to the design documents for Target Framework Names in .NET 5 if we want to target Windows specific apis then we should be able to add the -windows suffix to the TFM. If we change the TFM to .net5.0-windows, this is what we see when we build the project (I’m just going to show the two output lines for brevity).

1>c:\temp\TFMSample\TFMSample\TFMSample.csproj(7,3): warning : .NETCoreApp,Version=v5.0
1>c:\temp\TFMSample\TFMSample\TFMSample.csproj(8,3): warning : Windows,Version=7.0

With this change we’re now targeting the Windows platform and specifically version 7. As you’d imagine this aligns with the API set that was available for Windows 7….. it does not mean that the output can be run on a Windows 7 device. In this case, we should be able to access the APIs from Windows Forms and WPF. Let’s try this out by attempting to reference a WinForms API

static void Main(string[] args)
{
    Console.WriteLine("Hello .NET 5!");
    System.Windows.Forms.Application.SetHighDpiMode(System.Windows.Forms.HighDpiMode.SystemAware);
    Console.ReadLine();
}

This doesn’t compile, yielding the following error.

At this point I started to scratch my head – Surely since .net5 is just .netcoreapp5 then we should be able to reference WinForms and WPF in the same way as we did when targeting netcoreapp3.1. If you go back and create a WinForms or WPF app using the .NET Core project templates, you’ll see that the project file is basically the same as what we have for net5.0 with the exception that it includes an additional project UseWindowsForms or UseWPF.

Adding UseWindowsForms to our net5.0 project fixes our build issue. If you look at the Dependencies, you’ll see that adding UseWindowsForms adds a dependency on the Microsoft.WindowsDekstop.App.WindowsForms package.

It’s no surprise that we can do the same with UseWPF, which adds Microsoft.WindowsDesktop.App.WPF as a dependency.

Windows 10: UWP

Currently we’re using net5.0-windows and as we saw this maps to version 7 of the Windows platform. If we change the tfm to net5.0-windows7, it’s not surprise that we see exactly the same output (i.e. we’re still targeting version 7 of the Windows platform). Now let’s change it to net5.0-windows10.

1>------ Rebuild All started: Project: TFMSample, Configuration: Debug Any CPU ------
1>c:\temp\TFMSample\TFMSample\TFMSample.csproj(9,3): warning : .NETCoreApp,Version=v5.0
1>c:\temp\TFMSample\TFMSample\TFMSample.csproj(10,3): warning : Windows,Version=10.0.0.0
1>C:\Program Files\dotnet\sdk\5.0.100-rc.1.20452.10\Sdks\Microsoft.NET.Sdk\targets\Microsoft.NET.TargetFrameworkInference.targets(222,5): error NETSDK1140: 10.0.0.0 is not a valid TargetPlatformVersion for Windows. Valid versions include:
1>C:\Program Files\dotnet\sdk\5.0.100-rc.1.20452.10\Sdks\Microsoft.NET.Sdk\targets\Microsoft.NET.TargetFrameworkInference.targets(222,5): error NETSDK1140: 10.0.19041.0
1>C:\Program Files\dotnet\sdk\5.0.100-rc.1.20452.10\Sdks\Microsoft.NET.Sdk\targets\Microsoft.NET.TargetFrameworkInference.targets(222,5): error NETSDK1140: 10.0.18362.0
1>C:\Program Files\dotnet\sdk\5.0.100-rc.1.20452.10\Sdks\Microsoft.NET.Sdk\targets\Microsoft.NET.TargetFrameworkInference.targets(222,5): error NETSDK1140: 10.0.17763.0
1>C:\Program Files\dotnet\sdk\5.0.100-rc.1.20452.10\Sdks\Microsoft.NET.Sdk\targets\Microsoft.NET.TargetFrameworkInference.targets(222,5): error NETSDK1140: 8.0
1>C:\Program Files\dotnet\sdk\5.0.100-rc.1.20452.10\Sdks\Microsoft.NET.Sdk\targets\Microsoft.NET.TargetFrameworkInference.targets(222,5): error NETSDK1140: 7.0
1>Done building project "TFMSample.csproj" -- FAILED.
========== Rebuild All: 0 succeeded, 1 failed, 0 skipped ==========

As you’d expect, the windows10 maps to a version number of 10.0.0.0, which, as the error explains, is not a valid version number. There are a couple of interesting things to point out here. Firstly, that the only supported versions of Windows 10 are currently 10.0.17763.0, 10.0.18362.0 and 10.0.19041.0, which limits the backward compatibility of .net5 to only those versions of Windows that are still being supported. Whilst it’s not the official documentation on Windows 10 support, Wikipedia has a nice visual representation of supported versions of Windows 10.

The other thing to note from the error messages is that there is a version 8.0 that’s supported. I’m not sure what APIs are included in version 8.0 but I would imagine that they’re the WinRT APIs that align with Windows 8.0/8.1. I’m not sure why you’d necessarily want to target version 8.0, and not Windows 10, so I’m not going to skip over version 8.0 in this post.

Let’s update our TFM to target version 10.0.18362.0 (i.e. net5.0-windows10.0.18362.0). Now we see the addition of Microsoft.Windows.SDK.NET.Ref as a dependency.

When we build the project we now see the following output, which is what we’d expect.

1>c:\temp\TFMSample\TFMSample\TFMSample.csproj(9,3): warning : .NETCoreApp,Version=v5.0
1>c:\temp\TFMSample\TFMSample\TFMSample.csproj(10,3): warning : Windows,Version=10.0.18362.0

Let’s update our code to access a WinRT api – in this case the Storage API.

static void Main(string[] args)
{
    Console.WriteLine("Hello .NET 5!");
    var tempFolder = ApplicationData.Current.TemporaryFolder;
    Console.WriteLine("Folder " + tempFolder.DisplayName);
    Console.ReadLine();
}

This compiles but throws an InvalidOperationException when we attempt to run it.

Typically when you’re accessing WinRT apis you’re doing so from within the confines of say a UWP application. Here we’re attempting to access the WinRT apis from a Win32 application, so you can imagine there’s some extra work we need to do in order for our application to be permitted to access those apis.

The easiest way to grant access to the apis, is to add a Windows Packaging Project to our solution.

Right-click on the Applications folder in the packaging project and select Add Reference. Select the TFMSample project.

If you set the packaging project as the startup project you can attempt to build and run the application. However, in the current Visual Studio preview you’ll see an error in the debug output

The target process exited without raising a CoreCLR started event. Ensure that the target process is configured to use .NET Core. This may be expected if the target process did not run on .NET Core.

Right-click on the packaging project and select Publish, Create App Package. Follow the prompts (you’ll need to select or create a signing certificate) to create the app package. If you then right-click on the packaging project again and select Deploy, this should install the generated package onto your computer. Then you can run the application from the Start menu – it’ll have the name of the packaging project, not your net5 application.

Hopefully this post has given you some insight into how the target framework names will work for .net5+.

XAML Back to Basics #15: TreeView

How to display grouped data in a TreeView

XAML Basics Series Index Page

The next post in the series originally written by Beatriz Stollnitz. Original post available on Github.

How to display grouped data in a TreeView

The TreeView control is great at displaying structured data using the HierarchicalDataTemplate (see Karsten’s blog post on this topic). But what do you do if the data you’re given is not structured hierarchically? In this post, I will show you how to create that hierarchy from a flat list of data items, using the grouping feature of data binding.

I am using the same Animal data source I used in my last post. Grouping the Animals by Category is done the same way as in my last sample:

<local:Animals x:Key="animals"/>

<CollectionViewSource x:Key="cvs" Source="{Binding Source={StaticResource animals}, Path=AnimalList}">
    <CollectionViewSource.GroupDescriptions>
        <PropertyGroupDescription PropertyName="Category"/>
    </CollectionViewSource.GroupDescriptions>
</CollectionViewSource>

We now have the data in a hierarchical form. In this particular case it has only one level of groups, and another level with the animals. You can easily imagine that by adding more GroupDescriptions you would end up with a deeper hierarchy.

When binding to a CollectionViewSource, the Binding object knows to grab the CollectionViewSource’s View property. This property returns the custom view (of type ICollectionView) that CollectionViewSource creates on top of the data collection (where the grouping is applied). In our scenario, we want to bind to the hierarchy we created with grouping, or in other words, we want to bind to the groups. We can get to this data by binding to the Groups property in ICollectionView:

<TreeView ItemsSource="{Binding Source={StaticResource cvs}, Path=Groups}" ItemTemplate="{StaticResource categoryTemplate}" Width="200">
</TreeView>

When using data binding’s grouping feature, each group of items is wrapped in a CollectionViewGroup object. We can access the name of the group (the property we’re grouping by) by using CollectionViewGroup’s Name property, and we can get to the items that belong to the group through the Items property. This is all the information we need in order to make a HierarchicalDataTemplate that will display the Category of each animal and specify the animals that belong to it:

<HierarchicalDataTemplate x:Key="categoryTemplate" ItemsSource="{Binding Path=Items}" ItemTemplate="{StaticResource animalTemplate}">
    <TextBlock Text="{Binding Path=Name}" FontWeight="Bold"/>
</HierarchicalDataTemplate>

Finally we need a DataTemplate for the leaf nodes, which specifies how we want the Animal data to be displayed. In this case, we are interested in displaying the Name property of each Animal. Notice that the HierarchicalDataTemplate’s ItemTemplate property points to this template.

<DataTemplate x:Key="animalTemplate">
    <TextBlock Text="{Binding Path=Name}"/>
</DataTemplate>

Here is the result of the completed sample:

WPF Source Code

WPF

UWP/Uno Notes
Since UWP (and thus Uno and WinUI) doesn’t support grouping in the CollectionViewSource, we’ve provided an alternative implementation that makes use of Linq’s IGrouping and an ItemTemplateSelector to switch between templates based on whether it’s a Category or an Animal node in the tree.

Uno doesn’t currently support the TreeView, but it’s expected to land in the v3.1 timeframe.

UWP Source Code

UWP

WinUI with Uno and WinUI Desktop Source Code

WinUI – Desktop

XAML Back to Basics #14: Sorting and Grouping

How to sort groups of data items

XAML Basics Series Index Page

The next post in the series originally written by Beatriz Stollnitz. Original post available on Github.

How to sort groups of data items

With the introduction of CollectionViewSource, we are now able to do basic grouping of data items in an ItemsControl without using code. In this post I will show you how to group items and sort those groups.

The data source of this sample consists of a list of objects of type Animal. Animal has a Name and a Category (which is an enumeration). I want to group the items depending on their Category. This is easily done in markup by using CollectionViewSource:

<Window.Resources>
    <local:Animals x:Key="animals"/>

    <CollectionViewSource x:Key="cvs" Source="{Binding Source={StaticResource animals}, Path=AnimalList}">
        <CollectionViewSource.GroupDescriptions>
            <PropertyGroupDescription PropertyName="Category"/>
        </CollectionViewSource.GroupDescriptions>
    </CollectionViewSource>

    <DataTemplate x:Key="animalTemplate">
        <TextBlock Text="{Binding Path=Name}" Foreground="MediumSeaGreen"/>
    </DataTemplate>
</Window.Resources>

<ItemsControl ItemsSource="{Binding Source={StaticResource cvs}}" ItemTemplate="{StaticResource animalTemplate}"/>

As I explained in a previous post, CollectionViewSource creates a custom View over the source list through markup. A view is a layer on top of a source data list that allows us to group, sort, and filter items, as well as keep track of the currently selected item.

If you try the sample markup above, you will see the names of the animals, but no information about the groups. The next step is to provide a template to display the group titles. CollectionViewSource wraps each group of items in an object of type CollectionViewGroup, and we are interested in its “Name” property, which we can display using the following template:

<DataTemplate x:Key="categoryTemplate">
    <TextBlock Text="{Binding Path=Name}" FontWeight="Bold" Foreground="ForestGreen" Margin="0,5,0,0"/>
</DataTemplate>

In order to use this template for the group titles, we have to add it to the GroupStyle property of ItemsControl (which takes a collection of GroupStyle objects):

<ItemsControl ItemsSource="{Binding Source={StaticResource cvs}}">
    <ItemsControl.GroupStyle>
        <GroupStyle HeaderTemplate="{StaticResource categoryTemplate}" />
    </ItemsControl.GroupStyle>
</ItemsControl>

We could add more GroupStyles to the collection, in which case they would be applied to different levels of groups. (For simplicity, we just have one level of grouping in this sample.)

At this point, the groups and items display correctly, but we would like to sort the groups and the items within the groups. I’ve seen a few people approach this by looking for a specific “SortGroups” method or something similar. We didn’t design a special API to sort groups because you can accomplish that simply by sorting the items by the same property by which you are grouping:

<CollectionViewSource x:Key="cvs" Source="{Binding Source={StaticResource animals}, Path=AnimalList}">
    <CollectionViewSource.GroupDescriptions>
        <PropertyGroupDescription PropertyName="Category"/>
    </CollectionViewSource.GroupDescriptions>
    <CollectionViewSource.SortDescriptions>
        <scm:SortDescription PropertyName="Category" />
        <scm:SortDescription PropertyName="Name" />
    </CollectionViewSource.SortDescriptions>
</CollectionViewSource>

Adding two sort descriptions allows us to sort the groups first and then the items within the groups. Notice that because Category is an enumeration, sorting by that property will display the groups in the order they are defined in the enumeration (which may or may not be alphabetically). Name is of type string, so the leaf items will be displayed alphabetically.

This is a screenshot of the completed sample:

WPF Source Code

WPF

UWP Notes
The UWP CollectionViewSource doesn’t support grouping or sort, requiring the underlying data source to be grouped and sorted in advance.

UWP Source Code

UWP

Uno Notes
The Uno CollectionViewSource doesn’t handle grouped data source. Instead it presents the items in a single list.

WinUI Notes
The WinUI CollectionViewSource for both desktop and UWP is similar to the UWP implementation. As such it requires the data source to be grouped and sorted in advance.

WinUI with Uno and WinUI Desktop Source Code

WinUI – Desktop

XAML Back to Basics #13: DataTemplateSelector

How to display items in an ItemsControl using different templates

XAML Basics Series Index Page

The next post in the series originally written by Beatriz Stollnitz. Original post available on Github.

How to display items in an ItemsControl using different templates

I will show you two ways to display some items of a data bound collection differently from others. The rule of thumb is straightforward: if you want to differentiate items that are of the same type based on one of their properties, you should use DataTemplateSelector; if your data items are of different types and you want to use the types to differentiate them, then using implicit data templating is a simpler way to do this.

Let us consider the scenario where the source collection has elements that are all of the same type. In this case, the goal is to change the way they are displayed based on some property in the data element, and using a DataTemplateSelector is the way to go. In the sample code below, the ListBox is bound to a collection of Places, where Place is an object with properties Name and State. I want places in Washington state to be displayed differently from other places, so I defined two DataTemplates in the resources. Then I wrote a PlaceTemplateSelector that picks the correct DataTemplate based on the State property of a Place. Finally, I instantiated a ListBox whose ItemTemplateSelector DependencyProperty is set to the selector I defined.

<Window.Resources>    
    <local:Places x:Key="places" />

    <DataTemplate x:Key="washingtonTemplate">
        <Border Background="Lavender">
            <TextBlock Text="{Binding Path=Name}" Foreground="CornFlowerBlue" FontWeight="Bold"/>
        </Border>
    </DataTemplate>

    <DataTemplate x:Key="notWashingtonTemplate">
        <TextBlock Text="{Binding Path=Name}" Foreground="DarkSeaGreen" />
    </DataTemplate>

    <local:PlaceTemplateSelector WashingtonTemplate="{StaticResource washingtonTemplate}" NotWashingtonTemplate="{StaticResource notWashingtonTemplate}" x:Key="placeTemplateSelector" />
</Window.Resources>

<ListBox ItemsSource="{Binding Source={StaticResource places}}" ItemTemplateSelector="{StaticResource placeTemplateSelector}" Margin="10"/>

Here is the code for the PlaceTemplateSelector:

public class PlaceTemplateSelector : DataTemplateSelector
{
    private DataTemplate washingtonTemplate;

    public DataTemplate WashingtonTemplate
    {
        get { return washingtonTemplate; }
        set { washingtonTemplate = value; }
    }

    private DataTemplate notWashingtonTemplate;

    public DataTemplate NotWashingtonTemplate
    {
        get { return notWashingtonTemplate; }
        set { notWashingtonTemplate = value; }
    }

    public override DataTemplate SelectTemplate(object item, DependencyObject container)
    {
        Place place = (Place)item;

        if (place.State == "WA")
        {
            return washingtonTemplate;
        }
        else
        {
            return notWashingtonTemplate;
        }
    }
}

Consider now the scenario where the collection has objects with different types added to it. In this case, the goal is to template items differently depending on their type. In the sample code below, the ListBox is bound to a heterogeneous collection that contains both GreekGod and GreekHero objects.

<Window.Resources>
    <local:GreekGodsAndHeros x:Key="godsAndHeros" />
</Window.Resources>

<ListBox ItemsSource="{Binding Source={StaticResource godsAndHeros}}" Margin="10"/>

Sure, a DataTemplateSelector could be used to template the items by picking the correct DataTemplate depending on the type of the item passed to the SelectTemplate method, as I have seen a few people do. However, implicit data templating is a better way to do this because it accomplishes the same thing all in xaml (no need for code behind). To use a DataTemplate implicitly, instead of setting its key (with x:Key), I set the DataType property to the type I want it to be applied to.

<DataTemplate DataType="{x:Type local:GreekGod}">
    <Grid>
        <ColumnDefinition Width="100"/>
        <ColumnDefinition Width="*"/>
        <RowDefinition Height="Auto"/>
        <TextBlock Text="{Binding Path=GodName}" Grid.Column="0" Grid.Row="0" Foreground="Brown"/>
        <TextBlock Text="{Binding Path=GodDescription}" Grid.Column="1" Grid.Row="0" Foreground="Brown"/>
    </Grid>
</DataTemplate>

<DataTemplate DataType="{x:Type local:GreekHero}">
    <TextBlock Text="{Binding Path=HeroName}" FontWeight="Bold" Foreground="Red"/>
</DataTemplate>

Here is a screen shot of the completed sample:

WPF Source Code

WPF

UWP/Uno Notes

There is no support for implicit templating based on the type of data object. I’ve added an additional template selector which uses the type of the object to determine which template to use. The DataType attribute on the templates has been used to support x:Bind instead of Binding.

UWP Source Code

UWP

WinUI with Uno and WinUI Desktop Source Code

WinUI – Desktop

XAML Islands Getting Started Guide – Adding UWP Controls to Windows Forms or WPF Application

One of the reasons that Microsoft failed to get wide spread adoption of the Universal Windows Platform (UWP) is that there is already a massive investment into Windows Forms (WinForms) and Windows Presentation Foundation (WPF) applications. In this post we’re going to walk through how you can use XAML Islands to host UWP controls within an existing WinForms or WPF application.

One of the reasons that Microsoft failed to get wide spread adoption of the Universal Windows Platform (UWP) is that there is already a massive investment into Windows Forms (WinForms) and Windows Presentation Foundation (WPF) applications. What’s ironic is that this is true for both existing applications and new applications. Over the last couple of years Microsoft has changed strategy and has been looking at tools and techniques for bridging the gap between these frameworks in order to allow developers to take advantage of the rich controls and capabilities of UWP. In this post we’re going to walk through how you can use XAML Islands to host UWP controls within an existing WinForms or WPF application.

Before we get into working with XAML Islands, here are a couple of reference posts that are worth a read if you want to understand the background and some additional details about XMAL Islands:

Windows Forms

Let’s get into this – we’re going to start with Windows Forms and we’re going to be working with a Windows Forms application that’s sitting on .NET Core 3.1. As Miguel discusses in his post, there is support for .NET Framework but there are some limitations for third party controls. If your application is still based on .NET Framework, I would highly recommend looking at migrating to .NET Core.

In Visual Studio, we’ll create a new project using the Windows Forms (WinForms) Application project template. I’m currently using Visual Studio 2019 16.8 preview 2.1 where the project templates have been renamed – this template was formerly called Windows Forms App (.NET Core), which points to Microsoft’s intent to move developers to building Windows Forms app off .NET Core instead of .NET Framework (the .NET Framework based template is still called called Windows Forms App (.NET Framework)).

We’re going to select .NET Core 3.1 for the target framework

After creating the project we’ll rename Form1 to MainForm, and then proceed with adding four more forms that will host the four scenarios we’re going to look at.

Next I’ll create four buttons on the MainForm, which we’ll use to launch the four forms we just created.

The code behind for these buttons is relatively simple.

private void btnSimpleButton_Click(object sender, EventArgs e)
{
    new SimpleButtonForm().ShowDialog();
}
private void btnCustomControl_Click(object sender, EventArgs e)
{
    new CustomControlForm().ShowDialog();
}
private void btnThirdPartyControl_Click(object sender, EventArgs e)
{
    new ThirdPartyControlForm().ShowDialog();
}
private void btnThirdPartyControlWithStyle_Click(object sender, EventArgs e)
{
    new ThirdPartyControlWithStyleForm().ShowDialog();
}

Standard UWP Button

Now let’s start with the first scenario where we’re just going to display a standard UWP Button inside the SimpleButtonForm. To do this, the first thing we need to do is to reference the Microsoft.Toolkit.Forms.UI.XamlHost NuGet package.

Next, we’re going to add code in the SimpleButtonForm constructor to create the instance of both the Button and the WindowsXamlHost. The WindowsXamlHost is the wrapper that makes it really easy to add UWP based controls to the Windows Forms application.

public SimpleButtonForm()
{
    InitializeComponent();

    var myHostControl = new Microsoft.Toolkit.Forms.UI.XamlHost.WindowsXamlHost();
    myHostControl.Dock = System.Windows.Forms.DockStyle.Fill;
    myHostControl.Name = "hostUwpButton";

    var uwpButton = new Windows.UI.Xaml.Controls.Button();
    uwpButton.Content = "Say Something!";
    uwpButton.HorizontalAlignment = Windows.UI.Xaml.HorizontalAlignment.Stretch;
    uwpButton.VerticalAlignment = Windows.UI.Xaml.VerticalAlignment.Stretch;
    uwpButton.Click += UwpButton_Click;

    myHostControl.Child = uwpButton;
    this.Controls.Add(myHostControl);
}

private void UwpButton_Click(object sender, Windows.UI.Xaml.RoutedEventArgs e)
{
    MessageBox.Show("Hello World!");
}

Important Note: If we run the application at this point we’ll see an error shown in the following image, that reads “WindowsXamlManager and DesktopWindowsXamlSource are supported for apps targeting Windows version 10.0.118226.0 and later”.

To fix this issue we need to include an app.manifest file with the following content:

<?xml version="1.0" encoding="utf-8"?>
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
	<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
		<application>
			<!-- Windows 10 -->
			<maxversiontested Id="10.0.18362.0"/>
			<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />

		</application>
	</compatibility>
</assembly>

The app.manifest file needs to be set as the Manifest file for the Windows Forms project via the Application tab of the Project Properties (Right-click on the project in Solution Explorer and select Properties).

Now, we can run the application, click on the button entitled “Simple UWP Button” and then click on the Say Something button.

What we’ve seen so far is simply using the built-in UWP controls. If you want to use your own custom controls, or third party controls, you’ll need to follow some additional steps.

Custom Control

For the custom control scenario, let’s start by creating a new project based on the Class Library project template. Note that you could also use a UWP class library for this and follow the same steps.

In order to add UWP controls to the class library, we’ll update the project file to use the uap10.0.16299 target framework (this step isn’t required if you’re use the UWP class library project template).

<Project Sdk="MSBuild.Sdk.Extras/2.1.2">
  <PropertyGroup>
    <TargetFrameworks>uap10.0.16299</TargetFrameworks>
  </PropertyGroup>
</Project>

Our custom control is going to be very basic with a single Button that’s going to generate a random number that’s displayed in a TextBlock.

<UserControl
    x:Class="UwpControlLibrary.MyCustomControl"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d">
    <StackPanel>
        <Button Content="Generate Random Number" Click="RandomNumber_Click" />
        <TextBlock Text="[placeholder]" x:Name="RandomNumberOutputTextBlock" />
    </StackPanel>
</UserControl>

With very simple code behind

private void RandomNumber_Click(object sender, Windows.UI.Xaml.RoutedEventArgs e)
{
    var rnd = new Random();
    RandomNumberOutputTextBlock.Text = rnd.Next(0, 10000).ToString();
}

Referencing our control isn’t a simple as just adding a project reference to our class library. Instead, we need to provide a context in which our control is going to be instantiated. When our control gets created in a normal UWP application, it does so within the context of the application, which allows for resolution of resources, styles etc. We need to provide a similar context for our control when it’s rendered within a Windows Forms application.

To do this, we need to create a new project based on the Blank App (Universal Windows) project template. I would avoid attempting to use either a UWP class library or multi-targeted class library for this as neither of them will generate the necessary output for the hosting of our custom control in a Windows Forms or WPF application.

In creating the new project, make sure you select 10.0.18362 (version 1903) as the minimum version.

We then need to add a reference to the Microsoft.Tookit.Win32.UI.XamlApplication NuGet package to the UWP application project.

The UWP application needs to reference the control library.

And the Windows Forms application needs to reference both the UWP application and the control library.

Now we can go ahead and add the code to the CustomControlForm to add an instance of the MyCustomControl.

public CustomControlForm()
{
    InitializeComponent();

    var myHostControl = new Microsoft.Toolkit.Forms.UI.XamlHost.WindowsXamlHost();
    myHostControl.Dock = DockStyle.Fill;
    myHostControl.Name = "uwpHost";

    var customControl = new MyCustomControl();
    customControl.HorizontalAlignment = Windows.UI.Xaml.HorizontalAlignment.Stretch;
    customControl.VerticalAlignment = Windows.UI.Xaml.VerticalAlignment.Stretch;
    myHostControl.Child = customControl;

    this.Controls.Add(myHostControl);
}

At this point if you try to run the Windows Forms application you’ll see build errors similar to the following

Microsoft.VCRTForwarders.140.targets(91,9): warning : Because your app is being built as AnyCPU no Microsoft.VCRTForwarders.140 DLLs were copied to your ouput folder. Microsoft.VCRTForwarders.140 only supports x86, x64, or arm64 applications due to a C++ Runtime dependency.

or

error : The OutputPath property is not set for project 'UwpXamlIslandHostApp.csproj'. Please check to make sure that you have specified a valid combination of Configuration and Platform for this project. Configuration='Debug' Platform='AnyCPU'.

The errors are pointing to a disparity between the platforms that the projects are being built for. To work around this, you need to change the Platform for each project to be consistent. Right-click on the solution in Solution Explorer and select Configuration Manager. For each platform, make sure the same Platform is selected. This may mean that you have to create a new configuration for those projects that only have Any CPU, such as in this example.

From the New Project Platform dialog, select the platform and make sure the “Create new solution platforms” option is unchecked.

With this done, we should be able to run the application and click on the Custom Control button to launch the CustomControlForm that hosts the MyCustomControl. In this case the MyCustomControl encapsulates the functionality for handling the Button click and updating the Text on the TextBlock.

Third Party Control

In this scenario we’re going to reference the Telerik UWP control library (Telerik.UI.for.UniversalWindowsPlatform on NuGet) and make use of the RadCalendar. The first step is to simply add the reference to the NuGet package. I’m going to go ahead and add it to both the Windows Forms project, as well as both the UWP application and class library projects.

With the reference added, we can simply create an instance of the RadCalendar inside the constructor of the ThirdPartyControlForm.

public ThirdPartyControlForm()
{
    InitializeComponent();

    var myHostControl = new Microsoft.Toolkit.Forms.UI.XamlHost.WindowsXamlHost();
    myHostControl.Dock = DockStyle.Fill;
    myHostControl.Name = "uwpHost";

    var customControl = new Telerik.UI.Xaml.Controls.Input.RadCalendar();
    customControl.HorizontalAlignment = Windows.UI.Xaml.HorizontalAlignment.Stretch;
    customControl.VerticalAlignment = Windows.UI.Xaml.VerticalAlignment.Stretch;
    myHostControl.Child = customControl;
            
    this.Controls.Add(myHostControl);
}

And without any further changes, we can go ahead and run the Windows Forms application and click on the Third Party Control button. This will show the ThirdPartyControlForm with the RadCalendar visible.

Third Party Control With Style

The last scenario also makes use of the RadCalendar. This time we’re going to combine it with other Windows Forms controls to illustrate how you can use data binding and apply styles.

To begin with we’re going to use the Windows Forms designer to put together a basic layout. Unfortunately even though the WindowsXamlHost control appears in the Toolbox, an exception is thrown by Visual Studio when attempting to add it directly to the Form. Instead, I’ve added a Panel which will act as a placeholder for the WindowsXamlHost, and subsequently the RadCalendar.

I’ve also added a Windows Forms DateTimePicker and a Label. The idea is that the user should be able to use either the RadCalendar or the DateTimePicker to select a date, which will be displayed in the Label below.

We’ll add a very simple class that will be used for data binding.

public class DataModel : INotifyPropertyChanged
{
    private string dateAsString;
    private DateTime myDate;

    public event PropertyChangedEventHandler PropertyChanged;

    public string DateAsString
    {
        get => dateAsString; set
        {
            dateAsString = value;
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(DateAsString)));
        }
    }

    public DateTime MyDate
    {

        get => myDate;
        set
        {
            myDate = value;
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(MyDate)));
            DateAsString = myDate.ToString("O");
        }
    }
}

In terms of data binding to the RadCalendar, we have a couple of options. We could manually create the data binding expression. This seems quite archaic, so alternatively we can specify the binding in XAML. However, this only works if the instance of the RadCalendar is being created in XAML, so that we can specify the binding expression. Easily done – by creating a CustomCalendar UserControl in our Control Library, with the following XAML.

<UserControl x:Class="UwpControlLibrary.CustomCalendar"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             xmlns:input="using:Telerik.UI.Xaml.Controls.Input"
             xmlns:telerikCalendar="using:Telerik.UI.Xaml.Controls.Input.Calendar"
             mc:Ignorable="d">
    <UserControl.Resources>
        <telerikCalendar:CalendarDateToSingleDateRangeConverter x:Key="converter" />
    </UserControl.Resources>
    <input:RadCalendar SelectedDateRange="{Binding MyDate, Converter={StaticResource converter}, Mode=TwoWay}"
                       SelectionMode="Single" />
</UserControl>

Note that in this case, being able to do the binding in XAML is particularly useful since we need to create and use an instance of the CalendarDateToSingleDateRangeConverter. This converter allows for binding a single DateTime property (MyDate) to the SelectedDateRange property.

Back to the Windows Forms project, the code for creating the instance of the CustomCalendar control and wiring up the data binding with the other controls on the page, looks like this.

public ThirdPartyControlWithStyleForm()
{
    InitializeComponent();

    var myHostControl = new Microsoft.Toolkit.Forms.UI.XamlHost.WindowsXamlHost();
    myHostControl.Dock = DockStyle.Fill;
    myHostControl.Name = "uwpHost";

    var customControl = new CustomCalendar();
    customControl.HorizontalAlignment = Windows.UI.Xaml.HorizontalAlignment.Stretch;
    customControl.VerticalAlignment = Windows.UI.Xaml.VerticalAlignment.Stretch;
    myHostControl.Child = customControl;

    pnlXamlIsland.Controls.Add(myHostControl);

    var data = new DataModel();
    customControl.DataContext = data;
    dtpPickDate.DataBindings.Add(new Binding(nameof(DateTimePicker.Value), data, nameof(DataModel.MyDate), true, DataSourceUpdateMode.OnPropertyChanged));
    lblDate.DataBindings.Add(new Binding(nameof(Label.Text), data, nameof(DataModel.DateAsString), true, DataSourceUpdateMode.OnPropertyChanged));
}

Running the Windows Forms application and clicking on the Third Party Control With Style button shows the ThirdPartyControlWithStyleForm. Either the RadCalendar (nested in the CustomCalendar control) or the DateTimePicker can be used to select a date, which is shown in the Label below.

You’ll notice that the selected date in the RadCalendar has a different style applied with a green background and red border. This has been applied using an implicit style defined in the App.xaml in the UWP application project.

<xamlhost:XamlApplication xmlns:xamlhost="using:Microsoft.Toolkit.Win32.UI.XamlHost"
                          x:Class="UwpXamlIslandHostApp.App"
                          xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                          xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                          xmlns:input="using:Telerik.UI.Xaml.Controls.Input">
    <xamlhost:XamlApplication.Resources>
        <Style TargetType="input:RadCalendar">
            <Setter Property="SelectedCellStyle">
                <Setter.Value>
                    <input:CalendarCellStyle>
                        <input:CalendarCellStyle.DecorationStyle>
                            <Style TargetType="Border">
                                <Setter Property="Background"
                                        Value="PaleGreen" />
                                <Setter Property="BorderBrush"
                                        Value="MediumVioletRed" />
                            </Style>
                        </input:CalendarCellStyle.DecorationStyle>
                    </input:CalendarCellStyle>
                </Setter.Value>
            </Setter>
        </Style>
    </xamlhost:XamlApplication.Resources>
</xamlhost:XamlApplication>

That’s it for the Windows Forms application – four different scenarios for hosting UWP controls in a Windows Forms application using Xaml Islands.

Windows Presentation Foundation (WPF)

Now we’ll move on to showing the same four scenarios in a WPF application. As we’ve already done a lot of the setup work for the various controls, this section will focus on the differences with the hosting in WPF. To get started we’ll use the WPF Application project template.

Like we did for the Windows Forms application, we’ll create four additional Windows and connect them to four buttons on the main Window of the application.

We’ll need to reference the Microsoft.Toolkit.Wpf.UI.XamlHost NuGet package.

You’ll also need to add an app.manifest file and set it as the manifest file for the WPF application.

Standard UWP Button

The XAML and code behind for the SimpleButtonWindow are as follows.

<Window x:Class="WPFIslandsDemo.SimpleButtonWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:xamlhost="clr-namespace:Microsoft.Toolkit.Wpf.UI.XamlHost;assembly=Microsoft.Toolkit.Wpf.UI.XamlHost"
        mc:Ignorable="d"
        Title="SimpleButtonWindow" Height="450" Width="800">
    <Grid>
        <xamlhost:WindowsXamlHost x:Name="XamlHost"/>
    </Grid>
</Window>

public SimpleButtonWindow()
{
    InitializeComponent();

    var button = new Windows.UI.Xaml.Controls.Button();
    button.HorizontalAlignment = Windows.UI.Xaml.HorizontalAlignment.Stretch;
    button.VerticalAlignment = Windows.UI.Xaml.VerticalAlignment.Stretch;
    button.Content = "Say Something";
    button.Click += Button_Click;
    XamlHost.Child = button;
}

private void Button_Click(object sender, Windows.UI.Xaml.RoutedEventArgs e)
{
    MessageBox.Show("Hello World!");
}

Running this and clicking the Simple UWP Button, we see a new Window appear that’s similar to the Windows Forms example.

Custom Control

Adding the Custom Control is actually even simpler, as we can just specify the MyCustomControl using the InitialTypeName property. Don’t forget to add references to the UWP application and class library projects.

<Window x:Class="WPFIslandsDemo.CustomControlWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WPFIslandsDemo"
        xmlns:xamlhost="clr-namespace:Microsoft.Toolkit.Wpf.UI.XamlHost;assembly=Microsoft.Toolkit.Wpf.UI.XamlHost"
        mc:Ignorable="d"
        Title="CustomControlWindow" Height="450" Width="800">
    <Grid>
        <xamlhost:WindowsXamlHost InitialTypeName="UwpControlLibrary.MyCustomControl" />
    </Grid>
</Window>

Again, this looks very similar to the Windows Forms output.

Third Party Control

The ThirdPartyControlWindow is very similar to the CustomControlWindow in that we can just specify the InitialTypeName attribute. In this case using the class Telerik.UI.Xaml.Controls.Input.RadCalendar.

The ThirdPartyControlWithStyleWindow is slightly more complex as we need to establish the data binding. Here’s the XAML and code behind.

<Window x:Class="WPFIslandsDemo.ThirdPartyControlWithStyleWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:xamlhost="clr-namespace:Microsoft.Toolkit.Wpf.UI.XamlHost;assembly=Microsoft.Toolkit.Wpf.UI.XamlHost"
        mc:Ignorable="d"
        Title="ThirdPartyControlWithStyleWindow"
        Height="450"
        Width="800">
    <StackPanel>
        <TextBlock Text="Pick as date:" />
        <xamlhost:WindowsXamlHost InitialTypeName="UwpControlLibrary.CustomCalendar" />
        <DatePicker SelectedDate="{Binding MyDate, Mode=TwoWay}" />

        <TextBlock Text="{Binding DateAsString}" />
    </StackPanel>
</Window>

public ThirdPartyControlWithStyleWindow()
{
    InitializeComponent();

    DataContext = new DataModel();
}

Notice how simple this is – the DataContext is applied to both the WPF and UWP controls, making it possible to easily integrate controls from both frameworks into the same layout with minimal fuss.

And that’s how easy it is to integrate UWP controls with both Windows Forms and WPF applications. The source code for this walkthrough is available on GitHub

XAML Back to Basics #12: Dialogs

How to implement a data bound dialog box

XAML Basics Series Index Page

The next post in the series originally written by Beatriz Stollnitz. Original post available on Github.

How to implement a data bound dialog box

In this post I will show you how to implement a dialog box using data binding. While this may seem like a straightforward task at first glance, when using data binding it can be tricky to get the “OK” button of a dialog to commit the user’s changes and the “Cancel” button to discard them.

One possible approach is to allow the bindings to update the data source as the user is typing information into the dialog box, then undo the work done by the bindings if the user happens to press the “Cancel” button. I don’t like the “Cancel” scenario of this approach because the data source acquires values that are only kept temporarily. Besides, it requires additional logic in the application to remember the data when the dialog box opens and to revert back to that data if the user presses “Cancel”. This is a lot of work and quite confusing. Fortunately, there is an easier way to get the job done – by changing the value of UpdateSourceTrigger in your Bindings.

The main Window in this sample has a Button that launches the dialog box, and Labels that show the contents of the data source. When this app is loaded the Labels are empty. When the user opens the dialog box, enters data in the TextBoxes and presses OK, the Labels in the main Window display the data just entered. If the user presses Cancel instead, the Labels should remain empty.

<Button Click="ShowDialog" Width="100" Height="30">Show Dialog</Button>
<Label Grid.Row="0" Grid.Column="1" Name="Name" Margin="5" Content="{Binding Source={StaticResource source}, Path=Name}"/>
<Label Grid.Row="1" Grid.Column="1" Name="Comment" Margin="5" Content="{Binding Source={StaticResource source}, Path=Comment}"/>

private void ShowDialog(object sender, RoutedEventArgs args) { Dialog1 dialog = new Dialog1(); dialog.Owner = this; dialog.ShowDialog(); }

The dialog box contains TextBoxes data bound to the same data as the Labels and OK/Cancel Buttons. This is the markup that goes in the dialog box:

<TextBox Grid.Row="0" Grid.Column="1" Name="Name" Margin="5" Text="{Binding Source={StaticResource source}, Path=Name, UpdateSourceTrigger=Explicit}"/>
<TextBox Grid.Row="1" Grid.Column="1" Name="Comment" Margin="5" Text="{Binding Source={StaticResource source}, Path=Comment, UpdateSourceTrigger=Explicit}"/>
<Button Click="OKHandler" IsDefault="true" Margin="5">OK</Button>
<Button IsCancel="true" Margin="5">Cancel</Button>

The Binding object allows us to specify how to trigger updates to the data source through its UpdateSourceTrigger property. The default update trigger for the TextBox’s Text DP is “LostFocus”, which means that the data the user types is updated to the source when the TextBox loses focus. This is not what we want for this scenario though; we want the data to be updated only when the user presses the “OK” button. By changing the update trigger to “Explicit”, the data will not be updated to the source until we explicitly call the “UpdateSource()” method on the BindingExpression, which we can do in the handler for the “OK” button:

private void OKHandler(object sender, RoutedEventArgs args)
{
    BindingExpression bindingExpressionName = BindingOperations.GetBindingExpression(Name, TextBox.TextProperty);
    bindingExpressionName.UpdateSource();
    BindingExpression bindingExpressionComment = BindingOperations.GetBindingExpression(Comment, TextBox.TextProperty);
    bindingExpressionComment.UpdateSource();
    this.DialogResult = true;
}

The logic for the “OK” button is simple, but the “Cancel” is even simpler. Because we never allowed the values typed to update to the source, all we have to do is close the Window. This can be done by simply setting IsCancel=true on the Cancel button, no event handler necessary.

Here is a screen shot of the completed sample:

WPF Source Code

WPF

UWP Notes
The WPF code relies on what seems like magic in order to update the content on the Main page. For those familiar with XAML binding you may be surprised that this example works given that the DataSource class doesn’t implement INotifyPropertyChanged. There are definitely smarts built into WPF that will update all elements bound to the same source if one of its properties are updated, such as the case in this example. This does NOT work with UWP where you have to be explicit about raising PropertyChanged event in order for any elements bound to the source to update.

UWP also requires that the Mode of the Binding for the two TextBox elements be set to TwoWay

Additionally, the UWP ContentDialog has built in primary and secondary buttons to encourage a standard look and feel for dialogs.

Uno Notes
Currently the UpdateSourceTrigger attribute of the Binding expression isn’t respected. This means that any changes made in the dialog will be updated in the main page.

UWP Source Code

UWP

WinUI Notes

WinUI for Desktop, whilst respecting the UpdateSourceTrigger attribute, ends up looking more like UWP than WPF. The dialog needs to inherit from ContentDialog. There’s a need to explicitly set the XamlRoot and for some reason typing in the TextBox elements doesn’t work.

WinUI for UWP has issues with databound properties being changed in a ContentDialog and having to update on the main page. This is most likely a prerelease issue. Ironically the Uno platforms all update the content but again ignore the UpdateSourceTrigger attribute.

WinUI with Uno and WinUI Desktop Source Code

WinUI – Desktop

XAML Back to Basics #11: Multiple Linked Lists

How to synchronize ListBoxes displaying three levels of hierarchical data

XAML Basics Series Index Page

The next post in the series originally written by Beatriz Stollnitz. Original post available on Github.

How to synchronize ListBoxes displaying three levels of hierarchical data

The master-detail scenario with more than 2 levels is very common, and we made sure we have good support for it in WPF. I will show in this post three ways to sync selection of three ListBoxes, each displaying a different level of a hierarchy of data. In this sample, the first ListBox displays a list of mountain ski resorts. When the user selects a ski resort, the second ListBox gets updated with several lifts from that mountain. By selecting a particular lift, the third ListBox gets updated with ski runs that can be taken down from the top of that lift.

Here is the approach some developers might take when trying to get this scenario to work:

<Window.Resources>
    <local:Mountains x:Key="mountains" />
    <CollectionViewSource Source="{StaticResource mountains}" x:Key="cvs" />
</Window.Resources>
<ListBox ItemsSource="{Binding Source={StaticResource cvs}}" DisplayMemberPath="Name" Name="lb1" />
<ListBox ItemsSource="{Binding Source={StaticResource cvs}, Path=Lifts}" DisplayMemberPath="Name" Name="lb2" />
<ListBox ItemsSource="{Binding Source={StaticResource cvs}, Path=Lifts/Runs}" Name="lb3" />

Unfortunately this does not work as expected: lb1 and lb2 are in sync but lb3 is not. When creating a custom view on top of a collection by using CollectionViewSource, selection and currency are in sync by default. This is why lb1 and lb2 are in sync in this scenario. This markup does not use a custom view for the Lifts collection though – a default view is created internally instead. Default views do not have currency and selection in sync by default, which is the reason why lb2 and lb3 don’t sync.

There are at least three ways to have the three ListBoxes in sync.

The most obvious solution is to create a second CollectionViewSource for the Lifts collection and bind lb2 and lb3 to it:

<Window.Resources>
    (...)
    <CollectionViewSource Source="{Binding Source={StaticResource cvs}, Path=Lifts}" x:Key="cvs2"/>
</Window.Resources>
<ListBox ItemsSource="{Binding Source={StaticResource cvs}}" DisplayMemberPath="Name" Name="lb1" />
<ListBox ItemsSource="{Binding Source={StaticResource cvs2}}" DisplayMemberPath="Name" Name="lb2" />
<ListBox ItemsSource="{Binding Source={StaticResource cvs2}, Path=Runs}" Name="lb3" />

The second solution is to ignore CollectionViewSource, and let WPF create default views internally for us. Because default views don’t sync selection and currency by default, we have to override the default behavior by setting IsSynchronizedWithCurrentItem to true:

<ListBox ItemsSource="{Binding Source={StaticResource mountains}}" DisplayMemberPath="Name" IsSynchronizedWithCurrentItem="True" Name="lb1" />
<ListBox ItemsSource="{Binding Source={StaticResource mountains}, Path=Lifts}" DisplayMemberPath="Name" IsSynchronizedWithCurrentItem="True" Name="lb2" />
<ListBox ItemsSource="{Binding Source={StaticResource mountains}, Path=Lifts/Runs}" IsSynchronizedWithCurrentItem="True" Name="lb3" />

The third solution is to rely simply on the items displayed in the previous ListBox. Binding allows us to link not only to XML and objects, but also to other elements in the logical tree. To accomplish this scenario, we set the ElementName property of Binding to the Name of the source element (instead of setting Binding’s Source property), and the Path to the property of the element we’re interested in.

<ListBox ItemsSource="{Binding Source={StaticResource mountains}}" DisplayMemberPath="Name" Name="lb1" IsSynchronizedWithCurrentItem="True"/>
<ListBox DataContext="{Binding ElementName=lb1, Path=Items}" ItemsSource="{Binding Path=Lifts}" DisplayMemberPath="Name" Name="lb2" IsSynchronizedWithCurrentItem="True"/>
<ListBox DataContext="{Binding ElementName=lb2, Path=Items}" ItemsSource="{Binding Path=Runs}" Name="lb3" IsSynchronizedWithCurrentItem="True"/>

In the markup above, we set the DataContext of the second ListBox to the first ListBox’s Items property. Because DataContext is not expecting a collection, internally the binding engine returns the current item of that collection. We can then bind the ItemsSource to the Lifts property of the current Mountain, which returns the list we want.

This sample uses CLR objects as the data source. When using an XML data source, note that only the third solution above will work (for reasons I won’t go into here).

Here is a screen shot of the completed sample:

WPF Source Code

WPF

UWP/Uno/WinUI Notes

It’s highly recommended that you do NOT use the ISynchronizedWithCurrentItem as it’s likely to cause runtime errors. The Items collection doesn’t maintains a CurrentItem, instead use SelectedItem on the ListBox/ListView instead. It’s recommended to use the ListView control rather than the older ListBox control

UWP/Uno Source Code

UWP
WASM

WinUI with Uno and WinUI Desktop Source Code

WinUI – Desktop

XAML Back to Basics #10: List and Details

List-detail scenario

XAML Basics Series Index Page

The next post in the series originally written by Beatriz Stollnitz. Original post available on Github. Original post used terminology of Master-Detail, which has been changed to List-Detail to more accurately reflect what it represents.

List-detail scenario

In the simplest list-detail scenario, clicking a particular item of an ItemsControl causes the details about that item to be displayed in another control. For example, an application may display a list of customer names in a ListBox, and clicking a particular customer causes TextBlocks to be updated with the address, phone number and date of birth of that customer.

In this post I will use a data source with the planets of the solar system: clicking on the name of a planet in the ListBox causes its picture and information to be displayed in a templated ContentControl. The ListBox plays the role of the list and the ContentControl presents the detail.

In the resources section of the Window, I have an XmlDataProvider with the planet data and a CollectionViewSource with the Source property bound to the provider. Here is the markup for the ListBox bound to the CollectionViewSource:

<!-- list -->
<ListBox ItemsSource="{Binding Source={StaticResource cvs}}" DisplayMemberPath="@Name" Padding="5" Margin="0,0,5,0"/>

I also need a ContentControl, which is used to display the details of the selected item. The markup below may seem a little strange at first: we are binding a ContentControl (which displays a single item) to a collection of items? (Notice that its Content’s Binding is the same as the Binding in the ListBox’s ItemsSource.) This markup works fine because the data binding engine is smart enough to distinguish between the two targets. When binding an ItemsControl to a collection we get the collection; when binding a ContentControl to a collection we get the current item of that collection. This is what makes the list-detail scenario so simple in WPF.

<!-- detail -->
<ContentControl ContentTemplate="{StaticResource detailTemplate}" Content="{Binding Source={StaticResource cvs}}"/>

To specify how the details of the planet data should be displayed in the ContentControl, we use a DataTemplate. The following markup shows the data-binding specific parts of the DataTemplate. Notice that because I am binding to XML, the Binding is using XPath instead of Path.

<DataTemplate x:Key="detailTemplate">
    (...)
    <Image Source="{Binding XPath=Image, Converter={StaticResource stringToImageSource}}" />
    (...)
    <StackPanel Orientation="Horizontal" Margin="5,5,5,0">
        <TextBlock Text="Orbit: " FontWeight="Bold" />
        <TextBlock Text="{Binding XPath=Orbit}" />
    </StackPanel>
    <StackPanel Orientation="Horizontal" Margin="5,0,5,0">
        <TextBlock Text="Diameter: " FontWeight="Bold"/>
        <TextBlock Text="{Binding XPath=Diameter}" />
    </StackPanel>
    <StackPanel Orientation="Horizontal" Margin="5,0,5,5">
        <TextBlock Text="Mass: " FontWeight="Bold"/>
        <TextBlock Text="{Binding XPath=Mass}" />
    </StackPanel>
    (...)
</DataTemplate>

Here is a screen shot of the completed sample:

WPF Source Code

WPF

Uno Notes

Because the CollectionViewSource isn’t support across the different Uno platforms, I’ve data bound the SelectedItem on the ListView to a property on the MainPage, which in turn updates the Content property on the ContentControl. This only applies to the Non-UWP platforms.

You’ll also note that unlike in my previous post, where I used an XmlElementConverter, in this example I’ve used an XmlWrapper. This leads to binding expressions that are closer to what’s in the original post as it allows for traversing the element and attributes on the Xml that’s loaded from the associated data file.

UWP Source Code

UWP

WinUI Notes

Whilst WinUI for Desktop is very close to WPF, it doesn’t include the XmlDataProvider. Similar to the Uno project, we’ve used an embedded xml file instead of the inline data.

WinUI with Uno and WinUI Desktop Source Code

WinUI-Desktop

Fix: WinUI Preview 2 with Visual Studio 2019 Error Creating Project

If you’ve recently upgraded to the latest Visual Studio preview (Preview 2 of VS 16.8) then you may run into issues if you attempt to create a new WinUI for Desktop project. You’ll see a prompt similar to the following and only the packaging project will get created. The full text from the error is … Continue reading “Fix: WinUI Preview 2 with Visual Studio 2019 Error Creating Project”

If you’ve recently upgraded to the latest Visual Studio preview (Preview 2 of VS 16.8) then you may run into issues if you attempt to create a new WinUI for Desktop project. You’ll see a prompt similar to the following and only the packaging project will get created.

The full text from the error is as follows.

-------------------------
Microsoft Visual Studio
-------------------------
A problem was encountered creating the sub project 'MasterDetail.Desktop'. The expression "[Microsoft.Build.Utilities.ToolLocationHelper]::GetPlatformSDKLocation('', 10.0.18362.0)" cannot be evaluated. Parameter "targetPlatformIdentifier" cannot have zero length. C:\Program Files (x86)\Microsoft Visual Studio\2019\Preview\MSBuild\Current\Bin\Microsoft.Common.CurrentVersion.targets

The solution to this issue has been covered over at this github issue. The main steps are:

  • Download and extract Install-Dotnet script
  • Open an elevated command or powershell terminal window and run Install-DotNet -version 5.0.100-preview.5.20279.10
  • Open Visual Studio and create an empty solution

Note: If you attempt to simply create a new solution using the Blank App template, you’ll see the same error as before. You need to have the global.json file in the root folder before the WinUI project is created.

  • Add global.json file with the following content to the root folder of the solution (the same folder as the .sln file):
{
  "sdk": {
    "version": "5.0.100-preview.5.20279.10"
  }
}
  • Add project using the “Blank App, Packaged (WinUI in Desktop)” template

At this point you’ll be thinking to yourself that everything is looking good and you’ve successfully created your WinUI project. Unfortunately, when you go to run your project you’ll see an error similar to.

Severity Code Description Project File Line Suppression State
Error NETSDK1005 Assets file 'c:\temp\App1\App1\obj\project.assets.json' doesn't have a target for '.NETCoreApp,Version=v5.0'. Ensure that restore has run and that you have included 'net5.0' in the TargetFrameworks for your project. App1 C:\Program Files\dotnet\sdk\5.0.100-preview.5.20279.10\Sdks\Microsoft.NET.Sdk\targets\Microsoft.PackageDependencyResolution.targets 234

If you read the GitHub issue, you’ll note that it very clearly says “you need to use VS16.7.2”.

Make sure you are using Visual Studio 16.7, not Visual Studio 16.8 preview 2. The point of this post is that if you have both installed side by side, the installation of preview 2 will break your ability to run WinUI for Desktop projects. Adding the global.json file to your solution folder will fix this issue.

Thinking Out Loud: Mvvm Navigation for XAML Frameworks such as Xamarin.Forms, UWP/WinUI, WPF and Uno

One of the things that’s often given me pause for thought is the approach we take to navigation within applications. For the purpose of this post I’m going limit the scope to just XAML based applications (XF/Maui, UWP/WinUI/Uno, WPF). In all of these application platforms there is a built in capability to navigate between pages. … Continue reading “Thinking Out Loud: Mvvm Navigation for XAML Frameworks such as Xamarin.Forms, UWP/WinUI, WPF and Uno”

One of the things that’s often given me pause for thought is the approach we take to navigation within applications. For the purpose of this post I’m going limit the scope to just XAML based applications (XF/Maui, UWP/WinUI/Uno, WPF). In all of these application platforms there is a built in capability to navigate between pages. However, once we introduce view models, we want to be able to drive navigation from our view models. In this post I’m going to go through a bit of a thought journey to look at some of the existing strategies for view model navigation, discuss the pros and cons, and then look at whether we can build an alternative.

Rather than try to cover all the XAML platforms at every point along the way, I’m going to use a newly created @unoplatform application throughout this post. Towards the end we’ll look at whether we can adapt what we’ve created to the other XAML platforms – if we’ve done the job well, it should be a relatively easy thing to do.

Basic Page Navigation

As part of creating our project, MvvmNavigation, we’re given a MainPage, which is clearly the starting point of our application. To begin with, let’s:

  • Create another page, SecondPage
  • Add a Button to MainPage and an event handler that will simply navigate to SecondPage
  • Add a Button to SecondPage and an event handler to navigate back to MainPage
  • Add a TextBlock to both pages to give the pages a title

The XAML for MainPage

<Page x:Class="MvvmNavigation.MainPage"
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      xmlns:local="using:MvvmNavigation"
      xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
      xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
      mc:Ignorable="d">
    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
        <TextBlock Text="Main Page"
                   Margin="100"
                   FontSize="50"
                   HorizontalAlignment="Center" />
        <Button Content="Go to Second Page"
                Click="GoToSecondPageClick"
                VerticalAlignment="Bottom"
                HorizontalAlignment="Center"
                Margin="100" />
    </Grid>
</Page>

The GoToSecondPageClick event handler in the codebehind of MainPage

private void GoToSecondPageClick(object sender, RoutedEventArgs e)
{
    Frame.Navigate(typeof(SecondPage));
}

The XAML for SecondPage

<Page x:Class="MvvmNavigation.SecondPage"
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      xmlns:local="using:MvvmNavigation.Shared"
      xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
      xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
      mc:Ignorable="d"
      Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
        <TextBlock Text="Second Page"
                   Margin="100"
                   FontSize="50"
                   HorizontalAlignment="Center" />
        <Button Content="Go back to Main Page"
                Click="GoBackClick"
                VerticalAlignment="Bottom"
                HorizontalAlignment="Center"
                Margin="100" />
    </Grid>
</Page>

The GoBackClick event handler in the codebehind of SecondPage

private void GoBackClick(object sender, RoutedEventArgs e)
{
    Frame.GoBack();
}

And final a screenshot of each page that we’re navigating between.

What we can see from this example is that in a UWP/Uno application, navigating between pages is done by calling navigation methods on the Frame. As an aside, a UWP application can not only support multiple Windows, it can also support multiple frames. This can be very handy if you’re building complex desktop applications where you want to be able to control multiple page stacks. For the moment we’ll keep things simple and just consider a single window, single frame application.

ViewModel Navigation

Let’s augment our example by adding in some view models. To ensure we don’t pollute our view models with UI/framework specific code, we’ll add our view models to a separate .NET Standard class library, MvvmNavigation.Core. In this case I’ll create a view model for each page we currently have.

public class MainViewModel
{
    public string Title { get; } = "Main Page - VM";
}

public class SecondViewModel
{
    public string Title { get; } = "Second Page - VM";
}

One thing you’ll notice about a lot of mvvm frameworks is that they all seem to have their own base class. Where possible, I’m going to try to avoid this, in order to keep our view models as simple as possible. If you want to have bindings update the viewmodel will need to implement INotifyPropertyChanged, so having a base class with that implementation is something you’ll want to add. For the moment we’ll keep the view models as simple as possible.

Now let’s connect our view models to our pages. We’ll keep this really simple by creating the view models inline in the page as part of setting the DataContext. We’ll also update the Text on the TextBlock to bind to the Title property on the view model.

<Page x:Class="MvvmNavigation.MainPage" ... >
    <Page.DataContext>
        <vms:MainViewModel />
    </Page.DataContext>
    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
        <TextBlock Text="{Binding Title}" ... />
    ...
</Page>

With the basic setup done, we can start to think about navigation. Firstly, let’s set the scene as to why the current navigation (i.e. controlled invoked in the code behind of the page) is a problem. In our MainViewModel, let’s say we have a method that does something, DoSomething.

public async Task<int> DoSomething()
{
    var rnd = new Random().Next(1000);
    await Task.Delay(rnd);
    if (rnd % 2 == 0)
    {
        // Do one thing
    }
    else
    {
        // Do something else
    }
    return rnd;
}

After delaying for a random amount of time, it decides to do one of two things. Let’s assume that if the random number is even, the app should navigate to SecondPage. However, currently the view model doesn’t have a way to do this. We could augment the return value to return a flag to indicate that it should navigate. Alternatively, we could move the decision logic into the code behind. I don’t like either of these options and it would be much clearer if the view model had a way to invoke navigation itself.

This leads us to a quick discussion on how this is currently implemented by different frameworks. Typically the solution is to pass in a service, call it something like INavigationService, into the constructor for the view model. When the view model wants to navigate it calls a method, such as Navigate, on the service. Depending on which framework it is, the parameter might be a url or it could be the type of view model to navigate to (i.e. navigate to the page that is associated with the specified view model type).

My issue with this approach is two fold. Firstly, we’ve added a dependency to the view model that doesn’t really assist the view model with what it needs to do (controlling the state for the view; loading and updating data that’s data bound to the UI etc). Secondly, we’ve added in an implicit linkable between the current view model and the view model that’s being navigated to.

If I had to pick between navigating using a url versus the type of a view model, my preference is the type of a view model, purely because this can be enforced with type safety. However, my point is that in both cases there’s an implied link that’s only visible if you walk the code of the view model.

Event Based Navigation

Instead of using an injected navigation service, what if we simply expose an event on the view model for when we need navigation to take place. For example, our MainViewModel could look like

public class MainViewModel
{
    public event EventHandler ViewModelDone;
    public async Task<int> DoSomething()
    {
        var rnd = new Random().Next(1000);
        await Task.Delay(rnd);
        if (rnd % 2 == 0)
        {
            ViewModelDone?.Invoke(this, EventArgs.Empty);
        }
        ... 
    }
}

Note that I didn’t call the event NavigateToSecondPage because this would again start to build in that implicit linkage between the view models/pages. The point is that the MainViewModel doesn’t, and shouldn’t, care about what’s going to happen next, it just knows that it’s time is up and it’s job is done.

The only thing now, is that we need something to listen to the event. For the moment this will be the MainPage:

protected override void OnNavigatedTo(NavigationEventArgs e)
{
    base.OnNavigatedTo(e);
    (DataContext as MainViewModel).ViewModelDone += MainViewModelDone;
}

protected override void OnNavigatedFrom(NavigationEventArgs e)
{
    (DataContext as MainViewModel).ViewModelDone -= MainViewModelDone;
    base.OnNavigatedFrom(e);
}

private void MainViewModelDone(object sender, EventArgs e)
{
    Frame.Navigate(typeof(SecondPage));
}

private void GoToSecondPageClick(object sender, RoutedEventArgs e)
{
    (DataContext as MainViewModel)?.DoSomething();
}

Whilst we’ve kept our MainViewModel clear of any dependency/relationship with SecondPage or SecondViewModel, what we’ve ended up with is a bunch of wiring and navigation logic in our MainPage.

Abstracting Navigation Logic

Let’s step away from the specifics of how events and navigation logic works and think about what we want to achieve. Essentially we need a way of defining a set of behaviours such that when an event is raised on a particular object, the application will navigate to a particular page. So, for each behaviour we want to define, we need a way to declare the event that is going to be listened for and the action that should take place when that event is triggered.

The complexity in abstracting the navigation logic comes when you attempt to declare these behaviours at an application level. At a page level you can simply attach and detach event handlers, similar to what we’ve just done with MainPage in the OnNavigatedTo and OnNavigatedFrom methods. At an application level, you don’t have the instances of the pages until the user has navigated to them. As such, we’ll need to hook into the navigation event on the Frame and wire/unwire the event handlers.

Here’s a summary of the pieces we need:

  • Behaviour class – this will define the event to listen to and the action to execute
  • NavigationRoutes class – this will hold the list of behaviours for the application
  • OnNavigation method – this is the event handler for the Navigated event on the root frame of the application

Let me try to walk through these in a way that hopefully makes sense. We’ll start with the behaviour class.

public class Behaviour:IApplicationBehaviour
{
    public Action<object> Init { get; set; }
    public Action<object> Deinit { get; set; }

    private Behaviour() { }

    public static Behaviour Create<T, THandler>(Action<T, THandler> init, Action<T, THandler> deinit, THandler action)
    {
        return new Behaviour()
        {
            Init = obj => init((T)obj, action),
            Deinit = obj => deinit((T)obj, action)
        };
    }
}

public interface IApplicationBehaviour
{
    Action<object> Init { get; set; }
    Action<object> Deinit { get; set; }
}

According to what I said earlier, the behaviour class needs to define an event and an action, yet the implementation doesn’t really look like it has either. It does, they’re just masked a little.

Firstly, the challenge with an event in C# is that you can’t easily just pass the event object around and attach/detach from it. We could have just switched it out for an Action or some other delegate but that would make the programming model for the view model just a bit weird. Alternatively we could have used some hocky reflection to get the job done but … well just, no… I don’t want to be doing reflection due to the inherit overhead and potential linker issues it might create.

Instead of passing an event object around, we instead pass in an Action for wiring and unwiring the event. For example the wiring action would look something like (vm, act) => vm.ViewModelDone += act.

Next, we need the action that’s going to be invoked. Well this is easily identifiable in the Create method as the third parameter but then what do we do with it? Rather than hold a direct reference to the action, we simply combine it with the init and deinit Action delegates and assign them to the public Init and Deinit methods.

Initially this looks a bit strange as the signature on the Init and Deinit methods are Action<object> instead of Action<T>, and then we’re assigning a delegate that coerces the object into T – why not just make them Action<T>? The answer to this comes if you look at the interface IApplicationBehaviour that defines Init and Deinit methods, both as Action<object>. As you’ll see shortly, we’re going to have a collection of these behaviours defined for a variety of different pages (i.e. T). Rather than attempt to keep these as strongly typed behaviours, we’re simply going to use an interface which doesn’t include the type argument.

You might be wondering whether the type coercion is safe? Well the short answer is no, it might not be. However, as you’ll see shortly, one of the reasons why we don’t expose either a public constructor on the Behaviour class, nor expose the collection of Behaviours directly is to ensure that this type coercion is always valid.

Next up is the NavigationRoutes class, which is essentially just a wrapper around a collection of Behaviour instances.

public class NavigationRoutes
{
    private IList<Tuple<Type, IApplicationBehaviour>> Behaviours { get; } = new List<Tuple<Type, IApplicationBehaviour>>();

    public NavigationRoutes Register<T, THandler>(Action<T, THandler> init, Action<T, THandler> deinit, THandler action)
    {
        Behaviours.Add(new Tuple<Type, IApplicationBehaviour>(typeof(T), Behaviour.Create(init, deinit, action)));
        return this;
    }

    public void Wire(object page)
    {
        var typeOfPage = page.GetType();
        var behaviours = Behaviours.Where(x => x.Item1 == typeOfPage).Select(x=>x.Item2);
        foreach (var behaviour in behaviours)
        {
            behaviour.Init(page);
        }
    }

    public void Unwire(object page)
    {
        var typeOfPage = page.GetType();
        var behaviours = Behaviours.Where(x => x.Item1 == typeOfPage).Select(x => x.Item2);
        foreach (var behaviour in behaviours)
        {
            behaviour.Deinit(page);
        }
    }
}

You might be wondering why we didn’t just use a Dictionary. The answer is so that we can support multiple Behaviour definitions per page. I’m also certain there are more optimal ways to structure the Wire and Unwire methods but they’ll do for what we need right now. They simply iterate through the collection of behaviours and invoke the Init or Deinit method on behaviours that are registered for the specified page.

We also need to define an instance of the NavigationRoutes class, which for the moment we’ll place in App.xam.cs.

public NavigationRoutes Routes { get; } = new NavigationRoutes()
    .Register<MainViewModel, EventHandler>(
                (vm, act) => vm.ViewModelDone += act,
                (vm, act) => vm.ViewModelDone -= act,
                (s, e) => (Window.Current.Content as Frame).Navigate(typeof(SecondPage)))
    .Register<SecondViewModel, EventHandler>(
                (vm, act) => vm.ViewModelDone += act,
                (vm, act) => vm.ViewModelDone -= act,
                (s, e) => (Window.Current.Content as Frame).GoBack());

As you can see from this code we’re registering the ViewModelDone on both MainViewModel and SecondViewModel. For the MainViewModel we’re registering an Action that navigates to SecondPage. For the SecondViewModel we’re registering an Action that calls GoBack on the Frame.

The last thing to do is to intercept the Navigated event on the Frame in order to wire/unwire the behaviours.

private object PreviousPage { get; set; }
private void OnNavigated(object sender, NavigationEventArgs e)
{
    if (PreviousPage != null)
    {
        Routes.Unwire((PreviousPage as Page).DataContext);
        PreviousPage = null;
    }

    PreviousPage = e.Content;
    Routes.Wire((PreviousPage as Page).DataContext);

}

Here you can see why we needed the Init and Deinit methods on the IApplicationBehaviour interface to be Action<object> – The Content property on the NavigationEventArgs is simply an object which we are casting it to a Page and extracting the DataContext, which is also untyped. Note that again, this is very rough at this point and obviously needs some null checking before it would be considered fit for use in production.

Testing Navigation Logic

Whilst we’ve successfully abstracted the navigation logic away from the view models, it’s still very closely tied to platform implementation. By this I mean that the actions we’ve registered rely on having a reference to the Frame and they specify the actual navigation action to carry out eg Navigate(typeof(MainPage)) or GoBack. If we wanted to write tests to validate our navigation logic, we can’t easily, unless they were UI tests that literally tested the behaviour of the application (I’m not saying these aren’t important, they’re just not something we can write as unit tests).

The next step in our journey is to put in place an abstraction of what we mean by navigation so that we can replace the Navigate and GoBack method calls with something that can be tested. If we look to existing mvvm frameworks for suggestions, we’ll remember that they typically pass a navigation service into the viewmodels. What if we do something similar – we create a navigation service interface that can be passed into our Actions. Of course, this will then require a per-platform implementation for how each navigation method should be carried out.

This sounds simply but the first issue we’ll come up against is that the Navigate method for UWP takes a type parameter being the type of the page to navigate to. If we’re abstracting navigation away from the UI platform, we need to define navigation based on types that aren’t associated with any UI platform (i.e. not MainPage or SecondPage). The logical conclusion is that in order to navigate to SecondPage, we should simply attempt to navigate to SecondViewModel and let the platform implementation worry about how to translate this to SecondPage.

Let’s start by defining the interface for our INavigationService. At this stage I’m not going to place any type constraints on the TViewModel since one of our goals is to avoid imposing a particular base class or interface on the view models we define.

public interface INavigationService
{
    Task Navigate<TViewModel>();
    Task GoBack();
}

Next up, the implementation of the NavigationService for Windows (UWP/Uno).

public class WindowsNavigationService:INavigationService
{
    private Frame NavigationFrame { get; }
    private NavigationRoutes Routes { get; }
    public IDictionary<Type, Type> ViewModelToPageMap { get; } = new Dictionary<Type, Type>();

    public WindowsNavigationService(Frame navigationFrame, NavigationRoutes routes)
    {
        NavigationFrame = navigationFrame;
        NavigationFrame.Navigated += OnNavigated;
        Routes = routes;
    }

    public async Task GoBack()
    {
        NavigationFrame.GoBack();
    }

    public async Task Navigate<TViewModel>()
    {
        NavigationFrame.Navigate(ViewModelToPageMap[typeof(TViewModel)]);
    }

    public WindowsNavigationService RegisterForNavigation<TPage, TViewModel>() where TPage : Page
    {
        ViewModelToPageMap[typeof(TViewModel)] = typeof(TPage);
        return this;
    }

    private object PreviousPage { get; set; }
    private void OnNavigated(object sender, NavigationEventArgs e)
    {
        if (PreviousPage != null)
        {
            Routes.Unwire((PreviousPage as Page).DataContext);
            PreviousPage = null;
        }

        PreviousPage = e.Content;
        Routes.Wire(this,(PreviousPage as Page).DataContext);
    }
}

There’s three main sections to the implementation. Firstly, as you’d expect the constructor is expecting a Frame, which will be used to implement the Navigate and GoBack methods. In order to call Navigate on the Frame there is a translation between the TViewModel type parameter on the Navigate method to the type of Page to navigate to. This is done by looking up the ViewModelToPageMap dictionary. This leads us to the second section, which is the RegisterForNavigation method that simply adds a mapping into the ViewModelToPageMap dictionary. It might seem weird that we’re returning the instance of the WindowsNavigationService but this is simply to allow for a more fluent way of calling the RegisterForNavigation method, which we’ll see shortly.

The last section of the implementation is actually code we’ve seen before, which simply wires and unwires the behaviours as the Frame navigates between pages. This was previously in the App.xaml.cs but makes sense to be incorporated into the WindowsNavigationServices.

In the App.xaml.cs, rather than attaching an event handler to the Navigated event on the rootFrame, we’re instead going to create an instance of the WindowsNavigationService and call the RegisterForNavigation method for each page/view model pair we have.

rootFrame = new Frame();

rootFrame.NavigationFailed += OnNavigationFailed;
var navService = new WindowsNavigationService(rootFrame, Routes)
    .RegisterForNavigation<MainPage, MainViewModel>()
    .RegisterForNavigation<SecondPage, SecondViewModel>();

The last thing we need to do is to change the implementation of the NavigationRoutes class in order to leverage the INavigationService. So far as part of registering each behaviour we’ve avoided placing any constraints on the event or the action that’s passed into the Register method. The type of the event in the init and deinit methods (i.e. THandler) simply has to match the type of the Action. The challenge with wanting to pass in the INavigationService is that we risk enforcing some sort of constraint on THandler. For example we could require THandler to inherit, or be an instance of, EventHandler<INavigationService>. If we did this, all of a sudden we’re leaking the concept of navigation back into our view model.

The work around for this is that instead of passing in an Action into the register method, we’re going to pass a function that accepts an INavigationService and returns an Action. Essentially this is a sneaky way for us to wrap an instance of the INavigationService in a way that makes it accessible to the Action, without affecting the signature of the Action itself. You can see from the following code that the signature of the event and Action is still EventHandler.

private NavigationRoutes Routes { get; } = new NavigationRoutes()
    .Register<MainViewModel, EventHandler>(
                (vm, act) => vm.ViewModelDone += act,
                (vm, act) => vm.ViewModelDone -= act,
                (nav) => (s, e) => nav.Navigate<SecondViewModel>())
    .Register<SecondViewModel, EventHandler>(
                (vm, act) => vm.ViewModelDone += act,
                (vm, act) => vm.ViewModelDone -= act,
                (nav) => (s, e) => nav.GoBack());

Now that I’ve given the endgame away, let’s take a quick look at the implementation. Firstly, the Behaviour implementation has changed. Due to an increase in complexity, it now makes sense to capture the init, deinit and actionFactory parameters. The public interface for IApplicationBehaviour has also been updated.

public class Behaviour<T, THandler> :IApplicationBehaviour
{
    private Action<T, THandler> Init { get; set; }
    private Action<T, THandler> Deinit { get; set; }

    private Func<INavigationService, THandler> ActionFactory { get; set; }

    private T attachedEntity;
    private THandler action;

    public void Attach(INavigationService navService, object entity)
    {
        if(attachedEntity!=null)
        {
            Detach();
        }

        action = ActionFactory(navService);
        attachedEntity = (T)entity;
        Init(attachedEntity, action);
    }

    public void Detach()
    {
        if (attachedEntity == null)
        {
            return;
        }

        Deinit(attachedEntity, action);
    }

    private Behaviour() { }

    public static Behaviour<T, THandler> Create(Action<T, THandler> init, Action<T, THandler> deinit,Func<INavigationService, THandler> actionFactory)
    {
        return new Behaviour<T, THandler>()
        {
            Init = init,
            Deinit = deinit,
            ActionFactory=actionFactory
        };
    }
}

public interface IApplicationBehaviour
{
    void Attach(INavigationService navService, object entity);
    void Detach();
}

Then there’s the implementation of NavigationRoutes

public class NavigationRoutes
{
    private IList<Tuple<Type, IApplicationBehaviour>> Behaviours { get; } = new List<Tuple<Type, IApplicationBehaviour>>();

    public NavigationRoutes Register<T, THandler>(Action<T, THandler> init, Action<T, THandler> deinit, Func<INavigationService,THandler> actionFactory)
    {
        Behaviours.Add(new Tuple<Type, IApplicationBehaviour>(typeof(T), Behaviour<T,THandler>.Create(init, deinit, actionFactory)));
        return this;
    }

    public void Wire(INavigationService navigationService, object page)
    {
        var typeOfPage = page.GetType();
        var behaviours = Behaviours.Where(x => x.Item1 == typeOfPage).Select(x=>x.Item2);
        foreach (var behaviour in behaviours)
        {
            behaviour.Attach(navigationService, page);
        }
    }

    public void Unwire(object page)
    {
        var typeOfPage = page.GetType();
        var behaviours = Behaviours.Where(x => x.Item1 == typeOfPage).Select(x => x.Item2);
        foreach (var behaviour in behaviours)
        {
            behaviour.Detach();
        }
    }
}

The main change here is really the Register method signature and that we’re calling Attach and Detach instead of Init and Deinit.

Ok, so we have the basics of a platform agnostic navigation protocol (I’m trying to avoid the overloaded use of framework – what we’ve built is just a couple of classes that help abstract navigation, it’s definitely not a framework…. just yet).

At the beginning I stated that we’d look at the implementation for other platforms but rather than draw this post out any further, let’s list a couple of things for a future post that we need to look at:

  • Implementation of INavigationService for Xamarin.Forms, WPF and WinUI (UWP and/or Desktop)
  • Passing parameters as part of navigation
  • Returning values as part of navigating back
  • Multi-window and potentially multi-region support

Would love feedback on this – check out the repo on github: https://github.com/nickrandolph/mvvmnavigation

XAML Back to Basics #9: CollectionViewSource

How to sync selection of two data bound ListBoxes

XAML Basics Series Index Page

The next post in the series originally written by Beatriz Stollnitz. Original post available on Github.

How to sync selection of two data bound ListBoxes

I will show you two ways of syncing the selection of two data bound ListBoxes.

In the first solution, I will create a custom view over the collection and bind both ListBoxes to it. Views track the current item of their underlying collection, and allow us to sort, group and filter their items. CollectionViewSource is a new class that makes it possible to create a custom view in markup. Because the custom view created tracks the current item of the collection, and currency and selection are in sync in this scenario, binding both ListBoxes to the same view causes their selected items to be in sync.

<Window.Resources>
    <local:GreekGods x:Key="source" />
    <CollectionViewSource Source="{StaticResource source}" x:Key="cvs"/>
</Window.Resources>

<ListBox ItemsSource="{Binding Source={StaticResource cvs}}" DisplayMemberPath="Name"/>
<ListBox ItemsSource="{Binding Source={StaticResource cvs}}" DisplayMemberPath="Name"/>

I will write about how to use CollectionViewSource to sort, group and filter items in a future post.
An alternative way to achieve the same behavior is to set both ItemsSource properties to the data source and set the IsSynchronizedWithCurrentItem properties to true:

<ListBox ItemsSource="{StaticResource source}" IsSynchronizedWithCurrentItem="True" DisplayMemberPath="Name"/>
<ListBox ItemsSource="{StaticResource source}" IsSynchronizedWithCurrentItem="True" DisplayMemberPath="Name"/>

This markup works because when binding to a collection, a view is always created. If you don’t specify one, a default view is created for you internally. Although this view tracks current item the same way the custom one did, when you have a default view currency and selection do not sync by default. The way to override this behavior is by setting the IsSynchronizedWithCurrentItem property to true.

The data team made the default synchronization behavior be different for custom views and default views based on customer feedback. This way, users that are aware of the concept of view and are explicit about it get the synchronization they expect, and the rest of the users don’t.

In the image below, the first and second ListBoxes are bound to the CollectionViewSource and the third and fourth ones have InSynchronizedWithCurrentItem set to true.

WPF Source Code

WPF

Uwp/Uno/WinUI Notes

Whilst the InSynchronizedWithCurrentItem property still exists on the ListBox, attempting to set this causes either a runtime exception (UWP) or a XAML parsing error (WinUI).

CollectionViewSource is supported by UWP and WinUI for UWP but is currently not supported across other platforms via Uno. The application will build and run on those platforms. However, selection on the ListBox will not work and won’t be synchronised across the ListBoxes

UWP Source Code

UWP

WinUI with Uno and WinUI Desktop Source Code

WinUI – Desktop

XAML Back to Basics #8: Simple Bar Graph

How to make a data bound bar graph

XAML Basics Series Index Page

The next post in the series originally written by Beatriz Stollnitz. Original post available on Github.

How to make a data bound bar graph

A very simple bar graph can be created by combining the styling and templating features with a data bound ItemsControl. An ItemsControl is simply a control that displays a list of items. Those items can be anything you want: people, numbers, controls, and so on. If you template each item of an ItemsControl to be a rectangle whose height is bound to numerical data, you have a data bound bar graph.

The data source for this sample is a class with a property called ValueCollection of type ObservableCollection. ObservableCollection implements INotifyCollectionChanged, which means that if items are added/removed/replaced from that collection, the binding engine will be notified of that and the UI will be updated.

This is the markup for the ItemsControl:

<ItemsControl ItemsSource="{Binding Source={StaticResource source}, Path=ValueCollection}" ItemTemplate="{StaticResource template}" Height="130">
    <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
            <StackPanel Orientation="Horizontal" />
        </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>
</ItemsControl>

The default Panel for ItemsControl has vertical orientation, but we want the items to be displayed horizontally – each bar should be to the right of the previous one. To change the panel, we set the ItemsControl’s ItemsPanel property (see my previous blog post for more details on changing the Panel of an ItemsControl).

The template for each item has a rectangle with height bound to the corresponding integer in the data source and a second rectangle with no fill to add some blank space between the bars:

<DataTemplate x:Key="template">
    <StackPanel Orientation="Horizontal" VerticalAlignment="Bottom">
        <Rectangle Height="{Binding}" Width="20" Fill="Red" />
        <Rectangle Width="5" />
    </StackPanel>
</DataTemplate>

This is a very simple bar graph but it will hopefully give you ideas and serve as the base for more elaborate representations of data. This is the result:

WPF Source Code

WPF

UWP/Uno Source Code

UWP

WinUI with Uno and WinUI Desktop Source Code

WinUI – Desktop

XAML Basics for WPF, UWP, Uno and WinUI

This is an index post for a series of blog posts covering some XAML basics. The original content came from a series of posts that Beatriz Stollnitz made on WPF/Silverlight that had been moved to a github repository. Unfortunately most of the samples don’t work out of the box with the latest version of Visual Studio … Continue reading “XAML Basics for WPF, UWP, Uno and WinUI”

This is an index post for a series of blog posts covering some XAML basics. The original content came from a series of posts that Beatriz Stollnitz made on WPF/Silverlight that had been moved to a github repository. Unfortunately most of the samples don’t work out of the box with the latest version of Visual Studio but with some minor adjustments they’re easily fixed and updated to include cross platform support via Uno and WinUI

I took a fork of the repository where I’ve been updating the content: Updated Code Samples. The main changes are:

  • Updating from .NET FX 3.5 to .NET FX 4.7.2 for the WPF project
  • Added support for .NET Core 3.1 and .NET 5 using multi-targeting for the WPF project
  • Removing any Silverlight projects and content (apologies if you’re still stuck maintaining/working on a Silverlight project!)
  • Added new UWP + Uno solution that targets: iOS, Android, MacOS, UWP and WASM
  • Added new WinUI with Uno and Desktop solution that targets: iOS, Android, MacOS, UWP, WASM and Desktop

I’ll update this index with links to each post as they’re made available.

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

XAML Back to Basics #7: ItemsPanel

How to change the layout of an ItemsControl

XAML Basics Series Index Page

The next post in the series originally written by Beatriz Stollnitz. Original post available on Github.

How to change the layout of an ItemsControl

I will show in this sample two ways to change the layout of an ItemsControl. This sample uses XmlDataProvider, which allows binding to XML data.

The easiest way to change the layout of an ItemsControl is simply by setting the ItemsPanel property to the Panel that will contain the items:

<ListBox ItemsSource="{Binding Source={StaticResource xmlData}}" (...) >
    <ListBox.ItemsPanel>
        <ItemsPanelTemplate>
            <StackPanel Orientation="Horizontal" />
        </ItemsPanelTemplate>
    </ListBox.ItemsPanel>
</ListBox>

Alternatively, for more extensive customizations, you can create a ControlTemplate. This ControlTemplate allows you to replace the whole VisualTree, including picking a new Panel to hold the items. For example, the following markup shows a ControlTemplate that adds a Border and changes the Panel on the ItemsControl:

<ControlTemplate x:Key="listBoxTemplate">
    <Border BorderBrush="Orange" 
            BorderThickness="2" 
            Margin="10,0,10,10">
        <StackPanel Orientation="Horizontal"
            IsItemsHost="True" />
    </Border>
</ControlTemplate>

<ListBox 
    ItemsSource="{Binding Source={StaticResource xmlData}}" 
    Template="{StaticResource listBoxTemplate}" (...) />

Most people get this far in this scenario, but often forget to set the IsItemsHost property in the Panel. IsItemsHost is a property that says “Use this Panel to lay out the items in the ItemsControl.” Notice that selection still works as usual.

If you want your items to wrap onto multiples lines, you can use a WrapPanel in place of the StackPanel. In this scenario, bear in mind that the default template for ListBox contains a ScrollViewer, so your items won’t wrap. To make them wrap, you can either provide your own ControlTemplate or, if you don’t need selection to work, use an ItemsControl instead of a ListBox.

As I mentioned before, I am using XmlDataProvider to bind to XML data. This is how I converted the GreekGods CLR data source I’ve used in previous samples:

<Window.Resources>
    <XmlDataProvider XPath="/GreekGods/GreekGod" x:Key="xmlData">
        <x:XData>
            <GreekGods xmlns="">
                <GreekGod>
                    <Name>Aphrodite</Name>
                    <Description>Goddess of love, beauty and fertility</Description>
                    <RomanName>Venus</RomanName>
                </GreekGod>
                (...)
            </GreekGods>
        </x:XData>
    </XmlDataProvider>
</Window.Resources>

The only thing to keep in mind when binding to XML is that instead of using the Path property in the Binding object, you should use the XPath property. You can use either Path or XPath syntax for DisplayMemberPath.

WPF Source Code

WPF

UWP/Uno Notes

The XmlDataProvider doesn’t exist for UWP applications. Instead the GreekGods XML data has been added as an XML file with build action of Embedded Resource. The data is loaded on startup and set as the ItemsSource for each ListBox.

There is also no support for binding using an XPath expression. For this a simple XmlElementConverter has been added.

UWP/Uno Source Code

UWP
WebAssembly (WASM)

Update 31st August 2020

Uno sample has been updated to v3 of Uno and supports iOS, Android, Windows and MacOS.

WinUI with Uno and WinUI for Desktop samples added.

WinUI with Uno and WinUI Desktop Source Code