.NET MAUI 2026年完全ガイド:クロスプラットフォーム開発と面接頻出質問

.NET MAUI 10によるクロスプラットフォームアプリ開発の手順をHandlers、MVVM、HybridWebView、SafeAreaEdgesと共に解説し、2026年の技術面接で頻出する質問と回答を体系的にまとめています。

.NET MAUIクロスプラットフォーム開発:C#でAndroid、iOS、デスクトップを単一コードベースから構築

.NET MAUI(Multi-platform App UI)は、.NET 10のリリースにより本番環境に対応したクロスプラットフォームフレームワークとして成熟期を迎えています。2028年11月までサポートされるLTS(長期サポート)バージョンとして提供される.NET MAUI 10は、ワークロードおよびNuGetパッケージとして配布され、品質・パフォーマンスの改善に加え、HybridWebViewの機能拡張やSafeAreaEdgesといった新しいAPIを搭載しています。本記事では、クロスプラットフォームアプリの構築手順、MAUIの基盤となるアーキテクチャ、そして2026年の技術面接で問われる重要な質問について体系的に解説します。

.NET MAUI 10 LTS

.NET 10はLTS(長期サポート)リリースであり、2028年11月までサポートが継続されます。MAUI 10は新しいUIコントロールの追加よりも品質とパフォーマンスの改善に注力しており、過去のMAUIリリースの中で最も安定したバージョンです。.NETワークロードおよびNuGetパッケージとして配布されるため、プロジェクトごとにバージョンを固定できます。

.NET MAUI 10プロジェクトのセットアップ

MAUIアプリケーションの構築は.NET CLIから開始するのが最も効率的です。.NET 10では、.NET Aspireサービスデフォルトを含む更新されたプロジェクトテンプレートが導入され、テレメトリとサービスディスカバリが初期設定で利用可能になっています。

bash
# Install the MAUI workload (if not already present)
dotnet workload install maui

# Create a new MAUI app
dotnet new maui -n CrossPlatformDemo
cd CrossPlatformDemo

# Run on Android emulator
dotnet build -t:Run -f net10.0-android

シングルプロジェクト構成では、プラットフォーム固有のコードがPlatforms/フォルダに集約され、それ以外のコードはすべて共有されます。MauiProgram.csファイルがComposition Rootとして機能し、サービス、フォント、ハンドラの登録はすべてこのファイルで行われます。

MauiProgram.cscsharp
using Microsoft.Extensions.Logging;

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

        // Register services for dependency injection
        builder.Services.AddSingleton<IApiService, ApiService>();
        builder.Services.AddTransient<MainViewModel>();

#if DEBUG
        builder.Logging.AddDebug();
#endif

        return builder.Build();
    }
}

MAUIの依存性注入はASP.NET Coreと同じパターンに従います。Singletonサービスはアプリのライフタイム全体にわたって存続し、Transientサービスは要求のたびに生成されます。Scopedサービスも利用可能ですが、MAUIにはHTTPリクエストのような組み込みのスコープ概念が存在しないため、使用には注意が必要です。

Handlersアーキテクチャ:クロスプラットフォームレンダリングの仕組み

MAUIでは、Xamarin.Formsのレンダラーがハンドラアーキテクチャに置き換えられています。ハンドラは各クロスプラットフォームコントロールを、薄い抽象化レイヤーを介してネイティブの対応コントロールにマッピングします。従来のレンダラーとの最大の違いは、ハンドラがステートレスであり、仮想ビューから分離されている点です。この設計により、パフォーマンスが向上し、カスタマイズも容易になっています。

CustomEntryHandler.cs — Customizing the Entry control on Androidcsharp
using Microsoft.Maui.Handlers;

public class CustomEntryHandler : EntryHandler
{
    protected override void ConnectHandler(MauiAppCompatEditText platformView)
    {
        base.ConnectHandler(platformView);
        // Remove the default underline on Android
        platformView.SetBackgroundColor(Android.Graphics.Color.Transparent);
    }
}

// Register in MauiProgram.cs
builder.ConfigureMauiHandlers(handlers =>
{
    handlers.AddHandler<Entry, CustomEntryHandler>();
});

.NET 10では、AndroidのEntryおよびEditorコントロールがAppCompatEditTextからMauiAppCompatEditTextに変更され、SelectionChangedイベントのネイティブサポートが追加されています。.NET 9で導入されたCollectionViewおよびCarouselViewハンドラの改良版が、iOSとMac Catalystでデフォルトとなり、長期間にわたる安定性の問題が解消されています。

CommunityToolkit.MvvmによるMVVM:ボイラープレートの排除

