1. 前書き
前回の記事『MAUI 初体験:爽快』から早2ヶ月。当初は今年後半か来年にMAUIを研究する予定でしたが、MAUI Blazorがとても面白そうだったので計画を前倒ししました。Android、iOS、macOS、Windows間でUIを共有でき、1箇所のUIを追加・修正するだけで一貫したUI体験が得られます。
記事『Blazor Hybrid/MAUI の紹介と実践』でのMAUI Blazorの説明を見てみましょう:
MAUI
.NET マルチプラットフォーム アプリケーション UI (.NET MAUI) は、C# と XAML を使用してネイティブのモバイル・デスクトップ アプリケーションを作成するためのクロスプラットフォーム フレームワークです。.NET MAUI を使用すると、Android、iOS、macOS、Windows 上で動作するアプリを単一の共有コードベースから開発できます。
Blazor Hybrid アプリと .NET MAUI
Blazor Hybrid のサポートは .NET マルチプラットフォーム アプリ UI (.NET MAUI) フレームワークに組み込まれています。.NET MAUI には BlazorWebView コントロールが含まれており、Razor コンポーネントを埋め込み Web View にレンダリングします。.NET MAUI と Blazor を組み合わせることで、モバイル、デスクトップ、Web 間で一連の Web UI コンポーネントを再利用できます。
今回は、Blazor Server、Blazor Wasm、MAUI Blazor 間で UI を共有する実験について紹介します。これを完了すれば、今後のアプリ開発(UI の修正のみ)が非常に楽になります。
2. まずは各エンドの最終的な動作を確認
- Blazor Server: https://server.dotnet9.com/
- Blazor Wasm: https://wasm.dotnet9.com/
- MAUI (Android/Windows/macOS): https://github.com/dotnet9/Dotnet9/tree/develop/src/Dotnet9.MAUI(ソースコードから自分でビルド)
Windows デスクトップ、Blazor Server(オンライン)、Blazor Wasm(オンライン)、Android の動作

iPad Air、iOS、macOS デスクトップの動作

MAUI の各エンドでの公開ファイルの動作確認は行っていません(各プラットフォームの署名などが必要)。以下の説明に従ってプロジェクトを作成し、自分でビルドして体験してみてください。
iOS と macOS の画像素材は青城同学から提供いただきました。管理人の mbp には最新の macOS と xCode がインストールされていますが、プレビュー版の macOS のせいか、xCode が開けず、間接的に maui のビルドに影響している可能性があります。
macOS のバージョンと xCode のバージョン

xCode が使用不可の状態

VS でビルドエラー、後で解決予定

mbp をお使いの方は、プレビュー版の OS をインストールしないことをお勧めします。勇気ある行動は避けましょう...
3. 新しいプロジェクトを作成
MAUI の環境構築については、記事『MAUI で Masa Blazor を使用する』を参照してください。この記事では環境構築については説明せず、VS 2022 の最新プレビュー版を使用してプロジェクトテンプレートから直接プロジェクトを作成します。
3.1 Blazor Server プロジェクトを作成:Dotnet9.Server

3.2 Blazor WebAssembly プロジェクトを作成:Dotnet9.Wasm

3.3 MAUI Blazor プロジェクトを作成:Dotnet9.MAUI

3.4 共通点を探す
3 つのプロジェクトの 1 つ上のディレクトリで PowerShell を開き、tree /f を実行して詳細なディレクトリ・ファイル構造を確認:

