.NET7でBlazorWebViewとWPFによるハイブリッドアプリのサンプル
Blazorの登場から随分時間が経過してしまいました。
C#でWebアプリケーションの実装ができるこの技術、とてもおもしろいですね。
本記事では、BlazorWebViewを使用しBlazorのコードを動作させつつ、WPFアプリケーションの中に埋め込むサンプルを紹介します。
Visual Studio 2022と.NET7を使い、WPFの都合でWindowsのみを対象しています。
BlazorWebView
BlazorWebViewは、.NETのBlazor Web UIフレームワークをデスクトップアプリケーションで使用できるようにするコンポーネントです。
これは、Blazorのコンポーネントをネイティブデスクトップアプリケーションでレンダリングし、同じコードベースでWebとデスクトップの両方を対象とすることを可能にします。
Blazor自体は、下位層にWebAssemblyやJavaScriptを用いて実行するWeb UIフレームワークで、C#とHTML、CSSを使用してWeb UIを構築できます。
しかし、BlazorWebViewを使用することで、BlazorのコンポーネントをローカルのWebViewコントロールに表示できます。
これにより、デスクトップアプリケーションがBlazorのフル機能を利用でき、コンポーネントの更新やUIの変更を簡単に行うことができます。
プロジェクトの作成
WPF+BlazorWebViewという組み合わせでのテンプレートは用意されていないため、手作業で整える必要があります。GitHubを検索してみると、初期テンプレートを用意してくれるものがいくつか見つかるので、そちらをインストールして使うのも1つの手です。
ここではWPFアプリケーションのテンプレートをもとにはじめます。
作成された各種コードや設定ファイルなどを変更していきます。
NuGet でパッケージの追加
BlazorWebViewのために、NuGetで「Microsoft.AspNetCore.Components.WebView.Wpf」のパッケージをインストールします。
csprojの設定変更
csprojファイルを開いて、冒頭にあるSDK属性を以下のように変更します。
Visual Studioのソリューションエクスプローラーからファイルを選んで、右クリック「プロジェクトファイルの編集」を使うと便利です。
旧: <Project Sdk="Microsoft.NET.Sdk">
↓
新: <Project Sdk="Microsoft.NET.Sdk.Razor">
またこの後に配置するwwwrootフォルダのコピー設定も追記しておきます。
<ItemGroup>
<Content Update="wwwroot\**">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
Blazorのコードを追加する
App クラスの編集
App.xaml.csファイルを開き、以下のコードを追加します。
private void Application_Startup(object sender, StartupEventArgs e)
{
var serviceCollection = new ServiceCollection();
serviceCollection.AddWpfBlazorWebView();
Resources.Add("services", serviceCollection.BuildServiceProvider());
}
App.xamlファイルを開き、追加した関数をStartup
に設定します。
<Application x:Class="WpfAppHybrid.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfAppHybrid"
Startup="Application_Startup"
StartupUri="MainWindow.xaml">
WebViewを配置する
MainWindow.xamlを開いて、BlazorWebViewを追加します。
ここでは下記のように編集しました。
<Window x:Class="WpfAppHybrid.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:blazor="clr-namespace:Microsoft.AspNetCore.Components.WebView.Wpf;assembly=Microsoft.AspNetCore.Components.WebView.Wpf"
xmlns:local="clr-namespace:WpfAppHybrid"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<blazor:BlazorWebView x:Name="blazorWebView" HostPage="wwwroot\index.html" Services="{DynamicResource services}">
<blazor:BlazorWebView.RootComponents>
<blazor:RootComponent Selector="#app" ComponentType="{x:Type local:MyPage}"/>
</blazor:BlazorWebView.RootComponents>
</blazor:BlazorWebView>
</Grid>
</Window>
wwwrootフォルダ以下の準備
csprojと同じ場所にwwwroot
フォルダを作成して、中にindex.html
ファイルを配置します。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover" />
<base href="/" />
<link href="app.css" rel="stylesheet" />
<link href="WpfAppHybrid.styles.css" rel="stylesheet" />
<title>WpfAppHybrid1</title>
</head>
<body>
<div id="app">Loading...</div>
<div id="blazor-error-ui">
An unhandled error has occurred.
<a href="" class="reload">Reload</a>
<a class="dismiss">🗙</a>
</div>
<script src="_framework/blazor.webview.js"></script>
</body>
</html>
続いてapp.css
ファイルを同じように作成し、内容を以下の通りとします。
#blazor-error-ui {
background: lightyellow;
bottom: 0;
box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2);
display: none;
left: 0;
padding: 0.6rem 1.25rem 0.7rem 1.25rem;
position: fixed;
width: 100%;
z-index: 1000;
}
#blazor-error-ui .dismiss {
cursor: pointer;
position: absolute;
right: 0.75rem;
top: 0.5rem;
}
Razorファイルを配置する
BlazorではRazorテンプレートを用いてページを作ります。ここではMyPage.razor
としてファイルを作成しました。
@using Microsoft.AspNetCore.Components.Web
<h3>Hello,WPFApplication</h3>
<p>@Message</p>
<button @onclick="OnClicked">Click button</button>
@code {
private string Message = "Hello,hybrid world";
private void OnClicked()
{
Message = "ボタンが押されました。";
}
}
実行結果
実行すると、以下のような画面が表示されます。ウィンドウ内に表示されているのはBlazorWebViewで、Razorテンプレートで書かれたページ内容です。
ボタンをクリックすると、C#で記述したコードが実行されて表示が変化します。
まとめ
うまく作業が進めばなんの問題もないWPF+BlazorWebViewによるハイブリッドアプリですが、実際には割と手間取っていました。
WPFの場合は、 AddWpfBlazorWebView
で追加することや @using Microsoft.AspNetCore.Components.Web
の漏れだったりでしょうか。今回の手順でうまくいくとは思いますので、本記事が同じようにはまった人の助けになれば幸いです。
次回は、Razor内に閉じないC#のデータのやり取りについて紹介できればと考えています。
参考
Discussion