「.NET MAUI(Multi-platform App UI)」とは――Xamarn.Formsからの改良ポイント:.NET 6移行入門(6)
.NET 6の現状を把握し、具体的な移行方法を学ぶ連載。今回は、.NET MAUIの概要とXamarn.Formsからの改良ポイントについてまとめる。
.NET MAUIリリース
2022年5月23日(米国時間)、.NET MAUIがGA(一般提供)されました。Microsoftは「.NET MAUIは、.NET Multi-platform App UIの略称で、モバイル、タブレット、デスクトップにまたがるネイティブデバイスのアプリケーションを構築するフレームワークです」と紹介しています。
.NET MAUI自体はGAされましたが、2022年6月26日の原稿執筆では開発環境「Visual Studio」は、Windows版も、Mac版も最新のプレビュー版での対応です。こちらは程なく安定版のVisual Studioでも対応されるでしょう。
.NET MAUIはXamarin.Formsから全く違う製品名に変わりましたが、何が違うのでしょうか? 分かりやすくいえば、.NET MAUIはXamarin.Formsの改良版です。本稿では、.NET MAUIはXamarin.Formsと比べて、どこが改良されたのか、具体的に解説します。
Xamarin.Formsと.NET MAUIの主な違い
Xamarin.Formsと.NET MAUIの主な違いを表でまとめました。
| Xamarin.Forms | .NET MAUI | |
|---|---|---|
| プロジェクトの構造 | 非SDKスタイル(Franken-proj) プラットフォームごとに個別のプロジェクトを使用 |
SDKスタイル 単一のプロジェクトで、複数プラットフォームをターゲットにできる |
| ツールチェーン | .NET Framework | .NET CLI |
| リソース管理 | プラットフォームごとに個別管理 プラットフォーム固有のデバイスの解像度に応じたイメージを準備する必要がある |
単一のプロジェクト内で一元管理 SVGを準備すれば各プラットフォームの解像度のPNGに変換でされる |
| スタートアップ | 独自(Appクラス) | Generic Host対応 |
| ホットリロード | 完全なXAMLホットリロード (SDK 5.xおよびVisual Studio 2019 16.9以降) |
完全な.NETホットリロード 完全なXAMLホットリロード |
| UIコントロールアーキテクチャ | レンダラー | ハンドラー |
以降、.NET MAUIの改良点の中で、特に注目のポイントを紹介します。
.NET MAUIのプロジェクト構成
.NET MAUIのプロジェクトは下図のようになっています。
Xamarin.Formsのような「単一の共有プロジェクト+複数のプラットフォーム固有プロジェクト」の構成ではなく、単一のプロジェクトで、複数プラットフォームをターゲットにできるようになりました。
Xamarin.Formsのアーキテクチャに起因する問題点
Xamarin.Formsは登場当時、画期的なフレームワークでした。ですが、利用が進むにつれてアーキテクチャに起因する問題点も顕在化しました。
Xamarin.Formsのレンダラーアーキテクチャ
下図のようにレンダラーは共有プロジェクトのUIコントロール実装に依存しています。
Xamarin.Formsレンダラーアーキテクチャの問題点
- レンダラーがXamarin.FormsのUIコンポーネントと密結合している
- アセンブリスキャンとリフレクションというコストの大きい処理を使用してUIコントロールのレンダラーを取得するので遅い
- 共有プロジェクトからネイティブUIコントロールをカスタマイズする場合、たった1つのプロパティの変更でも、共有プロジェクトとネイティブプロジェクトにまたがるレンダリングの仕組みを理解した上で、定型的な多くのコードを記述する必要があり、非常に手間がかかる
.NET MAUIの最大の変更点
Xamarin.Formsからの最大の変更点は、レンダラーアーキテクチャからハンドラーアーキテクチャへの変更です。ハンドラーアーキテクチャの採用によってプラットフォーム固有のレンダリングの責務を、抽象化UIフレームワークの実装から分離しました。
.NET MAUIで導入されたハンドラーアーキテクチャ
下図のようにハンドラーはインタフェースにのみ依存し、抽象化UIコントロールの実装には依存しません。
また、共有コード内で複数のプラットフォームのネイティブUIコントロールを直接操作できます。
これによって、以下に述べるようなアドバンテージが生まれました。
.NET MAUIハンドラーアーキテクチャのアドバンテージ
- ハンドラーはアセンブリスキャンが不要となり速度が向上した
- 共有コード内でもネイティブUIコントロールに直接アクセスしてプロパティを変更できる
- ハンドラーは、スタートアップコードのGeneric Host内に直接記述も可能。特定のコントロールまたはアプリ全体で使用される全てのコントロールのカスタマイズが簡単に実装できる
Generic Hostは、アプリの起動やシャットダウンのようなライフタイム管理や、アプリの構成やロギング、DI(Dependency Injection:依存性の注入)といった、アプリのビジネスロジック自体とは関係ない、基盤となる機能をカプセル化するオブジェクトです。ビジネスロジックとアプリの基盤に関わる機能を分離できるので、クリーンな構造にすることができます。
Generic Hostの詳細はこちらをご覧ください。
.NET MAUIのスタートアップコードの例
.NET MAUIではGeneric Hostに対応したので、スタートアップの処理がXamarin.Formsと大きく異なります。ここからは、.NET MAUIのスタートアップコードのサンプルを見ていきます。なお以下のコードは、説明するために調整されたものです。ベストプラクティスではないのでご注意ください。
まずは、コード全体です。Generic Host内では、次の4つを行っています。
- 起動するアプリクラスの指定
- フォントの登録
- Dependency Injection
- ハンドラーを利用したUIのカスタマイズ
スタートアップコードはMauiProgram.csに記述します。
using MauiUICustomizeSample.Controls;
using Microsoft.Maui.Handlers;
using Microsoft.Maui.Platform;
namespace MauiUICustomizeSample;
public static class MauiProgram
{
public static MauiApp CreateMauiApp()
{
var builder = MauiApp.CreateBuilder();
builder .UseMauiApp<App>() // 起動するアプリクラスの指定
.ConfigureFonts(fonts => // リソースフォルダ内のフォントの登録
{
fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold");
});
// Dependency injection : AppSlell クラスをDIコンテナに登録
builder.Services.AddTransient<AppShell>();
// 全てのLabelのカスタマイズ
LabelHandler.Mapper.AppendToMapping(nameof(IView.Background), (handler, view) =>
{
if (view is Label)
{
#if IOS
handler.PlatformView.BackgroundColor = Colors.MediumSpringGreen.ToPlatform();
#elif ANDROID
handler.PlatformView.SetBackgroundColor(Colors.MediumSpringGreen.ToPlatform());
#endif
}
});
// 特定のインスタンスのButtonのカスタマイズ
ButtonHandler.Mapper.AppendToMapping(nameof(IView.Background), (handler, view) =>
{
if (view is MyButton)
{
#if IOS
handler.PlatformView.BackgroundColor = Colors.LightCoral.ToPlatform();
handler.PlatformView.SetTitleColor(Colors.White.ToPlatform(), UIKit.UIControlState.Normal);
handler.PlatformView.Layer.CornerRadius = 7;
#elif ANDROID
handler.PlatformView.SetBackgroundColor(Colors.LightCoral.ToPlatform());
#endif
}
});
return builder.Build();
}
}
個別の処理について詳説します。
・MAUI用のGeneric Hostのビルダーを作成
MauiAppのCreateBuilderメソッドでMauiAppBuilderのインスタンスが作成されます。
var builder = MauiApp.CreateBuilder();
・起動するアプリクラス(このサンプルではAppクラス)を指定
MauiAppBuilderのUseMauiAppメソッドの型パラメーターに起動するアプリクラスの型(App)を指定します。
builder.UseMauiApp<App>()
AppクラスはApp.xaml.csで定義されています。
namespace MauiUICustomizeSample;
public partial class App : Application
{
public App(AppShell appShell)
{
InitializeComponent();
MainPage = appShell;
}
}
・フォントの登録
プロジェクト内の「/Resources/Fonts」内に配置されたフォントを登録します。
MauiAppBuilderのConfigureFontsメソッド内のデリゲートで登録します。
.ConfigureFonts(fonts =>
{
fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold");
});
・AppSlellクラスをDIコンテナに登録
AppSlellクラスをDIコンテナに登録し、インスタンスが注入されるようにします。
builder.Services.AddTransient<AppShell>();
これによって、App.xaml.csのコンストラクタのappShellパラメーターにインスタンスが注入されます。
namespace MauiUICustomizeSample;
public partial class App : Application
{
public App(AppShell appShell)
{
InitializeComponent();
MainPage = appShell;
}
}
・ハンドラーを利用したUIのカスタマイズ
サンプルでは、「特定の種類のUIコントロール全てをカスタマイズする場合」「あるUIコントロールの特定のインスタンスのみをカスタマイズする場合」の2つのパターンでUIをカスタマイズしています。両者とも、Xamarin.Formsと比較して簡単にカスタマイズできます。
・特定の種類のUIコントロール全てをカスタマイズする場合
Labelコントロール全ての背景色をMediumSpringGreenに変更します。
// 全てのLabelのカスタマイズ
LabelHandler.Mapper.AppendToMapping(nameof(IView.Background), (handler, view) =>
{
if (view is Label)
{
#if IOS
handler.PlatformView.BackgroundColor = Colors.MediumSpringGreen.ToPlatform();
#elif ANDROID
handler.PlatformView.SetBackgroundColor(Colors.MediumSpringGreen.ToPlatform());
#endif
}
});
AppendToMappingのActionデリゲート内でUIコントロールの型がLabelの場合、カスタマイズしたい色を設定します。handler.PlatformViewには、iOSの場合はUILabelが、Androidの場合はAppCompatTextViewが割り当てられています。そのため、それぞれのネイティブUIコントロールのプロパティを変更することで、色も変更できます。設定する色はToPlatformメソッドによって、それぞれのプラットフォームのネイティブカラーに変換されます。
ここで注目なのは、たった数行のコードで共有コード内のUILabel.BackgroundColorや、AppCompatTextView.SetBackgroundColorといったプロパティやメソッドが使用できることです。これをXamarin.Formsでするには、各プラットフォームプロジェクト内でクラスを定義し定型的なコードを書く必要があったので、簡単になっているのが分かります。
・あるUIコントロールの特定のインスタンスのみをカスタマイズする場合
こちらでは、Buttonコントロール特定のインスタンスの背景色をLightCoralに変更します。
ButtonのサブクラスMyButtonを作成します。
namespace MauiUICustomizeSample.Controls
{
public class MyButton : Button
{
public MyButton()
{
}
}
}
MauiProgram.csでMyButtonに対してカスタマイズを適用します。
// 特定のインスタンスの Button のカスタマイズ
ButtonHandler.Mapper.AppendToMapping(nameof(IView.Background), (handler, view) =>
{
if (view is MyButton)
{
#if IOS
handler.PlatformView.BackgroundColor = Colors.LightCoral.ToPlatform();
handler.PlatformView.SetTitleColor(Colors.White.ToPlatform(), UIKit.UIControlState.Normal);
handler.PlatformView.Layer.CornerRadius = 7;
#elif ANDROID
handler.PlatformView.SetBackgroundColor(Colors.LightCoral.ToPlatform());
#endif
}
});
AppendToMappingのActionデリゲート内で、UIコントロールの型がMyButtonの場合、カスタマイズしたい色を設定します。
handler.PlatformViewには、iOSの場合はUIButtonが、Androidの場合はMaterialButtonが割り当てられるので、それぞれのネイティブUIコントロールのプロパティを変更することで、色を変更できます。設定する色はToPlatformメソッドによって、それぞれのプラットフォームのネイティブカラーに変換されます。
カスタマイズしたコントロールをXAML内で使用するには、「xmlns:button="clr-namespace:MauiUICustomizeSample.Controls"」のように名前空間を指定します。
MyButtonのみ背景色がカスタマイズされます。
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:button="clr-namespace:MauiUICustomizeSample.Controls"
x:Class="MauiUICustomizeSample.MainPage">
<ScrollView>
<VerticalStackLayout
Spacing="25"
Padding="30,0"
VerticalOptions="Center">
<Image
Source="dotnet_bot.png"
SemanticProperties.Description="Cute dot net bot waving hi to you!"
HeightRequest="200"
HorizontalOptions="Center" />
<Label
Text="Hello, MAUI!"
SemanticProperties.HeadingLevel="Level1"
FontSize="32"
HorizontalOptions="Center" />
<Label
Text="Welcome to.NET Multi-platform App UI"
SemanticProperties.HeadingLevel="Level2"
SemanticProperties.Description="Welcome to dot net Multi platform App UI"
FontSize="16"
HorizontalOptions="Center" />
<HorizontalStackLayout
Spacing="10"
HorizontalOptions="Center">
<Button
x:Name="CounterBtn"
WidthRequest="120"
Text="Click me"
SemanticProperties.Hint="Counts the number of times you click"
Clicked="OnCounterClicked"
HorizontalOptions="Start" />
<button:MyButton
x:Name="CustomizedBtn"
WidthRequest="120"
Text="Customized"
HorizontalOptions="End" />
</HorizontalStackLayout>
</VerticalStackLayout>
</ScrollView>
</ContentPage>
iOSでの実行結果は、下図のようになります。ラベルの全体の背景色と、右側のボタンの背景色がカスタマイズされています。
ハンドラーを使用したコントロールのカスタマイズの詳細は、こちらをご覧ください。
まとめ
本稿で説明したように、.NET MAUIでは、Xamarin.Formsの面倒だった部分が扱いやすくなり、直感的でシンプルなコードで記述できるように改善されました。また、アセンブリスキャンやリフレクションなど、コストの高い処理を回避することで起動速度が向上しました。つまり「.NET MAUI=よりシンプルで使いやすくなったXamarin.Forms」といえます。
.NET MAUIは今後も強化され続けるとアナウンスされています。まずは強力に生まれ変わった.NET MAUIを体験してみてはいかがでしょうか。
Copyright © ITmedia, Inc. All Rights Reserved.
関連記事
Arm64デバイスでネイティブ開発が可能に、「Visual Studio 2022 17.3 Preview 2」がリリース
MicrosoftはWindows 11向けのネイティブArm64アプリケーションとして「Microsoft Visual Studio 2022バージョン17.3 Preview 2」をリリースした。「Windows 11 Armデバイス」でArm64アプリケーションのビルドとデバッグをネイティブにサポートする。
デベロッパーが注目したいMSの開発関連技術「.NET MAUI」「Microsoft Dev Box」の威力とは
Microsoftは開発者向けカンファレンス「Build 2022」で発表したツールやサービスのうち、Visual Studioユーザーにとって要注目の技術「.NET MAUI」と「Microsoft Dev Box」を紹介した。さらに「Azure Deployment Environments」とArm64対応の「Visual Studio」も取り上げている。
Microsoft、「Xamarin」の全APIドキュメントをオープンソース化
Microsoftは、モバイルアプリの開発プラットフォームである「Xamarin」の全てのAPIドキュメントをGitHubでオープンソースとして公開した。