CommunityToolkit.Mvvmのソースジェネレーターを活用することで、MVVMパターンにおける定型コードの約80%を排除できます。INotifyPropertyChangedの手動実装もコマンドラッパーも不要で、属性によるコード生成が全体を駆動します。

MainViewModel.cscsharp
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;

public partial class MainViewModel : ObservableObject
{
    private readonly IApiService _apiService;

    public MainViewModel(IApiService apiService)
    {
        _apiService = apiService;
    }

    // Source generator creates the 'Title' property with change notification
    [ObservableProperty]
    private string _title = string.Empty;

    // Source generator creates the 'IsLoading' property
    [ObservableProperty]
    private bool _isLoading;

    // Source generator creates an async ICommand
    [RelayCommand]
    private async Task LoadDataAsync()
    {
        IsLoading = true;
        try
        {
            Title = await _apiService.FetchTitleAsync();
        }
        finally
        {
            IsLoading = false;
        }
    }
}

XAMLからは、ソースジェネレーターが生成したプロパティとコマンドに直接バインドできます。

xml
<!-- MainPage.xaml -->
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:vm="clr-namespace:CrossPlatformDemo.ViewModels"
             x:DataType="vm:MainViewModel">
    <VerticalStackLayout Padding="20" Spacing="16">
        <Label Text="{Binding Title}"
               FontSize="24"
               HorizontalOptions="Center" />
        <Button Text="Load Data"
                Command="{Binding LoadDataCommand}"
                IsEnabled="{Binding IsLoading, Converter={StaticResource InverseBoolConverter}}" />
        <ActivityIndicator IsRunning="{Binding IsLoading}"
                           IsVisible="{Binding IsLoading}" />
    </VerticalStackLayout>
</ContentPage>

x:DataType属性を指定することでコンパイル済みバインディングが有効になります。リフレクションベースのバインディングと比較して高速に動作し、バインディングパスの誤りがコンパイル時にエラーとして検出されます。

.NETの面接対策はできていますか?

インタラクティブなシミュレーター、flashcards、技術テストで練習しましょう。

.NET 10のHybridWebView:ネイティブとWebの統合

HybridWebViewを使用すると、MAUIアプリ内にWebコンテンツを埋め込みつつ、C#とJavaScript間の双方向通信を維持できます。.NET 10では、Fire-and-forget方式のJavaScript呼び出し、プラットフォーム固有の設定のための初期化イベント、Webリクエストのインターセプションという3つの機能が追加されています。

MainPage.xaml.cs — HybridWebView interactioncsharp
public partial class MainPage : ContentPage
{
    public MainPage()
    {
        InitializeComponent();

        // Initialization event for platform-specific tweaks
        hybridWebView.WebViewInitialized += (sender, args) =>
        {
            // Access the native platform view after initialization
            System.Diagnostics.Debug.WriteLine("WebView ready");
        };
    }

    // Call JavaScript from C#
    private async void OnCallJsClicked(object sender, EventArgs e)
    {
        var result = await hybridWebView.InvokeJavaScriptAsync<string>(
            "getFormData",  // JS function name
            HybridSampleContext.Default.String  // JSON serialization context
        );
        await DisplayAlert("Result", result, "OK");
    }

    // Fire-and-forget: no return type needed (.NET 10)
    private async void OnResetClicked(object sender, EventArgs e)
    {
        await hybridWebView.InvokeJavaScriptAsync("resetForm");
    }
}

対応するJavaScript側では、C#からの呼び出しを受け取り、メッセージを返すことが可能です。

wwwroot/index.html (HybridWebView content)javascript
function getFormData() {
    return JSON.stringify({
        name: document.getElementById('name').value,
        email: document.getElementById('email').value
    });
}

function resetForm() {
    document.getElementById('name').value = '';
    document.getElementById('email').value = '';
}

InvokeJavaScriptAsyncの実行中にJavaScript側でスローされた例外は、.NET側の例外として自動的に転送されるようになりました。従来発生していたサイレントエラーの問題が解消されています。

SafeAreaEdges:あらゆるデバイスでのレイアウト最適化

.NET MAUI 10では、Layout、ContentView、ContentPage、Border、ScrollViewに対してSafeAreaEdgesプロパティが導入されています。新しいenum値(NoneSoftInputContainerDefaultAll)により、iOS固有だったレガシーのPage.UseSafeAreaがクロスプラットフォーム対応のアプローチに置き換えられました。

