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

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

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

NetCoreApp

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

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

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

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

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

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

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

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

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

Windows 7: WindowsForms and WPF

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

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

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

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

This doesn’t compile, yielding the following error.

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

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

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

Windows 10: UWP

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

XAML Back to Basics #15: TreeView

How to display grouped data in a TreeView

XAML Basics Series Index Page

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

How to display grouped data in a TreeView

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

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

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

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

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

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

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

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

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

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

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

Here is the result of the completed sample:

WPF Source Code

WPF

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

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

UWP Source Code

UWP

WinUI with Uno and WinUI Desktop Source Code

WinUI – Desktop

XAML Back to Basics #14: Sorting and Grouping

How to sort groups of data items

XAML Basics Series Index Page

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

How to sort groups of data items

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

This is a screenshot of the completed sample:

WPF Source Code

WPF

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

UWP Source Code

UWP

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

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

WinUI with Uno and WinUI Desktop Source Code

WinUI – Desktop

XAML Back to Basics #13: DataTemplateSelector

How to display items in an ItemsControl using different templates

XAML Basics Series Index Page

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

How to display items in an ItemsControl using different templates

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

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

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

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

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

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

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

Here is the code for the PlaceTemplateSelector:

public class PlaceTemplateSelector : DataTemplateSelector
{
    private DataTemplate washingtonTemplate;

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

    private DataTemplate notWashingtonTemplate;

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

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

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

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

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

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

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

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

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

Here is a screen shot of the completed sample:

WPF Source Code

WPF

UWP/Uno Notes

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

UWP Source Code

UWP

WinUI with Uno and WinUI Desktop Source Code

WinUI – Desktop

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

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

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

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

Windows Forms

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

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

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

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

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

The code behind for these buttons is relatively simple.

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

Standard UWP Button

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

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

public SimpleButtonForm()
{
    InitializeComponent();

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

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

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

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

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

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

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

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

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

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

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

Custom Control

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

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

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

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

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

With very simple code behind

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

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

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

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

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

The UWP application needs to reference the control library.

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

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

public CustomControlForm()
{
    InitializeComponent();

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

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

    this.Controls.Add(myHostControl);
}

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

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

or

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

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

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

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

Third Party Control

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

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

public ThirdPartyControlForm()
{
    InitializeComponent();

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

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

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

Third Party Control With Style

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

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

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

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

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

    public event PropertyChangedEventHandler PropertyChanged;

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

    public DateTime MyDate
    {

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

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

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

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

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

public ThirdPartyControlWithStyleForm()
{
    InitializeComponent();

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

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

    pnlXamlIsland.Controls.Add(myHostControl);

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

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

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

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

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

Windows Presentation Foundation (WPF)

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

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

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

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

Standard UWP Button

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

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

public SimpleButtonWindow()
{
    InitializeComponent();

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

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

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

Custom Control

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

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

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

Third Party Control

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

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

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

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

public ThirdPartyControlWithStyleWindow()
{
    InitializeComponent();

    DataContext = new DataModel();
}

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

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

XAML Back to Basics #12: Dialogs

How to implement a data bound dialog box

XAML Basics Series Index Page

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

How to implement a data bound dialog box

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

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

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

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

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

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

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

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

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

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

Here is a screen shot of the completed sample:

WPF Source Code

WPF

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

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

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

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

UWP Source Code

UWP

WinUI Notes

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

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

WinUI with Uno and WinUI Desktop Source Code

WinUI – Desktop

XAML Back to Basics #11: Multiple Linked Lists

How to synchronize ListBoxes displaying three levels of hierarchical data

XAML Basics Series Index Page

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

How to synchronize ListBoxes displaying three levels of hierarchical data

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

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

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

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

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

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

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

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

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

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

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

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

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

Here is a screen shot of the completed sample:

WPF Source Code

WPF

UWP/Uno/WinUI Notes

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

UWP/Uno Source Code

UWP
WASM

WinUI with Uno and WinUI Desktop Source Code

WinUI – Desktop