Let’s use XAML Island on WPF app without changing C# code behind

This post has been republished via RSS; it originally appeared at: New blog articles in Microsoft Tech Community.

Final answer is case by case. However, if you had been creating apps using MVVM pattern, then there is possible that it's easy to migrate to UWP controls using XAML Islands.

 

In this article, I migrate to UWP controls using XAML Islands from WPF controls.

The sample app

I created a simple application like below:

itemmanager.gif


The app is a really simple MVVM app. MainPage.xaml is as below:

 

 

<Window x:Class="WpfApp.Views.MainWindow" 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:WpfApp.Views" xmlns:viewModels="clr-namespace:WpfApp.ViewModels" mc:Ignorable="d" Title="MainWindow" Height="450" Width="800"> <Window.DataContext> <viewModels:MainWindowViewModel /> </Window.DataContext> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition /> <ColumnDefinition /> </Grid.ColumnDefinitions> <local:ItemsTreeView SelectionChanged="ItemsTreeView_SelectionChanged" /> <local:ContentItemView Grid.Column="1" /> </Grid> </Window>

 

 

 

The left side is a UserControl that name is ItemsTreeView. The UserControl is to display TreeView and raise a SelectionChanged event when selection changed on the TreeView.

 

The right side is a UserControl that name is ContentItemView. The UserControl is to provide displaying and editing features to selected item on the TreeView.

 

A following picture is a project structure of the app.

clipboard_image_1.png

WpfApp.Models project is a Class Library project of .NET Standard 2.0. WpfApp project is a WPF App(.NET Core) project.

Let's set up for XAML Islands

The details is written on this document. I have set up my solution like following:

clipboard_image_2.png

 

UWPApp project and UWPApp.Controls project references WpfApp.Models project.

And I've created two UserControls to UWPApp.Controls project:

clipboard_image_3.png

 

Those are implemented same as WPF's ones. For example, TreeView tag of ItemsTreeView.xaml is:

 

 

<TreeView x:Name="treeView" ItemsSource="{Binding Categories}" ItemTemplateSelector="{StaticResource treeViewItemTemplateSelector}" SelectedItemChanged="TreeView_SelectedItemChanged"> </TreeView>

 

 


TreeView tag of UWPItemsTreeView.xaml is:

 

 

<winui:TreeView x:Name="treeView" ItemsSource="{Binding Categories}" ItemTemplateSelector="{StaticResource treeViewItemTemplateSelector}" ItemInvoked="treeView_ItemInvoked"> </winui:TreeView>

 

 

 

As with WPF, it can use data binding feature on UWP controls, even if using XAML Islands. And it also can use compile time data binding feature, if target data type is in Class Library in .NET Standard, like below:

 

 

<DataTemplate x:DataType="models:Item"> <winui:TreeViewItem> <TextBlock> <Run Text="{x:Bind Name, Mode=OneWay}" /> <Run Text=": " /> <Run Text="{x:Bind Emoji, Mode=OneWay}" /> </TextBlock> </winui:TreeViewItem> </DataTemplate>

 

 

 

Let's replace to UWP controls

Adding Microsoft.Toolkit.Wpf.UI.Controls package to the WPF project, and change MainWindow.xaml to use WindowsXamlHost control.

 

 

<Window x:Class="WpfApp.Views.MainWindow" 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:WpfApp.Views" xmlns:viewModels="clr-namespace:WpfApp.ViewModels" xmlns:xaml="clr-namespace:Microsoft.Toolkit.Wpf.UI.XamlHost;assembly=Microsoft.Toolkit.Wpf.UI.XamlHost" mc:Ignorable="d" Title="MainWindow" Height="450" Width="800"> <Window.DataContext> <viewModels:MainWindowViewModel /> </Window.DataContext> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition /> <ColumnDefinition /> </Grid.ColumnDefinitions> <xaml:WindowsXamlHost InitialTypeName="UWPApp.Controls.UwpItemsTreeView" ChildChanged="UwpItemTreeViewHost_ChildChanged" /> <xaml:WindowsXamlHost InitialTypeName="UWPApp.Controls.UwpContentItemView" ChildChanged="UwpContentItemView_ChildChanged" Grid.Column="1" /> </Grid> </Window>

 

 

 

And WindowsXamlHost doesn't have a feature to inherit DataContext value, so set up it on ChildChanged event handler:

 

 

