.NET MAUI を試してみた - 後編 -

前編に引き続き .NET MAUI を試してみます。 MAUI のプロジェクトテンプレートのソースコードの中身を眺めつつその感想を記してみました。前編ではプロジェクトを作成して実行してみるところまででした。

前編からの繰り返しとなりますが、この記事を執筆している2021年12月現在 MAUI はまだプレビューバージョンで、記事の内容はリリース版と異なる可能性がある点ご注意ください。後編では記載のコードから名前空間を削除していることも予めお断りしておきます。

試した環境

  • Windows 11 21H2
  • Visual Studio 2022 Preview (17.1.0 Preview 1.1)
  • .NET SDK 6.0.100
  • .NET MAUI Preview 10

MAUI プラットフォーム部分

まずはプラットフォーム側のソースコードを眺めてみます。対象は Android プロジェクトです。

前編にあるソリューションエクスプローラーのスクリーンショットをご覧頂ければ分かるとおり、作りたての MAUI プロジェクトには MainActivity.csMainApplication.cs の2ファイルが存在しています。 Xamarin.Forms と比較すると Application が増えましたね。まずは Xamarin にもいた Activity の方から眺めてみましょう。

// Platforms/Android/MainActivity.cs

[Activity(Theme = "@style/Maui.SplashTheme", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation | ConfigChanges.UiMode | ConfigChanges.ScreenLayout | ConfigChanges.SmallestScreenSize)]
public class MainActivity : MauiAppCompatActivity
{
}

Xamarin.Forms では Activity にてアプリや Essentials の初期化処理を行っていました。 MAUI では MauiAppCompatActivity を継承するのみで特別な実装は無いようですね。。

続いて Application です。

// Platforms/Android/MainApplication.cs

[Application]
public class MainApplication : MauiApplication
{
    public MainApplication(IntPtr handle, JniHandleOwnership ownership)
        : base(handle, ownership)
    {
    }

    protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp();
}

Activity からいなくなった初期化処理は新メンバーの Application にいるのかと思いきや Application もそれらしき処理は見当たらず、 MauiApp なるものを作成しているだけでした。この MauiApp を作成する処理は Android に限らず iOS や Windows でも同様に、各プラットフォームのエントリーポイントから同じ呼び出し方をされています。

Xamarin.Forms では共通部分のプロジェクトを .NET Standard で実装するため Android や iOS などのプラットフォーム SDK を参照することができず、プラットフォームに依存する処理は全てプラットフォームのプロジェクトに実装せねばなりませんでした。 MAUI では基盤が .NET 6 に更新され共通部分からも各プラットフォーム SDK を参照できるようになり、初期化処理みたいなちょっとしたプラットフォーム依存処理は共通部分へと移動する方針なのでしょう。

MAUI 共通部分

続いて共通部分、プラットフォームから呼び出されていた MauiProgram を眺めてみます。

// MauiProgram.cs

public static class MauiProgram
{
    public static MauiApp CreateMauiApp()
    {
        var builder = MauiApp.CreateBuilder();
        builder
            .UseMauiApp<App>()
            .ConfigureFonts(fonts =>
            {
                fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
            });

        return builder.Build();
    }
}

どこかで見かけたような流れですね。。
最近の .NET では汎用ホストを利用してコンソールアプリや Web アプリを実装します 1 。 MAUI も同様にホストで実行される構成になった様です。この構造は普段から見慣れているのでとても分かりやすくて良いと思います。

処理を眺めてみるとホストビルダーに UseMauiApp<App> で起動されるアプリケーションを、 ConfigureFonts でフォントの登録を行っています。ホストビルダーへの登録はフォント以外にも画像やエフェクト、アニメーション、ハンドラーと呼ばれる各プラットフォーム特異な処理が用意されていました。またこのホストビルダーには Microsoft Extensions 系のログやサービス登録の口が生えていますので、普段 .NET アプリ(主に ASP.NET Core )で利用しているログや DI の機能をそのまま利用できそうです。

