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

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

XAML Back to Basics #6: SelectedValue v SelectedItem

What is the difference between SelectedValue and SelectedItem?

XAML Basics Series Index Page

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

What is the difference between SelectedValue and SelectedItem?

When they are used by themselves, these two properties are very similar. The need for both and the difference between the two becomes apparent when SelectedValuePath is also set.

For example, consider our well-known GreekGods data source. I set the DataContext of the StackPanel to be that collection through code:

GreekGods items;
items = new GreekGods();
mainStackPanel.DataContext = items;

And used an empty Binding to bind that collection to the ListBox. I know that I want to select the GreekGod with description “Messenger of the Gods” (even though I am only displaying the Name of each God). This is when SelectedValuePath becomes useful. Each item in the ListBox is a GreekGod object, so by setting SelectedValuePath to “Description” I am able to drill down into the Description property of each GreekGod. Then I just have to set SelectedValue to the description I am looking for and the item becomes selected.

<StackPanel Name="mainStackPanel">
    <ListBox ItemsSource="{Binding}" DisplayMemberPath="Name" SelectedValue="Messenger of the Gods" SelectedValuePath="Description" Name="listBox1" (...) />
</StackPanel>

The difference between SelectedValue and SelectedItem should be obvious now. SelectedValue returns the string it was set to (“Messenger of the Gods”), while SelectedItem returns the actual GreekGod object with that description.

string messengerOfGods = (string)(listBox1.SelectedValue);
GreekGod hermes = (GreekGod)(listBox1.SelectedItem);

SelectedValue is particularly useful when only part of your item is stored in the model you are data binding to. In this scenario, you would data bind the SelectedValue property to the partial information in your model but the ListBox can show a lot more information about that item.

If you have ideas of how to combine these two properties in one, we would love to hear it.

WPF Source Code

WPF

UWP/Uno Source Code

UWP

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

XAML Back to Basics #5: DisplayMemberPath

DisplayMemberPath

XAML Basics Series Index Page

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

DisplayMemberPath

As I’ve shown in previous posts, binding an ItemsControl to an IEnumerable data source is really easy (remember that ListBox and ComboBox derive from ItemsControl). With DisplayMemberPath it’s even easier for the scenario where you want to display only one property of each data item as text. Before DisplayMemberPath, this scenario required the use of a DataTemplate that would specify the property we’re interested in, like in the following xaml:

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

<ItemsControl ItemsSource="{StaticResource greekGods}" ItemTemplate="{StaticResource itemTemplate}" />

The Data Binding team realized that this was a very common scenario and could be simplified, which was the motivation for introducing the DisplayMemberPath property in ItemsControl. The scenario above can now be done in a single line of xaml:

<ItemsControl ItemsSource="{StaticResource greekGods}" DisplayMemberPath="Name" />

It’s that easy 🙂

The image below shows both versions of the ItemsControl, the one on the left is using DataTemplate and the one on the right is using DisplayMemberPath.

WPF Source Code

WPF

UWP/Uno Source Code

t whose value you’re interested … Continue reading“XAML Back to Basics #2: Binding Markup”

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

What does “{Binding}” mean?

Most Bindings you see in samples have the Source and Path properties set. The Source property specifies the object you’re binding to and the Path specifies a property in that object whose value you’re interested in. I’ve seen several people get confused when encountering an empty Binding for the first time – “{Binding}”. It seems at first sight that we’re not giving the Binding enough information to do anything useful. This is not true and I will explain why. If you read my previous post you should understand that it is not necessary to set a Source in a Binding, as long as there is a DataContext set somewhere up in the tree. As for the Path, it should be left out when you want to bind to a whole object, and not only to a single property of an object. One scenario is when the source is of type string and you simply want to bind to the string itself (and not to its Length property, for example).

<Window.Resources>
    <system:String x:Key="helloString">Hello</system:String>
</Window.Resources>

<Border DataContext="{StaticResource helloString}">
    <TextBlock TextContent="{Binding}"/>
</Border>

Another common scenario is when you want to bind some element to an object with several properties.

<Window.Resources>
    <local:GreekGod Name="Zeus" Description="Supreme God of the Olympians" RomanName="Jupiter" x:Key="zeus"/>
</Window.Resources>

<Border DataContext="{StaticResource zeus}">
    <ContentControl Content="{Binding}"/>
</Border>

In this case, ContentControl does not know how to display the GreekGod data. Therefore you will only see the results of a ToString(), which is typically not what you want. Instead, you can use a DataTemplate, which allows you to specify the appearance of your data.

<Window.Resources>
    <local:GreekGod Name="Zeus" Description="Supreme God of the Olympians" RomanName="Jupiter" x:Key="zeus"/>
    <DataTemplate x:Key="contentTemplate">
        <DockPanel>
            <TextBlock Foreground="RoyalBlue" TextContent="{Binding Path=Name}"/>
            <TextBlock TextContent=":" Margin="0,0,5,0" />
            <TextBlock Foreground="Silver" TextContent="{Binding Path=Description}" />
        </DockPanel>
    </DataTemplate>
</Window.Resources>

<Border DataContext={StaticResource zeus}">
    <ContentControl Content="{Binding}" ContentTemplate="{StaticResource contentTemplate}"/>
</Border>

Notice that none of the Binding statements inside the DataTemplate has a Source. That is because a DataContext is automatically set to the data object being templated.

WPF Source Code

WPF

UWP/Uno Source Code

UWP

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

WinUI-Desktop

XAML Back to Basics #4: ComboBox Binding