xml
<!-- Granular safe area control per section -->
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             SafeAreaEdges="All">
    <Grid RowDefinitions="Auto,*,Auto">
        <!-- Header respects all safe areas -->
        <Border Grid.Row="0" SafeAreaEdges="Container">
            <Label Text="Header" />
        </Border>

        <!-- Content scrolls under safe areas -->
        <ScrollView Grid.Row="1" SafeAreaEdges="SoftInput">
            <VerticalStackLayout Padding="16">
                <Entry Placeholder="Type here..." />
            </VerticalStackLayout>
        </ScrollView>

        <!-- Footer avoids home indicator -->
        <Border Grid.Row="2" SafeAreaEdges="Container">
            <Label Text="Footer" />
        </Border>
    </Grid>
</ContentPage>

SoftInputはソフトウェアキーボードの表示時にコンテンツを自動調整します。Containerはノッチ、センサーハウジング、ホームインジケーターとの重なりを回避します。これらの値はコントロールごとに組み合わせることが可能で、ヘッダーをステータスバーの下に配置しつつ、コンテンツ領域は安全に確保するといった柔軟なレイアウトを実現できます。

Xamarin.Formsから.NET MAUIへの移行

Xamarin.Formsは2024年5月にサポートが終了しています。MAUIへの移行は単純な名前空間の変更にとどまらず、構造的な変更を伴います。以下は、実際のプロジェクト移行に基づいた移行チェックリストです。

Xamarinサポート終了

Xamarin.Formsのサポートは2024年5月に終了しました。Xamarin上で稼働を続けるアプリケーションには、セキュリティおよび互換性のリスクが伴います。.NET MAUI 10(LTS、2028年11月までサポート)が正式な移行先として指定されています。

  1. プロジェクト構造 -- プラットフォーム固有のプロジェクト構成から、MAUIのシングルプロジェクトモデルに変換します。共有コードをルートに、プラットフォームコードをPlatforms/配下に移動します。
  2. 名前空間 -- Xamarin.FormsMicrosoft.Maui.Controlsに、Xamarin.EssentialsMicrosoft.Maui.Essentials(MAUIに統合済み)にそれぞれ置換します。
  3. レンダラーからハンドラへ -- カスタムレンダラーはハンドラとして書き直す必要があります。ハンドラAPIはよりシンプルですが、マッピングロジックの構造が異なります。
  4. スタートアップ -- App.xaml.csでの初期化を、ビルダーパターンを使用するMauiProgram.csに置き換えます。
  5. NuGetパッケージ -- Xamarin時代のパッケージの多くにMAUI対応版が存在します。アップグレード前に互換性を必ず確認してください。
  6. 依存性注入 -- MAUIはMicrosoft.Extensions.DependencyInjectionをネイティブに使用します。サードパーティDIコンテナやDependencyServiceの呼び出しを置き換える必要があります。

.NET Upgrade Assistantはステップ1-2の一部を自動化しますが、ハンドラの書き直し(ステップ3)やビジネスロジックの調整は手作業で対応する必要があります。

2026年の.NET MAUI面接頻出質問

以下の質問は、.NET 10エコシステムを前提に、2026年の採用面接で実際に問われている内容を反映しています。

MAUIのハンドラアーキテクチャはXamarin.Formsのレンダラーとどう異なりますか?

Xamarin.Formsのレンダラーは、クロスプラットフォームコントロールとネイティブビューの双方に密結合しており、双方向の依存関係を生み出していました。MAUIのハンドラはステートレスなマッパーとして設計されており、プロパティ変更の通知を受け取り、マッパーディクショナリを通じてネイティブビューに反映します。この分離により、テスト、拡張、再利用が格段に容易になっています。PropertyMapperCommandMapperディクショナリが従来のOnElementPropertyChangedオーバーライドパターンを置き換えており、カスタマイズが明示的かつ宣言的に行われます。

MAUIにおけるDIライフタイムの注意点は何ですか?

MAUIはSingleton、Transient、Scopedの3つのライフタイムをサポートしていますが、ScopedはASP.NET Coreとは異なる振る舞いをします。MAUIにはHTTPリクエストのような自然なスコープ境界が存在しないため、Scopedサービスを登録しても手動でスコープを生成しない限りSingletonと同様に動作します。頻繁に見られる設計ミスとして、ページ固有の状態を保持するViewModelをSingletonとして登録してしまう(ナビゲーション間で古いデータが残る)ケースや、データベース接続をTransientとして登録してしまう(コネクションプールの枯渇)ケースが挙げられます。原則として、ViewModelはTransient、サービスはSingleton、Scopedはスコープのライフサイクルを明示的に管理できる場合にのみ使用するのが適切です。

面接対策のポイント