3 つのテンプレートプロジェクトのファイル構造を注意深く確認し、共通のファイルを抽出します:
フォルダー PATH のリスト
ボリューム シリアル番号は 76F5-AF62
C:.
│ Dotnet9.sln
│
├─Dotnet9.MAUI
【1 ここでは数ファイル省略】
│ │
│ ├─Data
│ │ WeatherForecast.cs
│ │ WeatherForecastService.cs
│ │
│ ├─Pages
│ │ Counter.razor
│ │ FetchData.razor
│ │ Index.razor
【2 ここでは数ファイル省略】
│ │
│ ├─Shared
│ │ MainLayout.razor
│ │ MainLayout.razor.css
│ │ NavMenu.razor
│ │ NavMenu.razor.css
│ │ SurveyPrompt.razor
【3 ここでは数ファイル省略】
│
├─Dotnet9.Server
│ │ App.razor
【4 ここでは数ファイル省略】
│ │
│ ├─Data
│ │ WeatherForecast.cs
│ │ WeatherForecastService.cs
│ │
│ ├─Pages
│ │ Counter.razor
│ │ Error.cshtml
│ │ Error.cshtml.cs
│ │ FetchData.razor
│ │ Index.razor
│ │ _Host.cshtml
│ │ _Layout.cshtml
│ │
│ ├─Properties
│ │ launchSettings.json
│ │
│ ├─Shared
│ │ MainLayout.razor
│ │ MainLayout.razor.css
│ │ NavMenu.razor
│ │ NavMenu.razor.css
│ │ SurveyPrompt.razor
【5 ここでは数ファイル省略】
│
└─Dotnet9.Wasm
【6 ここでは数ファイル省略】
│
├─Pages
│ Counter.razor
│ FetchData.razor
│ Index.razor
│
├─Properties
│ launchSettings.json
│
├─Shared
│ MainLayout.razor
│ MainLayout.razor.css
│ NavMenu.razor
│ NavMenu.razor.css
│ SurveyPrompt.razor
【7 ここでは数ファイル省略】
Data ディレクトリと Pages ディレクトリが存在しています(ただし Wasm プロジェクトには Data ディレクトリはなく、サンプルクラスは FetchData.razor の @code{} 内に直接記述されています)。これらのファイルを直接クラスライブラリに抽出すればよいので、実行します。
4. UI を Razor クラスライブラリに抽出
Razor クラスライブラリを作成:Dotnet9.WebApp

以下、UI の抽出を開始