How to bind the items of a ComboBox (and get its ComboBoxItems

XAML Basics Series Index Page

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

How to bind the items of a ComboBox (and get its ComboBoxItems)

Binding the items of a ComboBox is pretty much the same as binding the items of a ListBox:

<Window.Resources>
    <local:GreekGods x:Key="greekGods"/>

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

<ComboBox ItemsSource="{StaticResource greekGods}" ItemTemplate="{StaticResource itemTemplate}" Width="200" Name="comboBox"/>

The reason for this similarity is that both ComboBox and ListBox derive from ItemsControl, and ItemsSource and ItemTemplate are properties on ItemsControl.

If you read my previous post about how to get a ListBoxItem from a data bound ListBox, you’re probably thinking that you don’t need to keep reading to know how to do the same thing for a ComboBox. There is a little trick that you should be aware of, though.

If you use similar code to the solution of my previous post, you will notice that the ComboBoxItems are null:

GreekGod greekGod = (GreekGod)(comboBox.Items[0]);
ComboBoxItem cbi1 = (ComboBoxItem)(comboBox.ItemContainerGenerator.ContainerFromIndex(0));
ComboBoxItem cbi2 = (ComboBoxItem)(comboBox.ItemContainerGenerator.ContainerFromItem(comboBox.Items.CurrentItem));

This is because the generation of items for the ComboBox only happens when you open it. So the trick is to open the ComboBox before calling ContainerFromIndex/ContainerFromItem:

GreekGod greekGod = (GreekGod)(comboBox.Items[0]);
comboBox.IsDropDownOpen = true;
ComboBoxItem cbi1 = (ComboBoxItem)(comboBox.ItemContainerGenerator.ContainerFromIndex(0));
ComboBoxItem cbi2 = (ComboBoxItem)(comboBox.ItemContainerGenerator.ContainerFromItem(comboBox.Items.CurrentItem));
comboBox.IsDropDownOpen = false;

WPF Source Code

WPF

UWP/Uno Source Code

UWP

Update 19th 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

WinUI-Desktop

XAML Back to Basics #3: ListBox/ListView Binding

How to get a ListBoxItem from a data bound ListBox

XAML Basics Series Index Page

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

How to get a ListBoxItem from a data bound ListBox

Data binding a list box to an enumeration of items could not be easier in WPF:

<Window.Resources>
    <local:GreekGods x:Key="greekGods"/>
    <DataTemplate x:Key="itemTemplate">
        <TextBlock Text="{Binding Path=Name}" />
    </DataTemplate>
</Window.Resources>

<ListBox ItemsSource="{StaticResource greekGods}" ItemTemplate="{StaticResource itemTemplate}" Name="listBox"/>

The ItemsSource property of ListBox takes an IEnumerable, which is the list of items you want to display. In this case, the GreekGods data source is of type ObservableCollection, which implements IEnumerable. The ItemTemplate property specifies the DataTemplate that will be used to control how the data is displayed. In this case, we will have a TextBlock for each item that will display the GreekGod’s name.

Some of you might find surprising, however, that doing listBox.Items[i] in code returns the data we’re binding to, and not the TextBlock or the ListBoxItem. In my opinion, it is actually pretty cool that retrieving the data in a particular position of the list box is so easy, because most of the time this is exactly what you want.

GreekGod greekGod = (GreekGod)(listBox.Items[0]);

But what about when you want to have access to the actual ListBoxItem generated? This is a bit tricky to discover but can be just as easily done with the following code:

ListBoxItem lbi1 = (ListBoxItem)(listBox.ItemContainerGenerator.ContainerFromIndex(0));

There is also a listBox.ItemContainerGenerator.ContainerFromItem(object item) that returns the ListBoxItem given the corresponding data item. This method is frequently used, for example, to retrieve the ListBoxItem for the current item:

ListBoxItem lbi2 = (ListBoxItem)(listBox.ItemContainerGenerator.ContainerFromItem(listBox.Items.CurrentItem));

I will talk about selection and current item in detail in some other post, but for this sample it is sufficient to know that to keep the selection and current item in sync, I set IsSynchronizedWithCurrentItem=”true” in the ListBox.

WPF Source Code

WPF

UWP/Uno Notes

There are a couple of changes to the code for UWP/Uno:

  • Whilst the ListBox control still exists, it’s more common to use the ListView as it has a nice set of built in styles
  • The listBox.Items collection doesn’t have a CurrentItem property. Instead we can use listBox.SelectedItem
  • Attempting to set the IsSynchronizedWithCurrentItem property on the ListView (or even ListBox) throws an exception and is generally not required since the SelectedItem property is in sync with what is selected in the ListView.

UWP/Uno Source Code

UWP

XAML Note
The structure of the XAML has been left the same from the original post. However, you should really avoid embedding a ListView or ListBox within a StackPanel. This layout will limit the ability of the view to resize without the button being pushed off screen.

Update 15th 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

WinUI – Desktop

XAML Back to Basics #2: Binding Markup

What does “{Binding}” mean?

XAML Basics Series Index Page

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

What does “{Binding}” mean?

Most Bindings you see in samples have the Source and Path properties set. The Source property specifies the object you’re binding to and the Path specifies a property in that object whose value you’re interested in. I’ve seen several people get confused when encountering an empty Binding for the first time – “{Binding}”. It seems at first sight that we’re not giving the Binding enough information to do anything useful. This is not true and I will explain why. If you read my previous post you should understand that it is not necessary to set a Source in a Binding, as long as there is a DataContext set somewhere up in the tree. As for the Path, it should be left out when you want to bind to a whole object, and not only to a single property of an object. One scenario is when the source is of type string and you simply want to bind to the string itself (and not to its Length property, for example).

<Window.Resources>
    <system:String x:Key="helloString">Hello</system:String>
</Window.Resources>

<Border DataContext="{StaticResource helloString}">
    <TextBlock TextContent="{Binding}"/>
</Border>

Another common scenario is when you want to bind some element to an object with several properties.

<Window.Resources>
    <local:GreekGod Name="Zeus" Description="Supreme God of the Olympians" RomanName="Jupiter" x:Key="zeus"/>
</Window.Resources>

<Border DataContext="{StaticResource zeus}">
    <ContentControl Content="{Binding}"/>
</Border>

In this case, ContentControl does not know how to display the GreekGod data. Therefore you will only see the results of a ToString(), which is typically not what you want. Instead, you can use a DataTemplate, which allows you to specify the appearance of your data.

<Window.Resources>
    <local:GreekGod Name="Zeus" Description="Supreme God of the Olympians" RomanName="Jupiter" x:Key="zeus"/>
    <DataTemplate x:Key="contentTemplate">
        <DockPanel>
            <TextBlock Foreground="RoyalBlue" TextContent="{Binding Path=Name}"/>
            <TextBlock TextContent=":" Margin="0,0,5,0" />
            <TextBlock Foreground="Silver" TextContent="{Binding Path=Description}" />
        </DockPanel>
    </DataTemplate>
</Window.Resources>

<Border DataContext={StaticResource zeus}">
    <ContentControl Content="{Binding}" ContentTemplate="{StaticResource contentTemplate}"/>
</Border>

Notice that none of the Binding statements inside the DataTemplate has a Source. That is because a DataContext is automatically set to the data object being templated.

WPF Source Code

WPF

UWP/Uno Source Code

UWP
Uno – Wasm

Update 15th 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

WinUI Desktop

XAML Back to Basics #1: Data Context

How should I decide whether to use DataContext or Source?

XAML Basics Series Index Page

Last year I posted about a series of posts that Beatriz Stollnitz made on WPF/Silverlight that had been moved to GitHub. 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.

I took a fork of the repository and have started to a) update the wpf projects to work with VS2019 b) remove the Silverlight content and c) add equivalent examples that work cross platform using UWP and the Uno Platform.