App は特に中身が無かったので省略します。

MAUI Blazor 共通部分

最後に MAUI Blazor アプリの共通部分を眺めてみます。

// MauiProgram.cs

public static class MauiProgram
{
    public static MauiApp CreateMauiApp()
    {
        var builder = MauiApp.CreateBuilder();
        builder
            .RegisterBlazorMauiWebView()
            .UseMauiApp<App>()
            .ConfigureFonts(fonts =>
            {
                fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
            });

        builder.Services.AddBlazorWebView();
        builder.Services.AddSingleton<WeatherForecastService>();

        return builder.Build();
    }
}

ホストビルダーを作成して機能を登録する流れは先ほどと同じですが、 Blazor アプリは Blazor を表示するための WebView を登録しています。 WebView をホストに登録しつつ WebView を DI サービスにも登録するというあまり直感的に感じないことをしています。それぞれ何をしているのでしょうか。

// Microsoft.AspNetCore.Components.WebView.Maui.BlazorWebViewRegistrationExtensions

// https://github.com/dotnet/maui/blob/6.0.101-preview.10/src/BlazorWebView/src/Maui/BlazorWebViewRegistrationExtensions.cs

public static MauiAppBuilder RegisterBlazorMauiWebView(this MauiAppBuilder appHostBuilder)
{
    if (appHostBuilder is null)
    {
        throw new ArgumentNullException(nameof(appHostBuilder));
    }

    appHostBuilder.ConfigureMauiHandlers(handlers => handlers.AddHandler<IBlazorWebView, BlazorWebViewHandler>());

    return appHostBuilder;
}

前者の RegisterBlazorMauiWebView の中身を見ると、 IBlazorWebView に対して BlazorWebViewHandler をハンドラー登録しています。 MAUI では View の各プラットフォームのレンダリングをハンドラーで行う 2 そうで、 BlazorWebViewHandler は各プラットフォームのネイティブ WebView を実際に扱っていました 3 4 。ちなみに Xamarin.Forms では ViewRenderer を実装してアセンブリの Attribute で View と紐付けを行っていました。 MAUI は View のレンダリング・登録方法がだいぶ変わりましたね。

後者の AddBlazorWebView は .NET 6 の Windows Forms や WPF 上で Blazor をレンダリングするための機能だそうです 5 。 WPF と MAUI とで処理を共通化できるなんて .NET 6 は凄いなぁ。。と思いましたが、よくよく考えると MAUI の Windows プラットフォームの実装は WPF でした。

App の中身が無いのはこちらも同じなので省略して、最後に MainPage の中身を見て終わりにします。

<!-- MainPage.xaml -->

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
            xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
            xmlns:b="clr-namespace:Microsoft.AspNetCore.Components.WebView.Maui;assembly=Microsoft.AspNetCore.Components.WebView.Maui"
            xmlns:local="clr-namespace:MauiBlazorSampleApp"
            x:Class="MauiBlazorSampleApp.MainPage"
            BackgroundColor="{DynamicResource PageBackgroundColor}">

   <b:BlazorWebView HostPage="wwwroot/index.html">
       <b:BlazorWebView.RootComponents>
           <b:RootComponent Selector="#app" ComponentType="{x:Type local:Main}" />
       </b:BlazorWebView.RootComponents>
   </b:BlazorWebView>

</ContentPage>

RegisterBlazorMauiWebView で登録された BlazorWebView を表示していますね。このアプリは MainPage 以外の XAML ページは存在しないため、この BlazorWebView に表示されている Blazor がページ遷移まで含めて画面表示を行えることが窺えます。

終わりに

前編、後編の2つに分けてリリース前の MAUI を触ってみました。この熟れた Xamarin.Forms 感がたまりません。
GA まであと数ヶ月。期待してウォッチしていきたいと思います。


執筆担当者プロフィール
中谷 大造

中谷 大造(日本ビジネスシステムズ株式会社)

情報システム部の中谷です。社内用スクラッチアプリの開発をしています。

担当記事一覧