上の図のように、Dotnet9.MAUI プロジェクトの Data、Pages、Shared の 3 つのディレクトリと Main.razor ファイルを Dotnet9.WebApp プロジェクトに切り取り、切り取った後の対応するファイルの名前空間 Dotnet9.MAUI[xxx] を Dotnet9.WebApp[xxx] に変更します。Dotnet9.WebApp プロジェクトの _Import.razor ファイルを開き、Dotnet9.MAUI プロジェクトの _Import.razor ファイルの一部の名前空間を参考にして、以下のように変更します:
@using System.Net.Http
@using Microsoft.AspNetCore.Authorization
@using Microsoft.AspNetCore.Components.Forms
@using Microsoft.AspNetCore.Components.Routing
@using Microsoft.AspNetCore.Components.Web
@using Microsoft.AspNetCore.Components.Web.Virtualization
@using Microsoft.JSInterop
@using Dotnet9.WebApp
@using Dotnet9.WebApp.Shared
上の一部の名前空間は削除しても構いません(未確認)。Dotnet9.WebApp プロジェクトをビルドし、正しくコンパイルできるか確認します。
5. 各エンドのプロジェクトを修正
5.1 MAUI プロジェクト
Dotnet9.WebAppプロジェクトの参照を追加Program.csのusing Dotnet9.MAUI.Data;をusing Dotnet9.WebApp.Data;に変更Data、Pages、Sharedの 3 つのディレクトリとMain.razorファイルを削除(前の手順で切り取った場合はこの手順は不要)_Imports.razorファイルを修正。主にDotnet9.WebAppプロジェクトの名前空間参照を追加
@using System.Net.Http
@using Microsoft.AspNetCore.Components.Forms
@using Microsoft.AspNetCore.Components.Routing
@using Microsoft.AspNetCore.Components.Web
@using Microsoft.AspNetCore.Components.Web.Virtualization
@using Microsoft.JSInterop
@using Dotnet9.MAUI
@using Dotnet9.WebApp
@using Dotnet9.WebApp.Shared
MauiProgram.csの名前空間参照を変更:using Dotnet9.MAUI.Data;⇒using Dotnet9.WebApp.Data;MainPage.xamlを開き、ルーティングコンポーネントの名前空間参照を修正
名前空間 xmlns:webApp="clr-namespace:Dotnet9.WebApp;assembly=Dotnet9.WebApp" を追加し、コードを以下のように修正:
修正前:
<RootComponent Selector="#app" ComponentType="{x:Type local:Main}" />
修正後:
<RootComponent Selector="#app" ComponentType="{x:Type webApp:Main}" />
修正が完了したら、Dotnet9.MAUI プロジェクトをビルドして実行しましょう。次に Dotnet9.Server プロジェクトを修正します。
5.2 Blazor Server プロジェクト
Dotnet9.WebAppプロジェクトの参照を追加Program.csのusing Dotnet9.Server.Data;をusing Dotnet9.WebApp.Data;に変更Dataディレクトリを削除Pagesディレクトリ内のCounter.razor、FetchData.razor、Index.razorの 3 ファイル(および同名の.cs、.cssファイル)を削除Sharedディレクトリを削除_Imports.razorファイルを修正。主にDotnet9.WebAppプロジェクトの名前空間参照を追加
@using System.Net.Http
@using Microsoft.AspNetCore.Authorization
@using Microsoft.AspNetCore.Components.Authorization
@using Microsoft.AspNetCore.Components.Forms
@using Microsoft.AspNetCore.Components.Routing
@using Microsoft.AspNetCore.Components.Web
@using Microsoft.AspNetCore.Components.Web.Virtualization
@using Microsoft.JSInterop
@using Dotnet9.Server
@using Dotnet9.WebApp
@using Dotnet9.WebApp.Shared
App.razorファイルを削除し、./Pages/_Host.cshtmlファイルを開き、名前空間参照@using Dotnet9.WebAppを追加し、コードを以下のように修正:
修正前:
<component type="typeof(App)" render-mode="ServerPrerendered" />
修正後:
<component type="typeof(Main)" render-mode="ServerPrerendered" />
修正が完了したら、Dotnet9.Server プロジェクトをビルドして実行しましょう。次に Dotnet9.Wasm プロジェクトを修正します。
5.3 Blazor Wasm プロジェクト
Dotnet9.WebAppプロジェクトの参照を追加Pages、SharedディレクトリとApp.razorファイルを削除Program.csのusing Dotnet9.Wasm;をusing Dotnet9.WebApp;に変更し、コードを修正
修正前
builder.RootComponents.Add<App>("#app");
修正後
builder.RootComponents.Add<Main>("#app");
_Imports.razorファイルを修正。主にDotnet9.WebAppプロジェクトの名前空間参照を追加
@using System.Net.Http
@using Microsoft.AspNetCore.Authorization
@using Microsoft.AspNetCore.Components.Authorization
@using Microsoft.AspNetCore.Components.Forms
@using Microsoft.AspNetCore.Components.Routing
@using Microsoft.AspNetCore.Components.Web
@using Microsoft.AspNetCore.Components.Web.Virtualization
@using Microsoft.JSInterop
@using Dotnet9.Server
@using Dotnet9.WebApp
@using Dotnet9.WebApp.Shared
修正が完了したら、Dotnet9.Wasm プロジェクトをビルドして実行します。これで 3 つのプロジェクトテンプレートの修正が完了しました。最終的なソリューションは以下の図のようになります:

6 まとめ
まとめると以下の図のようになります:

- Dotnet9.WebApp:blazor コンポーネント関連のコードやルーティングコンポーネントなどを格納し、他のプロジェクトから参照されるプロジェクト
- Dotnet9.Server:Blazor Server テンプレートプロジェクト
- Dotnet9.Wasm:Blazor WebAssembly プロジェクト
- Dotnet9.MAUI:MAUI Blazor プロジェクト
一言で言うと:UI を Razor クラスライブラリ Dotnet9.WebApp にカプセル化し、他のエンドのプロジェクト(Dotnet9.Server、Dotnet9.MAUI、Dotnet9.Wasm)がこのプロジェクトを参照することで UI の共有を実現します。
- 本文のコード:https://github.com/dotnet9/Dotnet9
- 原文:https://dotnet9.com/2022/06/Share-razor-library-between-maui-and-blazor-server-or-client
参考