Updated Code Samples

Importantly, as I go through each of the posts I’m going to reprint the majority of the original post with edits to bring them up to date with both WPF and UWP/Uno. I want to make sure the Beatriz is recognised as the original author of this content and that I just want to make sure her contributions live on to benefit the next generation of XAML developers.

Original post available on Github

How should I decide whether to use DataContext or Source?

The DataContext is one of the most fundamental concepts in Data Binding.

The Binding object needs to get its data from somewhere, and there are a few ways to specify the source of the data. In this post I talk about setting the Source property directly in the Binding vs inheriting a DataContext from the nearest element when traversing up in the tree. The other two alternatives are setting the ElementName and RelativeSource properties in the Binding object, but I will leave that for a future post.

For example, let’s assume we have the following data sources (GreekGod being a class defined in the code behind):

<Window.Resources>
    <local:GreekGod Name="Zeus" Description="Supreme God of the Olympians" RomanName="Jupiter" x:Key="zeus"/>
    <local:GreekGod Name="Poseidon" Description="God of the sea, earthquakes and horses" RomanName="Neptune" x:Key="poseidon"/>
</Window.Resources>

<StackPanel DataContext="{StaticResource poseidon}">
    <TextBlock TextContent="{Binding Source={StaticResource zeus}, Path=Name}"/>
    <TextBlock TextContent="{Binding Path=Description}"/>
    <TextBlock TextContent="{Binding Path=RomanName}"/>
</StackPanel>

The first TextBlock inherits the DataContext from the parent StackPanel and has a Source set in the Binding object too. In this case, Source takes priority, causing the TextBlock to bind to the Name property of the resource with key “zeus” – this displays “Zeus”.

The second TextBlock does not have a Source set directly in the Binding object, so it inherits the DataContext from the StackPanel. As you might guess, this binds to the Description property of the resource with key “poseidon”, displaying “God of the sea, earthquakes and horses”.

The third TextBlock should be straightforward – it displays “Neptune”.

Most data bound applications I see from users tend to use DataContext much more heavily than Source. My recommendation is to use DataContext when you need to bind more than one property to a particular source. When binding only one property, it may be better to use the Source attribute . The reason for this is ease of debugging – I would rather see all the information about the Binding in one place than search for the nearest DataContext to understand what is going on. In a small sample like the one above there is no big advantage, but in complex applications this could save you some time.

WPF Source Code

WPF

UWP/Uno Notes

In UWP it’s not very common to reference the Windows of the application directly. Instead you can define static resources (in this case the instances of the GreekGod class) at the Application, Page or even Control level. For example, to make the static resource available throughout a given page, we would define them as Page Resources.

<Page.Resources>
    <local:GreekGod Name="Zeus" Description="Supreme God of the Olympians" RomanName="Jupiter" x:Key="zeus"/>
    <local:GreekGod Name="Poseidon" Description="God of the sea, earthquakes and horses" RomanName="Neptune" x:Key="poseidon"/>
</Page.Resources>

UWP/Uno Source Code

UWP

Update 15th 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

WinUI Desktop

Don’t Judge XAML Based On Lines of Code

For those following the on-going discussion around the future of XAML and specifically the use of XAML in DotNetMaui/Xamarin.Forms, this post is a follow on from two great posts discussing the options that are, or will be, available for Xamarin.Forms developers as we move forward with DotNetMaui: Mobile Blazor Bindings – Getting Started + Why … Continue reading “Don’t Judge XAML Based On Lines of Code”

For those following the on-going discussion around the future of XAML and specifically the use of XAML in DotNetMaui/Xamarin.Forms, this post is a follow on from two great posts discussing the options that are, or will be, available for Xamarin.Forms developers as we move forward with DotNetMaui:

I’ve already discussed why I feel that DotNetMaui will extend Xamarin.Forms to be an inclusive platform for developers wanting to build cross platform applications. Recent additions to Xamarin.Forms, such as C# markup, coupled with the features outlined on the DotNetMaui roadmap, suggest that the path forward will be full of options for developers.

This bring me to the point of this post, which is to discuss XAML both in the context of DotNetMaui/Xamarin.Forms but also in the context of UWP/WinUI/Uno. To do this I’m going to re-visit the scenario discussed by Dylan and Vincent and look at some alternatives that might make XAML a bit more appealing

Xamarin.Forms Visual States

When I initial read through Dylan’s post and reviewed the XAML I was initially shocked at how hard it was to read and in fact it took me a while to realise why it looked so chaotic and hard to follow. It came down to a couple of things:

  • Changing XAML attribute values based on data triggers at various points in the XAML
  • The lack of Visual States
  • Using MultiTrigger to change attribute values based on multiple data values.

Just so that I’m clear, I’m not saying that the code was poorly written; I’m just observing why I found it hard to read. Let me present an alternative XAML for the page and discuss how it makes the code a little more readable.

