Windows 10 October Update (Version 1809) で XAML Islands という機能が使えるようになりました。Windows 10 の UI コントロール (UWP のコントロール) が WPF や Windows Forms アプリケーションで使える夢の機能です。
詳細は公式ドキュメントへ
XAML Islands は開発者 Preview ではありますすが、ビルドして実行できます。この記事は、いくつか実際に試してみた記事の目次です。
Windows 10 October Update (Version 1809) で XAML Islands という機能が使えるようになりました。Windows 10 の UI コントロール (UWP のコントロール) が WPF や Windows Forms アプリケーションで使える夢の機能です。
詳細は公式ドキュメントへ
XAML Islands は開発者 Preview ではありますすが、ビルドして実行できます。この記事は、いくつか実際に試してみた記事の目次です。
.NET Core はバージョン 3.0 の新機能として Windows デスクトップ アプリケーションをサポートします。まだ Preview ではありますが、ビルドして実行できます。
この記事は、いくつか実際に試してみた記事の目次です。
この頃に .NET Core 3.0 alpha 版 + Visual Studio 2017 15.9 Preview 版で実際にビルドして実行できるようになりました。.NET Core 3.0 でのデスクトップアプリ開発の最初の一歩です。
この頃に .NET Core 3.0 preview 版 + Visual Studio 2019 Preview 版でより実装が進みストレスなくコードを書いてビルドして実行できるようになりました。alpha 版から変わったポイントも多くあります。
.NET Framework では 多くのことが Visual Studio の GUI で操作してきましたが .NET Core でコマンドラインを使う機会が増えました。.NET Core ではコマンドラインでの操作にもなれて行く必要があります。
先日、WPF アプリケーションでの XAML Islands を試す記事を書きました。
その中で、レイアウトが思った通りに表示されないという場面がありました。
Windows の表示スケールを変更します。
私が普段使ってる環境が175%だったので期待した表示になりませんでした。
まだ、表示スケールの変更には未対応のようです。
先日、WPF アプリケーションでの XAML Islands を試す記事を書きました。
しかしこの方法だと、使う UWP のエレメント名を文字列で指定せねばならず、プロパティも ChildChanged イベントでの設定になます。また階層化された複数のエレメントを配置するなどもできません。
最近このパターンばかりですが、コードで全てを設定すれば解決します。
次のように使いたいエレメントをインスタンス化して、WindowsXamlHost の Child プロパティに設定しても使えるようです。
using System; using System.Windows; namespace WpfApp1 { public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); { var progressRing = new Windows.UI.Xaml.Controls.ProgressRing { IsActive = true, Width = 300, Height = 200, }; windowsXamlHost1.Child = progressRing; } { var inkCanvas = new Windows.UI.Xaml.Controls.InkCanvas { Width = 300, Height = 200, }; inkCanvas.InkPresenter.InputDeviceTypes = Windows.UI.Core.CoreInputDeviceTypes.Mouse | Windows.UI.Core.CoreInputDeviceTypes.Pen; windowsXamlHost2.Child = inkCanvas; } } } }
エレメントを階層する場合もこの様に。
using System; using System.Windows; namespace WpfApp1 { public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); var grid = new Windows.UI.Xaml.Controls.Grid(); { var progressRing = new Windows.UI.Xaml.Controls.ProgressRing { IsActive = true, Width = 300, Height = 200, }; grid.Children.Add(progressRing); } { var inkCanvas = new Windows.UI.Xaml.Controls.InkCanvas { Width = 300, Height = 200, }; inkCanvas.InkPresenter.InputDeviceTypes = Windows.UI.Core.CoreInputDeviceTypes.Mouse | Windows.UI.Core.CoreInputDeviceTypes.Pen; grid.Children.Add(inkCanvas); } windowsXamlHost1.Child = grid; } } }
この例では、ProgressRing と InkCanvas を Grid の子要素として重ねて表示されるようにしています。
私の環境では行わなくても動作しましたが、これらの方法はドキュメントによると次の一文が最初に必要だと書いてあるような気がします。
global::Windows.UI.Xaml.Hosting.WindowsXamlManager.InitializeForCurrentThread();
この WindowsXamlManager クラスの InitializeForCurrentThread メソッドですが、Windows.UI.Xaml.Hosting.HostingContract (C:\Program Files (x86)\Windows Kits\10\References\<バージョン>\Windows.UI.Xaml.Hosting.HostingContract\<バージョン>\Windows.UI.Xaml.Hosting.HostingContract.winmd) を参照に追加すると使用できました。
InitializeForCurrentThread が必要になるパターンを確認してみました。
Windows のバージョン 1809 で XAML Islands (MS のドキュメントの翻訳では XAML 諸島) という機能が使えるようになりました。
Windows 10 の UI コントロール (UWP のコントロール) が WPF や Windows Forms アプリケーションで使える夢の機能です。
詳細は公式ドキュメントへ
Windows フォームで試した記事はこちら
今度は WPF アプリケーションで試してみます。
まずは、NuGet で Microsoft.Toolkit.Wpf.UI.XamlHost をインストールします。
以前は、プレリリースを含めるのチェックが必要だった記憶がありますが、今は不要のようです。
NuGet パッケージをインストールすると、XamlHost コントロールが使えるようになるので、XAML で配置します。
これが一番の難関でしょう。Window に次の属性を追加します。
xmlns:xamlhost="clr-namespace:Microsoft.Toolkit.Wpf.UI.XamlHost;assembly=Microsoft.Toolkit.Wpf.UI.XamlHost"
いつものように 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 |
XamlHost コントロールは、UWP のコントロールを入れるられるコンテナのようなものです。実際に中に配置するコントロールを設定します。今回もまずは ProgressRing を表示してみます。
XamlHost のプロパティ InitialTypeName に Windows.UI.Xaml.Controls.ProgressRing と設定しています。
InitialTypeName で指定したコントロールのプロパティを XML から設定するものであないようです。実際に生成されたイベントの中で設定をします。
XamlHost の ChildChanged イベントにハンドラを設定します。
<xamlhost:WindowsXamlHost Width="300" Height="200" ChildChanged="WindowsXamlHost_ChildChanged" InitialTypeName="Windows.UI.Xaml.Controls.ProgressRing"/>
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 が表示されました。これで時間のかかる処理を行う際も安心ですね。
ちょっとレイアウトが思っているのと違ってしまいました、こちらは今後検証して行きたいと思います。
UWP のコントロールが使えると聞いて皆が気になるあのコントロール InkCanvas も試してみます。
XamlHost コントロールをもう一つ配置して各種設定をして行きます。
<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 アプリケーションという要件があっても安心ですね。
先日 .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)
まだ使えないようです。残念。
先日、Windows フォームアプリケーションでの XAML Islands を試す記事を書きました。
しかしこの方法だと、使う UWP のエレメント名を文字列で指定せねばならずプロパティも ChildChanged イベントでの設定になります。また階層化された複数のエレメントを配置するなどもできません。
最近このパターンばかりですが、コードで全てを設定すれば解決します。
次のように使いたいエレメントをインスタンス化して、WindowsXamlHost の Child プロパティに設定しても使えるようです。
using System; using System.Windows.Forms; namespace WindowsFormsApp1 { public partial class Form1 : Form { public Form1() { InitializeComponent(); { var progressRing = new Windows.UI.Xaml.Controls.ProgressRing { IsActive = true, Width = 300, Height = 200, }; windowsXamlHost1.Child = progressRing; } { var inkCanvas = new Windows.UI.Xaml.Controls.InkCanvas { Width = 300, Height = 200, }; inkCanvas.InkPresenter.InputDeviceTypes = Windows.UI.Core.CoreInputDeviceTypes.Mouse | Windows.UI.Core.CoreInputDeviceTypes.Pen; windowsXamlHost2.Child = inkCanvas; } } } }
エレメントを階層する場合もこの様に。
using System; using System.Windows.Forms; namespace WindowsFormsApp1 { public partial class Form1 : Form { public Form1() { InitializeComponent(); var grid = new Windows.UI.Xaml.Controls.Grid(); { var progressRing = new Windows.UI.Xaml.Controls.ProgressRing { IsActive = true, Width = 300, Height = 200, }; grid.Children.Add(progressRing); } { var inkCanvas = new Windows.UI.Xaml.Controls.InkCanvas { Width = 300, Height = 200, }; inkCanvas.InkPresenter.InputDeviceTypes = Windows.UI.Core.CoreInputDeviceTypes.Mouse | Windows.UI.Core.CoreInputDeviceTypes.Pen; grid.Children.Add(inkCanvas); } windowsXamlHost1.Child = grid; } } }
この例では、ProgressRing と InkCanvas を Grid の子要素として重ねて表示されるようにしています。
私の環境では行わなくても動作しましたが、これらの方法はドキュメントによると次の一文が最初に必要だと書いてあるような気がします。
global::Windows.UI.Xaml.Hosting.WindowsXamlManager.InitializeForCurrentThread();
この WindowsXamlManager クラスの InitializeForCurrentThread メソッドですが、Windows.UI.Xaml.Hosting.HostingContract (C:\Program Files (x86)\Windows Kits\10\References\<バージョン>\Windows.UI.Xaml.Hosting.HostingContract\<バージョン>\Windows.UI.Xaml.Hosting.HostingContract.winmd) を参照に追加すると使用できました。
XAML Islands を試している際に次の例外が出ることがありました。
Microsoft.Windows.Interop.UWPTypeFactory: Could not create type: XXXXXXXXXX
原因は、InitialTypeName の設定ミスです。
このプロパティには 使うエレメントの名前を正確に (例えば "Windows.UI.Xaml.Controls.InkCanvas" のように) 書かねばなりません。しかしこのプロパティの型、文字列なので書き間違えていてもコンパイル時などにチェックしてくれません。
エレメントの名前を書き間違えないように注意して、場合によっては複数人でのダブルチェック、トリプルチェックなどを駆使して、正確に記述するよう気を付けましょう。