DIに関する質問では、MAUIのライフサイクルがASP.NET Coreのリクエストスコープモデルとどのように異なるかを理解していることを示すことが重要です。面接官は、長時間稼働するモバイルアプリに特有のメモリリークやステート管理の問題に対する認識を評価しています。

MAUIのコンパイル済みバインディングとリフレクションベースのバインディングはどう異なりますか?

リフレクションベースのバインディングはSystem.Reflectionを使用してプロパティパスを実行時に解決するため、動作が遅く、記述ミスがあっても実行時にしかエラーが検出されません。x:DataTypeを指定して有効化するコンパイル済みバインディングでは、バインディングパスがコンパイル時に解決されます。コンパイラがプロパティへの直接アクセスコードを生成し、リフレクションを完全にスキップするため、起動時間の短縮、メモリアロケーションの削減、ビルド時のバインディングエラー検出が実現します。.NET 10では、新しいXAMLソースジェネレーターにより、XAMLが実行時のパースではなくビルド時にコンパイルされるようになり、さらなる最適化が図られています。

MAUIアプリとASP.NET Coreバックエンド間でコードを共有する戦略は?

推奨されるアプローチは、DTO、バリデーションロジック、ビジネスルールを含む共有クラスライブラリを作成し、MAUIアプリとASP.NET Coreバックエンドの双方からこのライブラリを参照する構成です。.NET 10では、新たに導入された.NET Aspire統合により、モバイルプロジェクトとバックエンドプロジェクト間でサービスディスカバリとテレメトリが提供されます。System.Text.Jsonソースジェネレーターを使用した共有コントラクトにより、シリアライゼーションの一貫性が確保されます。重要な制約として、共有ライブラリはプラットフォーム固有のTFMではなくnet10.0をターゲットにする必要があります。

HybridWebViewとBlazorWebViewはどう使い分けますか?

BlazorWebViewは、MAUIアプリ内で完全なBlazorアプリケーションをホストします。Razorコンポーネントは埋め込みWebView内でレンダリングされますが、.NETランタイムはWebAssemblyではなくネイティブで実行されます。一方、HybridWebViewはより軽量で、静的なHTML/CSS/JSコンテンツを読み込み、BlazorフレームワークのオーバーヘッドなしにC#-JavaScript間の相互運用を提供します。選択基準はユースケースによって決まります。既存のBlazorコンポーネントを再利用したいチームにはBlazorWebViewが適しています。既存のWebコンテンツ(ダッシュボード、地図、エディタなど)をフルフレームワークなしでネイティブ統合したいシナリオにはHybridWebViewが適切です。

SharpSkillの.NET面接対策では、これらの概念をインタラクティブな演習で実践的に学ぶことができます。

今すぐ練習を始めましょう!

面接シミュレーターと技術テストで知識をテストしましょう。

まとめ

  • .NET MAUI 10はLTSリリース(2028年11月までサポート)であり、機能の追加ではなく安定性とパフォーマンスの向上に焦点を当てている。本番環境のクロスプラットフォームアプリに適した信頼性の高い選択肢である
  • ハンドラアーキテクチャはXamarinのレンダラーをステートレスなマッパーに置き換え、PropertyMapperCommandMapperによりテスト容易性とカスタマイズ性を向上させている
  • CommunityToolkit.Mvvmのソースジェネレーターにより、MVVMの定型コードが大幅に削減される。[ObservableProperty][RelayCommand]属性が、手動のINotifyPropertyChanged実装やコマンド実装を不要にする
  • .NET 10のHybridWebViewにはFire-and-forgetのJavaScript呼び出し、初期化イベント、リクエストインターセプションが追加され、ネイティブとWebの統合がさらに強化されている
  • SafeAreaEdgesはデバイスのノッチ、キーボード、システムバーに対するクロスプラットフォームの細粒度制御を提供し、iOS限定だったレガシーのUseSafeAreaを置き換えている
  • Xamarin.Formsからの移行にはハンドラの書き直しとスタートアップのリファクタリングが必要である。.NET Upgrade Assistantは名前空間の変更を自動化するが、カスタムレンダラーの対応は手作業が残る
  • 面接対策では、ハンドラとレンダラーのアーキテクチャの違い、長時間稼働アプリにおけるDIライフタイムの注意点、コンパイル済みバインディング、HybridWebViewとBlazorWebViewのトレードオフに重点を置くことが推奨される

今すぐ練習を始めましょう!

面接シミュレーターと技術テストで知識をテストしましょう。

タグ

#.net maui
#cross-platform
#tutorial
#interview
#xamarin migration
#.net 10
#mobile development

共有

関連記事