<?xml version="1.0" encoding="utf-8"?>
<ContentPage
    xmlns="http://xamarin.com/schemas/2014/forms"
    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
    xmlns:local="clr-namespace:XamlFlags"
    x:Class="XamlFlags.MainPage"
    x:Name="Page">
    <ContentPage.BindingContext>
        <local:MainPageViewModel />
    </ContentPage.BindingContext>
    <StackLayout
        BindableLayout.ItemsSource="{Binding Options}">
        <BindableLayout.ItemTemplate>
            <DataTemplate>
                <Frame
                    CornerRadius="4"
                    Padding="0">
                    <StackLayout
                        Orientation="Horizontal"
                        Padding="5"
                        BackgroundColor="White">
        <VisualStateManager.VisualStateGroups>
            <VisualStateGroup>
                <VisualState
                    x:Name="IsEnabledAndSelected">
                    <VisualState.StateTriggers>
                        <StateTrigger
                            IsActive="{Binding Selected}" />
                    </VisualState.StateTriggers>
                    <VisualState.Setters>
                        <Setter
                            Property="BackgroundColor"
                            Value="DarkBlue" />
                        <Setter
                            Property="IsVisible"
                            Value="False"
                            TargetName="OptionsSelectedLabel" />
                        <Setter
                            Property="Label.TextColor"
                            Value="White"
                            TargetName="OptionsValueLabel" />
                    </VisualState.Setters>
                </VisualState>
                <VisualState
                    x:Name="IsNotEnabled">
                    <VisualState.StateTriggers>
                        <StateTrigger
                            IsActive="{Binding IsNotEnabled}" />
                    </VisualState.StateTriggers>
                    <VisualState.Setters>
                        <Setter
                            Property="IsEnabled"
                            Value="false" />
                        <Setter
                            Property="BackgroundColor"
                            Value="DarkGray" />
                        <Setter
                            Property="Label.TextColor"
                            Value="LightGray"
                            TargetName="OptionsValueLabel" />
                    </VisualState.Setters>
                </VisualState>
            </VisualStateGroup>
        </VisualStateManager.VisualStateGroups>
                        <Button
                            Text="Select"
                            Command="{Binding BindingContext.SelectTypeCommand, Source={x:Reference Page}}"
                            CommandParameter="{Binding .}" />
                        <Label
                            Text="✓"
                            TextColor="White"
                            x:Name="OptionsSelectedLabel"
                            FontSize="12"
                            HorizontalOptions="EndAndExpand"
                            VerticalOptions="Center"
                            IsVisible="false" />
                        <Label
                            Text="{Binding Value}"
                            TextColor="Black"
                            x:Name="OptionsValueLabel"
                            HorizontalOptions="EndAndExpand"
                            VerticalOptions="Center" />
                    </StackLayout>
                </Frame>
            </DataTemplate>
        </BindableLayout.ItemTemplate>
    </StackLayout>
</ContentPage>

On initial inspection you might be looking at this XAML wondering whether it’s improved the readability. However, if you collapse the VisualStateManager.VisualStateGroups node in the editor, the code immediately looks much more readable, as shown in the following image.

We can clearly make out that the interface is made up of a stack of items with each row being made up off a Button and two Label elements. The default state is for the row to have a White background and for the ✓ Label to be hidden (IsVisible set to false).

Now, let’s look at the contents of the VisualStateManager.VisualStateGroups. In this case it defines a single VisualStateGroup in which I’ve included two VisualState elements. If we consider each row it can be in one of three states:

  • Enabled and Not Selected – This is the default state where the item hasn’t been selected (IsEnabled = true, IsSelected = false).
  • Enabled and Selected – This is the state where the item has been selected by the user clicking the Select button (IsEnabled = true, IsSelected = true).
  • Is Not Enabled – When an item in the list is selected, other items may be set to disabled (IsEnabled = false, IsSelected = false).

Even though there are only two VisualStates defined in the XAML, there are in fact three visual states because there is the default state. Each of the VisualState uses a StateTrigger to determine whether the visual state is active or not. Rather than attempting to data bind to two different properties on the underlying data model, I’ve explicitly created single properties that reflect whether the state is active or not. The default state is both the initial state of each row, as well as the state that is returned to if neither of the defined VisualStates are active (ie the StateTrigger IsActive is set to true).

One of the benefits of using visual states is that you can set multiple properties, on one or more different elements, essentially grouping all the changes required for a particular visual states. This eliminates the need to have data triggers littered through the XAML used to define the layout for the page. In this scenario each of the VisualState elements has multiple Setters defining the background colour, the foreground (text) colour and in the case of the IsEnabledAndSelected state, the visibility of the ✓ Label.

As I pointed out earlier, for me, this makes the XAML easier to read. I know other developers prefer to have the data triggers local to where the attributes being changed are in the XAML. This approach has some advantages, particularly where you only have a small number of changes in a large XAML document.

Before we move on from discussing Xamarin.Forms I would point out that the VisualStateManager is a relatively new addition and specifically the ability to use TargetName to target different elements from a single VisualState. I would encourage revisiting your XAML to see whether using the VisualStateManager will simplify your code. Alternatively, you may want to consider breaking your code up into different controls that will help encapsulate things like visual states whilst reducing the complexity of the XAML on each page. We’ll talk more about using UserControls in the context of UWP shortly but the same concept can be used for Xamarin.Forms as well.

UWP / WindowsUI / Uno

The XAML used by the Universal Windows Platform (ie UWP) provides a couple of alternatives when it comes to defining the desired layout. In this section we’re going to consider three options: Optimised, UserControl and ContainerStyle (Code is available on GitHub)

Optimised for Lines of Code

In this approach the goal was simply to reduce the number of lines of code. I’m not going to spend much time on this because I fundamentally think measuring the quality of a technology based on the number of lines of code is just ridiculous. The following image shows the resulting 41 lines of XAML – of course this is also contingent on my Visual Studio settings where the first attribute is on the same line as the element; other developers have different options set, so will see different numbers of lines.

I will comment on a couple of things:

  • Unlike the Xamarin.Forms approach that uses a horizontal StackLayout, this XAML uses a Grid which clearly defines three columns. I prefer this approach as it clearly defines the sizing option for each column. Using a StackLayout with different HorizontalOptions isn’t as clear in my opinion.
  • This code uses x:Bind instead of the usual Binding syntax. This allows for code to be generated during compilation that enforces type checking and results in better performance as it reduces the reliance on reflection.
  • Rather than using converters this code uses static methods defined on the page to convert property values (in some cases multiple properties) into the required attribute value. In practice I would not recommend doing this as it embeds logic in your XAML layout

UserControl with Visual States

In the previous section we optimised the XAML by interleaving logic into our XAML layout. This is a practice that I actively try to avoid when defining the XAML for a page or control. I try to ensure the XAML I create is as declarative as possible, which means avoiding calling methods as part of binding expressions. I’d even go so far as to say that you should avoid using converters unless they are simple type converters (the most obvious one being for bool to Visibility conversion). I advocate for all logic to be encapsulated in either the ViewModel (if UI relate logic) or in the Model (if business logic), thus making it testable and ensuring a clean separation from the way that it’s presented (i.e. the View).

