rksoftware

Visual Studio とか C# とかが好きです

WPF アプリケーションで最新機能に対応する XAML Islands

Windows のバージョン 1809 で XAML Islands (MS のドキュメントの翻訳では XAML 諸島) という機能が使えるようになりました。
Windows 10 の UI コントロール (UWP のコントロール) が WPF や Windows Forms アプリケーションで使える夢の機能です。

詳細は公式ドキュメントへ

Windows フォームで試した記事はこちら

今度は WPF アプリケーションで試してみます。

■ NuGet パッケージのインストール

まずは、NuGet で Microsoft.Toolkit.Wpf.UI.XamlHost をインストールします。
f:id:rksoftware:20190106145756j:plain

以前は、プレリリースを含めるのチェックが必要だった記憶がありますが、今は不要のようです。

■ XAML で XamlHost コントロールを配置

NuGet パッケージをインストールすると、XamlHost コントロールが使えるようになるので、XAML で配置します。

xmlns の追加

これが一番の難関でしょう。Window に次の属性を追加します。

xmlns:xamlhost="clr-namespace:Microsoft.Toolkit.Wpf.UI.XamlHost;assembly=Microsoft.Toolkit.Wpf.UI.XamlHost"

Xamlost エレメントの配置

いつものように XAML を書いて行きます。

<xamlhost:WindowsXamlHost Width="300" Height="200"
                          ChildChanged="WindowsXamlHost_ChildChanged"
                          InitialTypeName="Windows.UI.Xaml.Controls.ProgressRing"/>

■ 参照の設定

以前は 6 つほど参照を追加した記憶がありますが、今回は二つだけで済みました。
※もっと多くのコントロールのプロパティなどを設定する場合、他にも参照がいつ様になります

ファイル 場所
Windows.Foundation.UniversalApiContract.winmd C:\Program Files (x86)\Windows Kits\10\References<sdk version>\Windows.Foundation.UniversalApiContract<version>
Windows.WinMD C:\Program Files (x86)\Windows Kits\10\UnionMetadata\Facade

参考
https://docs.microsoft.com/ja-jp/windows/uwp/porting/desktop-to-uwp-enhance#first-set-up-your-project

■ プロパティを設定

XamlHost コントロールは、UWP のコントロールを入れるられるコンテナのようなものです。実際に中に配置するコントロールを設定します。今回もまずは ProgressRing を表示してみます。
XamlHost のプロパティ InitialTypeNameWindows.UI.Xaml.Controls.ProgressRing と設定しています。

InitialTypeName で指定したコントロールのプロパティを XML から設定するものであないようです。実際に生成されたイベントの中で設定をします。
XamlHost の ChildChanged イベントにハンドラを設定します。

<xamlhost:WindowsXamlHost Width="300" Height="200"
                          ChildChanged="WindowsXamlHost_ChildChanged"
                          InitialTypeName="Windows.UI.Xaml.Controls.ProgressRing"/>

XAML

XAML は次のようにしました。

<Window x:Class="WpfApp1.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:WpfApp1"
        xmlns:xamlhost="clr-namespace:Microsoft.Toolkit.Wpf.UI.XamlHost;assembly=Microsoft.Toolkit.Wpf.UI.XamlHost"
        mc:Ignorable="d"
        Title="MainWindow" Height="300" Width="400" Background="LightGray">
    <Grid>
        <xamlhost:WindowsXamlHost Width="300" Height="200"
                                  ChildChanged="WindowsXamlHost_ChildChanged"
                                  InitialTypeName="Windows.UI.Xaml.Controls.ProgressRing"/>
    </Grid>
</Window>

イベントハンドラ

イベントハンドラの中で ProgressRing のプロパティを設定します。

private void WindowsXamlHost_ChildChanged(object sender, EventArgs e)
{
    var host = (Microsoft.Toolkit.Wpf.UI.XamlHost.WindowsXamlHost)sender;
    var progressRing = (Windows.UI.Xaml.Controls.ProgressRing)host.Child;
    if (progressRing != null)
    {
        progressRing.IsActive = true;
        progressRing.Width = 300;
        progressRing.Height = 200;
    }
}