using Microsoft.Toolkit.Wpf.UI.XamlHost; using System.Windows; using UWPApp.Controls; using WpfApp.Models; using WpfApp.ViewModels; namespace WpfApp.Views { /// <summary> /// Interaction logic for MainWindow.xaml /// </summary> public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); } private void UwpItemTreeViewHost_ChildChanged(object sender, System.EventArgs e) { var host = (WindowsXamlHost)sender; if (host.Child is UwpItemsTreeView treeView) { treeView.DataContext = DataContext; treeView.SelectionChanged += (_, args) => { ((MainWindowViewModel)DataContext).SelectedItem = args.SelectedItem as Item; }; } } private void UwpContentItemView_ChildChanged(object sender, System.EventArgs e) { var host = (WindowsXamlHost)sender; if (host.Child is UwpContentItemView itemView) { itemView.DataContext = DataContext; } } } }

 

 

 

And, launch the app, then the following window is displayed:

itemmanager2.gif


Color emoji is fine. And the controls size is touch friendly. If you don't need touch friendly size, then you can also use compact size:

itemmanager3.gif


And also, if you like dark mode, then it can be used really easy. it's just adding RequestedTheme="Dark" to App.xaml of UWPApp project.

itemmanager4.gif

 

Improve XAML

Currently, I have been using WindowsXamlHost control on MainWindow.xaml, like below:

 

 

<xaml:WindowsXamlHost InitialTypeName="UWPApp.Controls.UwpItemsTreeView" ChildChanged="UwpItemTreeViewHost_ChildChanged" />

 

 

 

I think it is not good. Because I have to set DataContext property manually. And I also have to set any other properties and event handlers in C# code. I'm XAMLer, so I would like to write almost all in XAML.

InkCanvas control that is provided from Windows Community Toolkit is on XAML Islands, however it doesn't use WindowsXamlHost control.

I checked the source code(It's OSS!). The control inherits WindowsXamlHostBase class, and it was binding properties using Bind method.

 

I created two wrapper control like following:

UWPItemsTreeView class:

 

 

using Microsoft.Toolkit.Wpf.UI.XamlHost; using System; using WpfApp.Views; using UWPControl = UWPApp.Controls.UwpItemsTreeView; namespace WpfApp.Wrapper { public class UWPItemsTreeView : WindowsXamlHostBase { public event EventHandler<TreeViewSelectionChangedEventArgs>? SelectionChanged; public UWPItemsTreeView() : base(typeof(UWPControl).FullName) { } protected override void OnInitialized(EventArgs e) { Bind(nameof(DataContext), DataContextProperty, UWPControl.DataContextProperty); base.OnInitialized(e); } protected override void OnChildChanged() { if (ChildInternal is UWPControl c) { c.SelectionChanged += ChildInternal_SelectionChanged; } base.OnChildChanged(); } private void ChildInternal_SelectionChanged(object? sender, UWPApp.Controls.TreeViewSelectionChangedEventArgs e) { SelectionChanged?.Invoke(this, new TreeViewSelectionChangedEventArgs(e.SelectedItem)); } } }

 

 

 

UwpContentItemView class:

 

 

using Microsoft.Toolkit.Wpf.UI.XamlHost; using System; using UWPControl = UWPApp.Controls.UwpContentItemView; namespace WpfApp.Wrapper { public class UwpContentItemView : WindowsXamlHostBase { public UwpContentItemView() : base(typeof(UWPControl).FullName) { } protected override void OnInitialized(EventArgs e) { Bind(nameof(DataContext), DataContextProperty, UWPControl.DataContextProperty); base.OnInitialized(e); } } }

 

 

 

And I modified MainWindow.xaml to use those.

 

 

<Window x:Class="WpfApp.Views.MainWindow" 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:WpfApp.Views" xmlns:viewModels="clr-namespace:WpfApp.ViewModels" xmlns:wrapper="clr-namespace:WpfApp.Wrapper" mc:Ignorable="d" Title="MainWindow" Height="450" Width="800"> <Window.DataContext> <viewModels:MainWindowViewModel /> </Window.DataContext> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition /> <ColumnDefinition /> </Grid.ColumnDefinitions> <wrapper:UWPItemsTreeView SelectionChanged="ItemsTreeView_SelectionChanged" /> <wrapper:UwpContentItemView Grid.Column="1" /> </Grid> </Window>

 

 

 

And I removed ChildChanged event handler from the code behind.

That's all. The app is continuously work fine, and XAML looks nice.

The source code is following github repository:

https://github.com/runceel/IgniteLT

Wrap up

XAML Islands is great feature to import modern look and feel to Win32 apps.

Color emoji(:sushi::cat::dog_face::rooster::map_of_japan::cat_face:‍:bust_in_silhouette:) is fine, touch friendly is also fine, and high dpi support is well.

 

And WinUI 3.0 or later, XAML Islands will be delivered with WinUI, and it supports Windows 10 Creators Update or later.

It means all UWP Controls are going to be able to use on all Win32 apps on all supported Windows 10.

 

I'm really looking forward to it being released. If you also feel it is interesting, please check the details on the roadmap of WinUI:

https://github.com/microsoft/microsoft-ui-xaml/blob/master/docs/roadmap.md

 

Leave a Reply

Your email address will not be published. Required fields are marked *

*

This site uses Akismet to reduce spam. Learn how your comment data is processed.