Let’s look at how we can keep our XAML clean of logic by following a similar approach to what we did earlier for Xamarin.Forms. What’s interesting about UWP is that if you attempt to follow the same strategy of defining visual states within the DataTemplate you’ll find that it doesn’t work. Instead you either need to define the visual states within a UserControl, or you have to apply the visual states in the ItemContainerStyle.

We’ll start with defining a UserControl which will include the layout for each item in the list, along with the different visual states. Rather than just show you the code, let’s jump into Blend and use the visual designer to build the desired layout. Rather than boring you with creating the project and adding things like the MainViewModel, I’m just going to focus on creating the UserControl.

From Solution Explorer, right-click on the project or a sub-folder and select Add, New Item.

From the Add New Item dialog, select User Control, give it a name (eg OptionItemControl) and click Add. This should give you a new design surface that we can start adding controls to.

Using the Assets tool window, double-click on the Button and then twice on the TextBlock. Alternatively you can drag these controls onto the design surface.

If you look at the XAML you’ll notice that Blend has added some default attributes to each of the controls that you added. Normally my recommendation would be to tidy up whatever XAML Blend creates, as you go, to ensure your XAML remains clean and does exactly what you want. If you’re not that familiar with XAML, reviewing the XAML that Blend generates is a great way to learn about features that you might not be aware of.

If we look at the design surface we can see that the three controls we added are incorrectly located on the design surface – we’re after a single row with three cells that should hold the “select” Button, a check mark TextBlock and the option value TextBlock.

Let’s go ahead and create the cells in the Grid by defining three columns. We can do this using the design by hovering the mouse near the top of the design surface. The cursor should change to include a small + sign and if you click with the mouse a new column break will be added. You can then use the designer to specify how the column widths are defined. In this case we want the first column to be Auto and the other two columns to be * (just *, so you may have remove any numeric value added by Blend eg 101* should be changed to just *)

Next we want to reposition the two TextBlock into the appropriate column. You can do this by simply dragging them using the mouse. As you drag and hover over each cell in the Grid you’ll see alignment and positioning lines appear in red.

After positioning the TextBlock into the correct column you will want to tidy up the XAML – Blend has an annoying habit of adding Margins and other layout attributes that are typically unnecessary. You can clean up these excess attributes by right-clicking the control and selecting Layout, Reset All.

The next thing to do is to replace the default Text value for the TextBlock from “TextBlock” to something more meaningful. Rather than having to locate the Text property in the properties tool window, you can simply double-click the TextBlock and type directly on the designer. You can do the same thing with the Button control to change the Content to “Select”.

According to the design we’re aiming to achieve we need to change the default (i.e. Enabled but not Selected) Background to White. Use the colour picker in the Properties tool window to do this. This of course will override the theming and thus break support for dark and light themes, but that’s a topic for another day.

Unfortunately setting the Background to White means we can no longer see the three controls, since their default text colour is currently also White. This can be fixed easily by selecting all three controls in the Objects and Timelines tool window.

From the Properties tool window, update the Foreground colour to Black.

You’ll also need to adjust the BorderBrush on the Button. After doing this the layout should be similar to the following.

And the XAML for this is quite tidy.

What’s missing are the visual states for when the option is selected and when it’s disabled. We’ll add two VisualStates to the UserControl via the States window. Normally I advocate for creating a third VisualState that represents the default state – this is so that in code you can use the VisualStateManager GoToState method to return to the default layout. However, we’re going to be using state triggers to transition between states, so a third VisualState is not required – when none of the state triggers are set to active, the UserControl will return to the default state.

By clicking on a VisualState in the States tool window we can put Blend into state editing mode. This is highlighted by both the red dot alongside the VisualState, as well as the red border around the design surface. Any changes you make whilst this highlighting is visible will be recorded against the corresponding VisualState.

I’m not going to go through setting all the properties for each VisualState but essentially we need to update the Background colour on the Grid, the Foreground colour on the TextBlock and Button, and the Visibility on the checkmark TextBlock. For the checkmark TextBlock, you’ll need to switch the default Visibility to Collapsed – we had it visible whilst designing the layout but it’s default state is actually to be hidden (i.e. Collapsed).

As part of defining the VisualStates you may have noticed that the controls in the Objects and Timeline tool window were all given default names like grid, button, textBlock and textBlock1. I would encourage you to give these more meaningful names by double-clicking on them in the Objects and Timeline tool window and typing a new name.

Currently we have completed the design of the UserControl, including the three different states it can be in. However, we haven’t wired it up to any data. If we were to use this UserControl as it is, it would simply show a list of “Option 1”. So that we can wire up some data, let’s add an instance of the OptionViewModel as a design time DataContext for the UserControl (note the use of the d: prefix to indicate design time only).

Select the TextBlock that should show the OptionViewModel Value property. Click on the small square to the right of the Text property in the Properties tool window and select Create Data Binding.

Since we’ve set up an instance of the OptionViewModel as a design time DataContext, Blend is able to offer suggestions for the Path attribute. Select the Value property and click Ok.

At this point you’ll be wondering why the TextBlock that you just setup data binding for as disappeared from the designer. Well, the good news is that the TextBlock is still there, it just has zero width because the Value property on the OptionViewModel is returning null. This is easily fixed by specifying the Value property on the OptionViewModel specified in the design time DataContext.

Ok, we’re almost there. We have our layout, our Visual States and our data. We still need to be able to trigger then changing of states. For this we’re going to use a state trigger. Click the “Edit Adaptive Triggers” button next to the VisualState.

From the Dropdown at the bottom of the screen select <Other Type…>

In our project we have a trigger called DualBooleanDataTrigger which will allow us to trigger a VisualState based on the value of two different bool properties.

Unfortunately this is about as far as the data trigger design experience can take us. Luckily the XAML for the DualBooleanDataTrigger isn’t that complex. As you can see from this image, we need to data bind the DataValue and SecondDataValue attributes to the IsEnabled and IsSelected properties on the OptionViewModel and then we need to specify the value for each of these properties that we want the VisualState to be active for. In this case we want both the IsEnabled and IsSelected properties to be True for this state to be active.