■ 実行

実行してみると、UWP の ProgressRing が表示されました。これで時間のかかる処理を行う際も安心ですね。
f:id:rksoftware:20190106145822j:plain

ちょっとレイアウトが思っているのと違ってしまいました、こちらは今後検証して行きたいと思います。

■ InkCanvas

UWP のコントロールが使えると聞いて皆が気になるあのコントロール InkCanvas も試してみます。
XamlHost コントロールをもう一つ配置して各種設定をして行きます。

XAML

<Window x:Class="WpfApp1.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:WpfApp1"
        xmlns:xamlhost="clr-namespace:Microsoft.Toolkit.Wpf.UI.XamlHost;assembly=Microsoft.Toolkit.Wpf.UI.XamlHost"
        mc:Ignorable="d"
        Title="MainWindow" Height="300" Width="800" Background="LightGray">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition/>
            <ColumnDefinition/>
        </Grid.ColumnDefinitions>
        <xamlhost:WindowsXamlHost Grid.Column="0" Width="300" Height="200"
                                  ChildChanged="WindowsXamlHost_ChildChanged"
                                  InitialTypeName="Windows.UI.Xaml.Controls.ProgressRing"/>

        <xamlhost:WindowsXamlHost Grid.Column="1" Width="300" Height="200"
                                  ChildChanged="WindowsXamlHost_ChildChanged_1"
                                  InitialTypeName="Windows.UI.Xaml.Controls.InkCanvas"/>
    </Grid>
</Window>

イベントハンドラ

using System;
using System.Windows;

namespace WpfApp1
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        private void WindowsXamlHost_ChildChanged(object sender, EventArgs e)
        {
            var host = (Microsoft.Toolkit.Wpf.UI.XamlHost.WindowsXamlHost)sender;
            var progressRing = (Windows.UI.Xaml.Controls.ProgressRing)host.Child;
            if (progressRing != null)
            {
                progressRing.IsActive = true;
                progressRing.Width = 300;
                progressRing.Height = 200;
            }
        }

        private void WindowsXamlHost_ChildChanged_1(object sender, EventArgs e)
        {
            var host = (Microsoft.Toolkit.Wpf.UI.XamlHost.WindowsXamlHost)sender;
            var inkCanvas = (Windows.UI.Xaml.Controls.InkCanvas)host.Child;
            if (inkCanvas != null)
            {
                inkCanvas.Width = 300;
                inkCanvas.Height = 200;
                inkCanvas.InkPresenter.InputDeviceTypes =
                    Windows.UI.Core.CoreInputDeviceTypes.Mouse
                    | Windows.UI.Core.CoreInputDeviceTypes.Pen;
            }
        }
    }
}

InlCanvas にマウスで書けるように inkCanvas.InkPresenter.InputDeviceTypes = Windows.UI.Core.CoreInputDeviceTypes.Mouse | Windows.UI.Core.CoreInputDeviceTypes.Pen; という設定を行っています。

■ 実行

実行して文字を書いてみました。これで最新機能を実装する WPF アプリケーションという要件があっても安心ですね。
f:id:rksoftware:20190106145846j:plain

■ おまけ .NET Core 3.0

先日 .NET Core 3.0 での Windows フォームアプリケーションでは XAML Islands がまだ使えないませんでした。

デスクトップアプリも .NET Core で作る時代が近づいています。せっかくなので WPF でも XAML 諸島が .NET Core でも使えるか試してみました。
結果は

エラー NU1202 パッケージ Microsoft.Toolkit.Wpf.UI.XamlHost 5.0.1 は netcoreapp3.0 (.NETCoreApp,Version=v3.0) / win-x64 と互換性がありません。 パッケージ Microsoft.Toolkit.Wpf.UI.XamlHost 5.0.1 がサポートするもの: net462 (.NETFramework,Version=v4.6.2)

まだ使えないようです。残念。