The last thing we need to do is to wire up the Button to a method that will set the OptionViewModel to be selected. For this, we’re going to use x:Bind so that we can bind directly to a method on the OptionViewModel. In order to do this, we need to expose the the OptionViewModel as a property on the UserControl.

In the MainPage (i.e. where we’re defining the ItemsControl) we need to include an instance of the OptionItemControl in the DataTemplate. In order for x:Bind to work we need to make sure we set the ViewModel to be the current DataContext of the DataTemplate (i.e. binding path of . which can be omitted in this scenario).

We then need to specify the Click attribute on the Button.

After completing this we’re done! But let’s take a look at what’s been generated. Well, the good news is that we’ve created the entire user experience without having to write much XAML at all. The bad news is that Blend does a terrible job of generating optimized XAML. Take for example the following snippet where you can see that each Setter is spread over 5 lines.

In contrast, when I tidy these up, each Setter takes only 2 lines and that’s only because I have Visual Studio and Blend set to put attributes on new lines.

As you can see from this walk through, you can use a separate UserControl to easily design the layout for the three states required in this design.

ListView ItemsContainerStyle

The third option I wanted to briefly discuss (because there’s plenty of room for an entire discussion on this topic) is to override the ItemsContainerStyle for a ListView in order to define the VisualStates for the items in the list. Let’s back up for a second and explain a few things.

The scenario that we’re addressing is a common enough scenario where items in a list are selected. In this case the individual item tracks whether it’s selected (and in fact whether it’s enabled). This approach is actually at odds with the way a number of controls work. Take for example the ListView or GridView which are used to represent a list, or grid, of selectable items. Theses controls have built in styles that represent how each ListViewItem will look in various states. These states include (not an exhaustive list) Selected, Pressed, Enabled and Disabled. Further more, these control separate the appearance of each item (specified using the ItemTemplate) from the behaviour when the user interacts with the items (specified in the ControlTemplate used to define the ListViewItem Template nested in the ItemsContainerStyle). Both the ItemTemplate and ItemsContainerStyle are editable using the visual designer, allowing you to customise the appearance of the item and how it changes when the item is selected or enabled/disabled.

For example in Blend we can right-click on a ListView and select Edit Additional Templates, Edit Generated Item Container (ItemContainerStyle) and then either Edit Current, Edit Copy or Create Empty. I would suggest starting by selecting Edit Copy, which will give you a default style that you can customise, rather than starting from scratch.

In the following image you can see how I’ve defined different VisualStates for the ListViewItem to control the appearance for the three states the item can be in. You can also see all the other states that are available by default on the ListViewItem.

At this point you might be asking why, since the ListViewItem already has a Selected and a Disabled state, don’t I just use these. Well, the issue we have is that the Background and Foreground properties have three different colours based on the combination of two different properties (IsEnabled and IsSelected). This means we need to define states that are in the same group that respond to changes in both these properties. This is not how the built in VisualStates are defined. The Selected and Disabled states are in different VisualStateGroups – if we were to use these to control the same properties, we’ll end up with random and chaotic changes in the appearance.

VisualStates in different VisualStateGroups should not control the same property.

This is an important rule and one that’s easily forgotten or broken. If you ever see unusual behaviour relating to visual states, make sure you check this rule.

I’m not going to delve further into how to use the ItemsContainerStyle in this post but feel free to check out the source code. You’ll notice that this is very verbose coming in at around 130 lines, and this is using a much simplified ItemsContainerStyle. Unfortunately due to the complexity of the built in styles, the ItemsContainerStyle is a large style, mainly because the Template for the ListViewItem is so large. Don’t let this put you off considering using this approach because it does mean that you can reuse the style across various lists that may have different content.

Summary

Wow, sorry this ended up being quite a long post but hopefully you’ll find it useful and get some insight on the various XAML based options available to you. Don’t forget that all the options presented for UWP are able to be taken cross platform using the Uno Platform.

In summary, yes, XAML is verbose but it does come with options. If you spend time maintaining your XAML you’ll find that it is easy to understand and you can leverage the designer support in Blend and Visual Studio along the way.

I don’t believe that looking at the number of lines of XAML (or C# code) is productive. What is useful is to look at the options that are available and the easy with which you can achieve the desired outcome.

Windows (UWP) Designer in Visual Studio and Blend for Visual Studio

If you’ve been looking at the release notes for the Visual Studio previews, you’ll have noted that there’s some work being done on the XAML designer that now supports both WPF and UWP. I figured I would take this opportunity to go through and document some of the features that I use and some of … Continue reading “Windows (UWP) Designer in Visual Studio and Blend for Visual Studio”

If you’ve been looking at the release notes for the Visual Studio previews, you’ll have noted that there’s some work being done on the XAML designer that now supports both WPF and UWP. I figured I would take this opportunity to go through and document some of the features that I use and some of the new features that have appeared in the previews. Note that this isn’t an exhaustive list by any means – would love feedback on what other features you use and what you think is missing in the designer.

One of the reasons that the designer experience in Visual Studio and Blend is so relevant is that you can take the design you’ve done for your Windows app and port it across to iOS, Android, MacOS and even the web (using Web Assembly). All these platforms are available via the Uno platform – If you’re new to the Uno Platform, head over to https://platform.uno/ and get started with building cross-platform mobile, desktop and Web applications.

New Project

Ok, so let’s start by creating ourselves a new Windows (UWP) project based on the Blank App (Universal Windows) template.

As usual, give the project a name to get started – you’ll be prompted to pick which target and minimum versions of Windows you want but for the purpose of this post I just went with the defaults.

Visual Studio Designer

Once created, depending on your setup of Visual Studio, you’ll probably have the MainPage.xaml already open. If not, find MainPage.xaml in the Solution Explorer tool window and double-click it to open it. Here you can see that I have the Toolbox on the left of the design surface and the Properties window, above the Solution Explorer, on the right side. I find this layout works well for working with the designer but you can easily customise the layout of the tool windows to suit how you work.

Design in Blend

We’re actually going to switch across to Blend for Visual Studio for the rest of this post. I prefer to do any designer work in Blend because I prefer to have the tool windows in a different position when doing design work, than when writing code. Switching between Visual Studio and Blend also gives me a mental switch to go between designer mode (well at least “layout-oriented work” mode, since I’m clearly not a designer) and developer mode.

It’s worth noting that the designer experience in Visual Studio and Blend is very similar – Microsoft made the decision years ago to build a consistent experience with the majority of functionality now available in both tools. Blend still retains a number of designer oriented features, such as creating visual states and animations with storyboards, that haven’t been exposed in the Visual Studio designer.

You can easily switch to Blend by right-clicking on the project or a XAML file in Solution Explorer and selecting Design in Blend.

As you can see the tool windows are labeled slightly differently in Blend and have a different default position. Again, feel free to reposition them to suit how you work.

Zoom

If you look at the main design surface, you’ll note that the initial position of the MainPage is very small. Working at this zoom level will be quite hard as each of the elements will be small and hard to manipulate.

In the bottom left corner of the design surface there’s a series of icon buttons and a dropdown. Expanding the dropdown allows you to select from a number of predefined zoom levels.

Alternatively you can select Fit all, or Fit selection, in order to bring the whole page into view.

The other way that you can control the positioning of the design area is using the mouse:

  • Scroll up/down – Two-finger drag on touch pad, or scroll wheel on mouse
  • Scroll left/right – Hold Shift + Two-finger drag on touch pad, or scroll wheel on mouse
  • Zoom in/out – Hold Ctrl + Two-finger drag on touch pad, or scroll wheel on mouse

Adding Controls with Assets Tool Window

Let’s start to add some controls to the page. We’ll use the Assets tool window to locate the TextBlock control using the search function.

If I just click and drag the TextBlock onto the design surface, Blend will add the control where I drop it.

Objects and Timeline Tool Window

If we take a look at the XAML you’ll note that a very arbitrary margin has been set on the TextBlock

Layout – Reset All

Right-click on the TextBlock in the Objects and Timelines tool window and select Reset All from the Layout menu.

Now the TextBlock has been reposition to take up the whole page.

However because text flows from the top-left, the word TextBlock is in the top left corner of the page.

Edit Style – Apply Resource

Next, let’s increase the size of the text. Instead of manually setting FontSize, we’re going to make use of one of the existing TextBlock styles. Right-click on the TextBlock again in the Objects and Timeline window and select Edit Style, Apply Resource and then we’ll select HeaderTextBockStyle. You can read more about predefined styles and the use of typography here.

This gives our TextBlock a nice size, without hard coding font sizes and styles randomly throughout the application.

Design Surface

Up to now I haven’t given you much context for this app – we’re going to build a simple interface that shows a list of contacts. The TextBlock we’ve added so far will act as a header/title for the page, and then beneath it we’ll need a ListView showing the list of our contacts.

Drawing Grid Rows

Before we can add the ListView, let’s create two rows in the Grid that the TextBlock is sitting in. Select the Grid in the Objects and Timelines window and then on the design surface, if you move the mouse cursor near the edge of the page, you’ll see the cursor change to one that’s got a small plus sign on it. Clicking at this position will add a row to the Grid.

Once added, you can then adjust the sizing of the row. In this scenario we’re going to change the row from 33* to being Auto, which will mean the row will be sized based on the height of the TextBlock.

Reset Grid.RowSpan in Properties Tool Window

One thing you may notice is that after adjusting the row size, the row seems to disappear. This is because in creating the row, Blend decided that the TextBlock was going to span both rows. To fix this, we can change the RowSpan on the TextBlock from 2 to 1 using the Properties tool window.

In this case, we’re going to select the Reset option, rather than setting it explicitly to 1. This will mean one fewer attributes in XAML that needs to be parsed.

Assets Button

Next up, we’re going to add the ListView to the second row. This time, instead of going all the way to the Assets tool window, we’re going to use the dropdown from the chevrons on the top left of the designer. This allows us to easily search and add controls, without having to open the tool windows – when doing a lot of design work I will often hide the tool windows (or even detach them and put them on a different monitor), so not having to open the tool windows to find a control is quite handy.

Runtime Toolbar

After dragging the ListView onto the page and making sure it’s sitting in the second row, I’m going to run the application to see what it looks like. Note that despite me not doing anything, the color of the title bar has aligned with my Windows theme (and yes, it’s pink because I’m using the setting where the theme is derived off the background I have set, which in turn is changed daily using the Dynamic Theme app).

Hot Reload

You’ll also note at the top of the window there is a toolbar that’s been added whilst I’m debugging the application. This toolbar has been available for a while but the Hot Reload indicator is a new addition. Hot Reload allows you to make changes to your XAML and for it to immediately take effect on the page when you save the XAML file (Hot Reload can be configured via Options off the Tools menu).

Display Layout Adorners

I’m going to toggle the Display Layout Adorners button (immediately adjacent to the left of the Hot Reload green tick).

Select Element

Next I’m going to click on the Select Element button (second in from the left) and click on the open space immediately below the TextBlock. As you can see, this highlights the whole area in a light blue (the layout adorner) which is the ListView.

Go to Live Visual Tree

Next I can click on the Go to Live Visual Tree button (first from the left) which will switch back to Visual Studio with the focus being set on the appropriate node in the Live Visual Tree.

XAML Editor

You’ll also note that the corresponding code in the XAML editor has been selected. As I start to type in the XAML editor I get intellisense showing me what options I have.

I’ve gone ahead and set a simple data template for the ListView which will determine what’s displayed for each item in the list. In this case it’s just going to print the word “Hi” for each item.

XAML Highlighting

Note that as I move the cursor around the XAML editor, the matching pairs of XML tags are highlighted, making it easy to see start and end of the blocks of XAML.

DataContext for Data Binding

Currently, despite setting a data template, the ListView isn’t going to show any items because I haven’t connected it to any data. To allow me to continue to layout elements on the page I’ve created some mock, or fake, data.

public class FakeData
{
    public Contact[] Contacts { get; } = new[]
    {
        new Contact(){Name="Bob Jones",PhoneNumber="+1 442 002 3234", Photo="ms-appx:///Assets/Photos/p0.jpg"},
        new Contact(){Name="Jessica Phelps",PhoneNumber="+1 394 234 1235", Photo="ms-appx:///Assets/Photos/p1.jpg"},
        new Contact(){Name="Andrew Jenkins",PhoneNumber="+1 232 282 29321", Photo="ms-appx:///Assets/Photos/p2.jpg"},
        new Contact(){Name="Francis Davis",PhoneNumber="+1 92329 2923 923", Photo="ms-appx:///Assets/Photos/p3.jpg"},
        new Contact(){Name="Xavier Smith",PhoneNumber="+1 93483 3923423", Photo="ms-appx:///Assets/Photos/p4.jpg"},
        new Contact(){Name="Kevin Chow",PhoneNumber="+1 343 994 39342", Photo="ms-appx:///Assets/Photos/p5.jpg"},
        new Contact(){Name="Phil Stevenson",PhoneNumber="+1 885 367 44432", Photo="ms-appx:///Assets/Photos/p6.jpg"},
        new Contact(){Name="Heath Sales",PhoneNumber="+1 903 912 9392", Photo="ms-appx:///Assets/Photos/p7.jpg"},
        new Contact(){Name="Sarah Wright",PhoneNumber="+1 347 399 499234", Photo="ms-appx:///Assets/Photos/p8.jpg"},
        new Contact(){Name="Geoff Sans",PhoneNumber="+1 834 1232 01923", Photo="ms-appx:///Assets/Photos/p9.jpg"}
    };
}

I can go ahead and create an instance of the FakeData class as a Resource on the Page. This instance is then set as the DataContext for the root Grid element on the page (and subsequently the DataContext for all elements on the page).

The ItemsSource on the ListView is then data bound to the Contacts property on the FakeData instance. The running application updates immediately to show a list of “Hi” down the screen.

Hide the Runtime Toolbar

As we start to design the page, the toolbar at the top can often get in the way. If you click on the chevron on the right side of the toolbar, the first click will reduce the size of the Hot Reload indicator (removing the words Hot Reload). The second click will minimise the toolbar completely.

Let’s amend the data template for the list, this time data binding to the Name property.

The ListView is immediately looking better, showing the names of the people in the Contacts list.

Edit ListView Item Template

If your working in the designer there are a number of ways you can make changes to the data template for the list. In this example we’re using the dropdown at the top left corner of the design area. Clicking the down button, followed by Edit Additional Template, then Edit Current, then Edit Generated Items (ItemTemplate).

Objects and Timeline – Group Into

You’ll notice that the Objects and Timeline window updates to show the tree from the perspective of the data template. Right-clicking on the TextBlock I can select Group Into, followed by StackPanel, to wrap the TextBlock in a StackPanel.

TextBlock Button

Next, I can add a TextBlock to the newly created StackPanel but simply double-clicking the TextBlock button on the left of the design area.

XAML Suggested Actions

One of the newest additions to the designer is the XAML Suggested Actions. Anyone who’s worked in Word or Excel is familiar with the suggested actions toolbar that often appears to try to make more of the tools are accessible when you need them. in this case we can click on the lightbulb button to display an in-situ property editor.

In this case we’re going to click through to Create Data Binding.

Create Data Binding

From the Create Data Binding window we can traverse down to the PhoneNumber property on the Contact object.

Note that there’s a minor bug at this point because the data binding that’s created isn’t 100% accurate. Instead of data binding to the PhoneNumber property (eg {Binding PhontNumber}) the generated binding is with Contacts.PhoneNumber. Simply removing the “Contacts.” prefix is enough to get it to work.

Edit ColumnDefinitions

I’m going to follow a similar approach to before – select the StackPanel and group into a Grid. From the Properties tool window find the ColumnDefinitions.

Click the … to open the Collection Editor for the ColumnDefinitions. Here we’re going to limit the first column to 50 pixels and then leave the second column as *.

I’ve update the data template with the profile picture as an image. Simply add an Image control and follow the same process as before to setup data binding to the Photo property on the Contact.

WinUI – ProfilePicture Control

To improve the layout of the contacts I’m going to use the ProfilePicture control, which will ensure all the images are the same size.. Instead of using the built in control, I’m going to grab the ProfilePicture control from the WinUI toolkit. Using the nuget package manager, select the Microsoft.UI.Xaml package and install it.

Once you’ve added the nuget package, make sure you don’t ignore the readme file that’s displayed to remind you to include the appropriate runtime values.

We’re almost there, we just need to add the PersonProfile, which we can do by discovering it in the Assets window (even though its from a third party)

And so now we have all our contacts appearing.

Final Design

After applying some minor tweaks to the layout of the data set, we can have a nicer looking sample app.

Before I copied the final code to this post, I just wanted to format the XAML. From the Options window, you can configure how you want your XAML files to appear.

Then, once you have the formatting options the way you want them, you can invoke Format Document from the various places around the design surface.

Final XAML

And here’s the final source code

<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      xmlns:local="using:WindowsDesigner"
      xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
      xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
      xmlns:Custom="using:Microsoft.UI.Xaml.Controls"
      x:Class="WindowsDesigner.MainPage"
      mc:Ignorable="d">
    <Page.Resources>
        <local:FakeData x:Key="DesignTimeData" />
    </Page.Resources>

    <Page.Background>
        <ThemeResource ResourceKey="ApplicationPageBackgroundThemeBrush" />
    </Page.Background>

    <Grid DataContext="{StaticResource DesignTimeData}">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition />
        </Grid.RowDefinitions>
        <TextBlock Margin="16"
                   Text="Contacts"
                   TextWrapping="Wrap"
                   Style="{StaticResource HeaderTextBlockStyle}" />
        <ListView Grid.Row="1"
                  ItemsSource="{Binding Contacts}">
            <ListView.ItemTemplate>
                <DataTemplate>
                    <Grid Margin="8">
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition Width="50" />
                            <ColumnDefinition />
                        </Grid.ColumnDefinitions>
                        <StackPanel Orientation="Vertical"
                                    Grid.Column="1"
                                    Margin="8"
                                    HorizontalAlignment="Center">
                            <TextBlock Text="{Binding Name}"
                                       Style="{StaticResource BaseTextBlockStyle}" />
                            <TextBlock Text="{Binding PhoneNumber}"
                                       Style="{StaticResource BodyTextBlockStyle}" />
                        </StackPanel>
                        <Custom:PersonPicture Width="50"
                                              Height="50"
                                              ProfilePicture="{Binding Photo}" />
                    </Grid>
                </DataTemplate>
            </ListView.ItemTemplate>
        </ListView>
    </Grid>
</Page>

And that’s it – hopefully you’ve learnt a bit more about how to use the XAML editor and the new designer features.