mcpField
D
dotnet-maui-development

dotnet-maui-development

github.com/wshaddix開発8690 回インストール35 週間前に更新
リポジトリ

dotnet-maui-development について

.NET MAUI で C# と XAML を使ったクロスプラットフォームアプリを開発する方向けのガイドです。プロジェクト構成、MVVM、プラットフォームサービス、Hot Reload、移行案、.NET 11 の変更点を扱います。

主な機能

  1. 01単一プロジェクトのマルチターゲット構成
  2. 02MVVM による XAML データバインディング
  3. 03Shell ナビゲーションと依存性注入
  4. 04プラットフォーム別サービスの実装パターン
  5. 05Hot Reload の手順と注意点
  6. 06移行案と .NET 11 の情報

活用シーン

  1. 01マルチプラットフォーム MAUI プロジェクトの構成
  2. 02XAML と ViewModel で共通 UI を実装
  3. 03MAUI アプリにプラットフォーム別サービスを追加
  4. 04MAUI の適合性と移行案を評価

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/wshaddix/dotnet-skills --skill dotnet-maui-development

インストール後、Claude Code で/dotnet-maui-developmentを入力して読み込まれたか確認します。

詳細

dotnet-maui-development

.NET MAUI によるクロスプラットフォーム開発:プラットフォーム別フォルダーを含む単一プロジェクト構造、MVVM(CommunityToolkit.Mvvm)による XAML データバインディング、Shell ナビゲーション、部分クラスと条件付きコンパイルによるプラットフォームサービス、依存性注入、プラットフォームごとのホットリロード、.NET 11 の改善点(XAML ソース生成、Android 向け CoreCLR、デバイス選択用の dotnet run)を扱います。現状の率直な評価と移行方法も含みます。

バージョンの前提: .NET 8.0 以降を基準とします(MAUI は .NET 8 以降に同梱されています)。.NET 11 Preview 1 の内容には明示的にその旨を記載しています。例では最新の安定版 API を使用します。

スコープの境界: このスキルは、MAUI プロジェクト構造、XAML/MVVM パターン、プラットフォームサービス、現状の評価、移行方法、.NET 11 の改善点を扱います。iOS/Mac Catalyst での MAUI Native AOT は [skill:dotnet-maui-aot] が扱います。

対象外: MAUI のテスト(Appium、XHarness、プラットフォーム固有のテスト)については、[skill:dotnet-maui-testing] を参照してください。一般的な Native AOT パターンについては、[skill:dotnet-native-aot] を参照してください。UI フレームワークの選択に関する判断ツリーについては、[skill:dotnet-ui-chooser] を参照してください。

相互参照:iOS/Mac Catalyst での Native AOT については [skill:dotnet-maui-aot]、テストパターンについては [skill:dotnet-maui-testing]、TFM の検出については [skill:dotnet-version-detection]、一般的な AOT パターンについては [skill:dotnet-native-aot]、フレームワークの選択については [skill:dotnet-ui-chooser]、アクセシビリティパターン(SemanticProperties、スクリーンリーダー)については [skill:dotnet-accessibility] を参照してください。


プロジェクト構造

MAUI は単一プロジェクトアーキテクチャを採用しています。1 つの .csproj がマルチターゲットによりすべてのプラットフォームを対象とし、プラットフォーム固有のコードはプラットフォーム別フォルダーに配置します。


<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFrameworks>net8.0-android;net8.0-ios;net8.0-maccatalyst</TargetFrameworks>
    <TargetFrameworks Condition="$([MSBuild]::IsOSPlatform('windows'))">
      $(TargetFrameworks);net8.0-windows10.0.19041.0
    </TargetFrameworks>
    <OutputType>Exe</OutputType>
    <UseMaui>true</UseMaui>
    <SingleProject>true</SingleProject>
    <RootNamespace>MyApp</RootNamespace>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="CommunityToolkit.Mvvm" Version="8.*" />
    <PackageReference Include="CommunityToolkit.Maui" Version="9.*" />
  </ItemGroup>
</Project>
プロジェクトのレイアウト
MyApp/
  MyApp/
    App.xaml / App.xaml.cs            # Application entry, resource dictionaries
    AppShell.xaml / AppShell.xaml.cs  # Shell navigation definition
    MauiProgram.cs                   # Host builder, DI, service registration
    MainPage.xaml / MainPage.xaml.cs  # Initial page
    ViewModels/                      # MVVM ViewModels
    Views/                           # XAML pages
    Models/                          # Data models
    Services/                        # Service interfaces and implementations
    Resources/
      Fonts/                         # Custom fonts (.ttf/.otf)
      Images/                        # SVG/PNG images (auto-resized per platform)
      Styles/                        # Shared styles, colors, resource dictionaries
      Raw/                           # Raw assets (JSON, etc.)
      Splash/                        # Splash screen image
    Platforms/
      Android/                       # AndroidManifest.xml, MainActivity.cs
      iOS/                           # Info.plist, AppDelegate.cs
      MacCatalyst/                   # Info.plist, AppDelegate.cs
      Windows/                       # Package.appxmanifest, App.xaml
    Properties/
      launchSettings.json
  MyApp.Tests/                       # Unit tests
リソースの管理

MAUI ではリソースファイルを宣言的に扱います。画像は単一のソースからプラットフォームごとに自動でリサイズされます。


<ItemGroup>
  
  <MauiImage Include="Resources\Images\*" />

  
  <MauiFont Include="Resources\Fonts\*" />

  
  <MauiSplashScreen Include="Resources\Splash\splash.svg"
                    Color="#512BD4" BaseSize="128,128" />

  
  <MauiIcon Include="Resources\AppIcon\appicon.svg"
            ForegroundFile="Resources\AppIcon\appiconfg.svg"
            Color="#512BD4" />
</ItemGroup>

XAML パターン

データバインディングの基礎

MAUI の XAML データバインディングは、UI 要素と ViewModel のプロパティを接続します。適切に BindingContext を設定したうえで {Binding} を使用します。

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:vm="clr-namespace:MyApp.ViewModels"
             x:Class="MyApp.Views.ProductListPage"
             x:DataType="vm:ProductListViewModel">

    <VerticalStackLayout Padding="16" Spacing="12">
        <SearchBar Text="{Binding SearchTerm}"
                   SearchCommand="{Binding SearchCommand}" />

        <CollectionView ItemsSource="{Binding Products}"
                        SelectionMode="Single"
                        SelectionChangedCommand="{Binding SelectProductCommand}"
                        SelectionChangedCommandParameter="{Binding SelectedItem,
                            Source={RelativeSource Self}}">
            <CollectionView.ItemTemplate>
                <DataTemplate x:DataType="model:Product">
                    <Frame Padding="12" Margin="0,4">
                        <HorizontalStackLayout Spacing="12">
                            <Image Source="{Binding ImageUrl}"
                                   HeightRequest="60" WidthRequest="60" />
                            <VerticalStackLayout>
                                <Label Text="{Binding Name}"
                                       FontAttributes="Bold" />
                                <Label Text="{Binding Price, StringFormat='{0:C}'}"
                                       TextColor="Gray" />
                            </VerticalStackLayout>
                        </HorizontalStackLayout>
                    </Frame>
                </DataTemplate>
            </CollectionView.ItemTemplate>
        </CollectionView>
    </VerticalStackLayout>
</ContentPage>

コンパイル済みバインディング: ページやデータテンプレートに x:DataType を指定して、コンパイル済みバインディングを有効にします。コンパイル済みバインディングはビルド時に型チェックされ、リフレクションベースのバインディングより実行時の処理が高速です。

CommunityToolkit.Mvvm による MVVM

CommunityToolkit.Mvvm(Microsoft MVVM Toolkit)は、MAUI に推奨される MVVM フレームワークです。ソースジェネレーターを使用して定型コードを減らします。

// ViewModels/ProductListViewModel.cs
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;

public partial class ProductListViewModel : ObservableObject
{
    private readonly IProductService _productService;
    private readonly INavigationService _navigationService;

    public ProductListViewModel(
        IProductService productService,
        INavigationService navigationService)
    {
        _productService = productService;
        _navigationService = navigationService;
    }

    [ObservableProperty]
    [NotifyCanExecuteChangedFor(nameof(SearchCommand))]
    private string _searchTerm = "";

    [ObservableProperty]
    private ObservableCollection<Product> _products = [];

    [ObservableProperty]
    private bool _isLoading;

    [RelayCommand]
    private async Task LoadProductsAsync(CancellationToken ct)
    {
        IsLoading = true;
        try
        {
            var items = await _productService.GetProductsAsync(ct);
            Products = new ObservableCollection<Product>(items);
        }
        finally
        {
            IsLoading = false;
        }
    }

    [RelayCommand(CanExecute = nameof(CanSearch))]
    private async Task SearchAsync(CancellationToken ct)
    {
        var results = await _productService.SearchAsync(SearchTerm, ct);
        Products = new ObservableCollection<Product>(results);
    }

    private bool CanSearch() => !string.IsNullOrWhiteSpace(SearchTerm);

    [RelayCommand]
    private async Task SelectProductAsync(Product? product)
    {
        if (product is null) return;
        await _navigationService.GoToAsync(
            nameof(ProductDetailPage),
            new Dictionary<string, object> { ["Product"] = product });
    }
}

主なソースジェネレーター属性:

  • [ObservableProperty] -- バッキングフィールドから INotifyPropertyChanged を備えたプロパティを生成します
  • [RelayCommand] -- メソッドから ICommand を生成します(非同期処理、キャンセル、CanExecute に対応)
  • [NotifyPropertyChangedFor] -- 依存するプロパティの PropertyChanged を発生させます
  • [NotifyCanExecuteChangedFor] -- プロパティの変更時にコマンドの CanExecute を再評価します
Shell ナビゲーション

Shell は視覚的な階層とナビゲーション構造を定義します。フライアウト、タブ、URI ベースのナビゲーションに対応しています。


<Shell xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
       xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
       xmlns:views="clr-namespace:MyApp.Views"
       x:Class="MyApp.AppShell">

    
    <TabBar>
        <ShellContent Title="Products"
                      Icon="products.png"
                      ContentTemplate="{DataTemplate views:ProductListPage}" />

        <ShellContent Title="Cart"
                      Icon="cart.png"
                      ContentTemplate="{DataTemplate views:CartPage}" />

        <ShellContent Title="Profile"
                      Icon="profile.png"
                      ContentTemplate="{DataTemplate views:ProfilePage}" />
    </TabBar>
</Shell>
// Register routes for pages not in the Shell visual hierarchy
// (pushed onto the navigation stack, not direct Shell tabs)
public partial class AppShell : Shell
{
    public AppShell()
    {
        InitializeComponent();
        Routing.RegisterRoute(nameof(ProductDetailPage), typeof(ProductDetailPage));
        Routing.RegisterRoute(nameof(CheckoutPage), typeof(CheckoutPage));
    }
}
// Navigate with parameters
await Shell.Current.GoToAsync(nameof(ProductDetailPage),
    new Dictionary<string, object>
    {
        ["Product"] = selectedProduct
    });

// Receive parameters via QueryProperty
[QueryProperty(nameof(Product), "Product")]
public partial class ProductDetailViewModel : ObservableObject
{
    [ObservableProperty]
    private Product _product = null!;
}
ContentPage のライフサイクル
public partial class ProductListPage : ContentPage
{
    private readonly ProductListViewModel _viewModel;

    public ProductListPage(ProductListViewModel viewModel)
    {
        InitializeComponent();
        _viewModel = viewModel;
        BindingContext = viewModel;
    }

    protected override async void OnAppearing()
    {
        base.OnAppearing();
        // Load data when page appears (not in constructor)
        await _viewModel.LoadProductsCommand.ExecuteAsync(null);
    }

    protected override void OnDisappearing()
    {
        base.OnDisappearing();
        // Cancel pending operations, unsubscribe events
    }
}

プラットフォームサービス

プラットフォーム固有コード向けの部分クラス

部分クラスと、Platforms フォルダーに配置したプラットフォーム固有の実装を使用します。ビルドシステムは各ターゲットに適した実装をコンパイルします。

// Services/IDeviceService.cs (shared interface)
public interface IDeviceService
{
    string GetDeviceIdentifier();
    Task<bool> HasBiometricAsync();
}

// Services/DeviceService.cs (shared partial class)
public partial class DeviceService : IDeviceService
{
    public partial string GetDeviceIdentifier();
    public partial Task<bool> HasBiometricAsync();
}
// Platforms/Android/Services/DeviceService.cs
public partial class DeviceService
{
    public partial string GetDeviceIdentifier()
    {
        return Android.Provider.Settings.Secure.GetString(
            Android.App.Application.Context.ContentResolver,
            Android.Provider.Settings.Secure.AndroidId) ?? "unknown";
    }

    public partial Task<bool> HasBiometricAsync()
    {
        var manager = BiometricManager.From(Android.App.Application.Context);
        var result = manager.CanAuthenticate(
            BiometricManager.Authenticators.BiometricStrong);
        return Task.FromResult(
            result == BiometricManager.BiometricSuccess);
    }
}
// Platforms/iOS/Services/DeviceService.cs
public partial class DeviceService
{
    public partial string GetDeviceIdentifier()
    {
        return UIKit.UIDevice.CurrentDevice.IdentifierForVendor?.ToString()
            ?? "unknown";
    }

    public partial Task<bool> HasBiometricAsync()
    {
        var context = new LocalAuthentication.LAContext();
        return Task.FromResult(context.CanEvaluatePolicy(
            LocalAuthentication.LAPolicy.DeviceOwnerAuthenticationWithBiometrics,
            out _));
    }
}
条件付きコンパイル

小さなプラットフォーム間の差異には、部分クラスの代わりに #if ディレクティブを使用します。

public void ConfigurePlatformDefaults()
{
#if ANDROID
    // Android-specific: request permissions
    Platform.CurrentActivity?.RequestPermissions(
        [Android.Manifest.Permission.Camera]);
#elif IOS || MACCATALYST
    // iOS/Mac Catalyst: no runtime permission request needed for camera
    // (handled via Info.plist NSCameraUsageDescription)
#elif WINDOWS
    // Windows: WinUI-specific configuration
#endif
}

使い分け:

  • 部分クラス: 大規模なプラットフォーム実装、複数のメソッド、複雑なロジック
  • 条件付きコンパイル: 1 行だけの差異、小規模な分岐、定数
依存性注入

MAUI は Microsoft.Extensions.DependencyInjection を使用します。サービス、ViewModel、ページを MauiProgram.cs に登録します。

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

        // Services
        builder.Services.AddSingleton<IProductService, ProductService>();
        builder.Services.AddSingleton<INavigationService, NavigationService>();
        builder.Services.AddTransient<IDeviceService, DeviceService>();

        // HTTP client
        builder.Services.AddHttpClient("api", client =>
        {
            client.BaseAddress = new Uri("https://api.example.com");
        });

        // ViewModels (transient so each page gets a fresh instance)
        builder.Services.AddTransient<ProductListViewModel>();
        builder.Services.AddTransient<ProductDetailViewModel>();

        // Pages (transient, resolved with DI-injected ViewModels)
        builder.Services.AddTransient<ProductListPage>();
        builder.Services.AddTransient<ProductDetailPage>();

        // For DI patterns beyond MAUI-specific registration, see [skill:dotnet-csharp-dependency-injection]

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

        return builder.Build();
    }
}

現状の評価(2026 年 2 月)

最終確認日:2026-02-13

本番環境への対応状況

.NET MAUI は注意点はあるものの、本番環境で使用可能です。このフレームワークは企業での採用が進み、コミュニティからも積極的な投資を受けていますが、既知のツールとプラットフォームの課題に注意する必要があります。

成長指標:

  • 前年比でユーザー数が36%増加
  • コミュニティからのプルリクエストが557%増加
  • 基幹業務アプリで企業による採用が進んでいます
既知の問題

Visual Studio 2026 のツールに関するバグ:

  • IDE の更新後、最初のビルドで Android ツールチェーンが失敗することがあります。クリーンして再ビルドすると、ほとんどの問題は解決します。
  • 一部の Android エミュレーター構成では、ホットリロードの接続に断続的に失敗します。
  • XAML IntelliSense で、有効なコンパイル済みバインディングに対して誤ったエラーが表示されることがあります。赤い波線が表示されていても、ビルドは成功します。

iOS プラットフォームの課題:

  • iOS 26.x との互換性については、最新の MAUI サービス更新プログラムを適用した状態でテストする必要があります。
  • 一部の iOS 固有のコントロール(カスタム書式を指定した DatePicker など)では、レンダリングに一貫性がない場合があります。
  • Xcode の更新により、MAUI のサービス更新プログラムがリリースされるまで、ビルドツールチェーンが動作しなくなる場合があります。
公平な評価

Android、iOS、macOS(Catalyst)、Windows を対象に、単一の C#/.NET コードベースでネイティブ UI をレンダリングする必要がある場合、MAUI は適切な選択肢です。基幹業務アプリ、企業向けのシナリオ、既存の .NET の専門知識を持つチームに適しています。

次の場合は、MAUI が最適な選択肢ではありません。

  • Web ブラウザーを対象にする必要がある場合(Blazor または Uno Platform をご検討ください)
  • Linux デスクトップのサポートが必要な場合(Uno Platform または Avalonia をご検討ください)
  • プラットフォーム間でピクセル単位の精度でカスタムレンダリングする必要がある場合(ゲームエンジンまたは Skia ベースのフレームワークをご検討ください)
  • チームに .NET の経験がない場合(ネイティブまたは React Native の代替案をご検討ください)

移行の選択肢

MAUI が最適ではない場合は、次の .NET 代替案をご検討ください。

WinUI 3(Windows 専用)

アプリケーションが Windows のみを対象とする場合、WinUI 3 を使用すれば、クロスプラットフォームの抽象化レイヤーなしで、より豊かな Windows ネイティブのエクスペリエンスを実現できます。


<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>net8.0-windows10.0.19041.0</TargetFramework>
    <UseWinUI>true</UseWinUI>
  </PropertyGroup>
</Project>

MAUI より WinUI を選ぶ場合:

  • モバイルや macOS の要件がない Windows 専用アプリ
  • タスクバー、通知、ウィジェットなど、Windows との深い統合が必要
  • MAUI では公開されていない WinAppSDK の機能が必要
  • UWP から移行する
Uno Platform(Web + Linux + 全プラットフォーム)

モバイルや Windows に加えて、Web(WASM)または Linux デスクトップのサポートが必要な場合、Uno Platform なら WinUI 互換の API サーフェスを備え、より幅広い対象プラットフォームをサポートできます。

MAUI より Uno を選ぶ場合:

  • Web ブラウザーへのデプロイが必要(WASM)
  • Linux デスクトップのサポートが必要(GTK/Framebuffer)
  • 組み込みデバイスを対象にする必要がある
  • MVUX のリアクティブパターンを備えた WinUI API サーフェスをチームが希望する
  • Figma から XAML へのデザインワークフローが必要

Uno Platform の開発パターンについては [skill:dotnet-uno-platform] を、対象ごとのデプロイについては [skill:dotnet-uno-targets] をご覧ください。

選択肢の概要
要件MAUIWinUI 3Uno Platform
Android + iOS対応非対応対応
Windows デスクトップ対応対応(最適)対応
macOS(Catalyst)対応非対応対応
Web(WASM)非対応非対応対応
Linux デスクトップ非対応非対応対応
ネイティブ UI のレンダリング対応対応Skia + ネイティブ
MVVM Toolkit対応対応対応
MVUX リアクティブ非対応非対応対応

フレームワークの選択に関する完全な判断ツリーについては、[skill:dotnet-ui-chooser] をご覧ください。


.NET 11 の改善点

最終確認日:2026-02-13

XAML ソース生成(.NET 11 Preview 1 でデフォルト)

.NET 11 Preview 1 では、従来の XAMLC(XamlCompilationAttribute)方式に代わり、XAML ソース生成がデフォルトの XAML コンパイルモードになります。ソース生成された XAML は AOT に対応し、より適切な診断情報を提供するとともに、起動を高速化します。



<PropertyGroup>
  <MauiXamlInflator>XamlC</MauiXamlInflator>
</PropertyGroup>

変更点: XAML ページは、XamlCompilationAttribute を介して IL にコンパイルされるのではなく、ビルド時に C# ソースコードに変換されます。これにより、Native AOT で動作する型安全な初期化コードが生成されます。

元に戻す場合: カスタムマークアップ拡張機能でソース生成の問題が発生した場合、実行時の XAML 読み込みに依存するサードパーティ製コントロールを使用する場合、または従来の LoadFromXaml を使用する場合は、XamlC に戻してください。

Android の CoreCLR(.NET 11 Preview 1 でデフォルト)

.NET 11 Preview 1 では、Android の Release ビルドのデフォルトランタイムが Mono から CoreCLR に切り替わります。CoreCLR は、パフォーマンスの向上、診断機能の改善、サーバー/デスクトップのランタイムとの統一を実現します。



<PropertyGroup>
  <UseMonoRuntime>true</UseMonoRuntime>
</PropertyGroup>

変更点: Release ビルドでは、Mono の代わりに CoreCLR(ASP.NET Core やデスクトップアプリと同じランタイム)を使用します。Debug ビルドでは、Hot Reload をサポートするため引き続き Mono を使用します。

無効にする場合: Mono 固有の動作に依存している場合、古い Android デバイス(API < 26)で CoreCLR との互換性の問題が発生する場合、または Mono の内部実装を特に対象とするライブラリを使用する場合は、無効にしてください。

dotnet run のデバイス選択

.NET 11 Preview 1 では、MAUI プロジェクトの dotnet run に対話型のターゲットフレームワークとデバイスの選択機能が追加されます。

# .NET 11: interactive device selection
dotnet run --project MyApp/MyApp.csproj
# Prompts: Select target framework:
#   1. net11.0-android
#   2. net11.0-ios
#   3. net11.0-maccatalyst
# Then: Select device:
#   1. Pixel 7 API 34 (emulator)
#   2. Samsung Galaxy S24 (physical)

# Skip interactive selection with explicit TFM
dotnet run --project MyApp/MyApp.csproj -f net11.0-android

これにより、正確な TFM 文字列を指定して -f を手動で入力する必要がなくなり、開発者の反復作業が簡素化されます。


Hot Reload

MAUI は XAML Hot Reload と C# Hot Reload の両方をサポートしていますが、機能はプラットフォームによって異なります。

サポート状況
変更の種類AndroidiOSmacOSWindows
XAML のレイアウト/スタイル対応対応対応対応
C# のメソッド本体対応対応対応対応
新しいインスタンスメソッド(非ジェネリッククラス)一部対応(.NET 9 以降)一部対応(.NET 9 以降)一部対応(.NET 9 以降)一部対応(.NET 9 以降)
新しい静的メソッド/ジェネリック型のメンバー再ビルド再ビルド再ビルド再ビルド
リソースディクショナリ対応対応対応対応
新しい XAML ページの追加再ビルド再ビルド再ビルド再ビルド
CSS の変更対応対応対応対応
Hot Reload の有効化
# CLI: Hot Reload is enabled automatically in Debug configuration
dotnet run --project MyApp/MyApp.csproj -f net8.0-android

# Visual Studio: Hot Reload is on by default (fire icon in toolbar)
# VS Code: Use MAUI extension with Hot Reload enabled

注意点:

  • Hot Reload には Debug ビルド構成が必要です。Release ビルドではサポートされません
  • カスタムレンダラーまたはハンドラーへの変更は、再ビルドするまで XAML Hot Reload に反映されない場合があります
  • Android では、Hot Reload は MetadataUpdateHandler の仕組みを使用します。静的フィールドやコンストラクターへの変更には再起動が必要です
  • iOS シミュレーターでは Hot Reload は動作しますが、実機での Hot Reload には安定した USB/Wi-Fi 接続が必要です

エージェントでの注意点

  1. プラットフォームごとに別々のプロジェクトを作成しないでください。 MAUI は単一プロジェクト構成を採用しています。プラットフォーム固有のコードは、別々の Android/iOS プロジェクトではなく、同じプロジェクト内の Platforms/ フォルダーに配置します(これは Xamarin.Forms の方式です)。
  2. MVVM Toolkit の属性と手動の INotifyPropertyChanged を併用しないでください。 [ObservableProperty] を一貫して使用してください。ソース生成による実装と手書きのプロパティ変更通知の実装を混在させると、微妙なバインディングの不具合が発生します。
  3. コンストラクター内で非同期メソッドを呼び出さないでください。 データ読み込みを開始するには、OnAppearing() または読み込み用コマンドを使用してください。コンストラクター内で非同期呼び出しを行うと、未観測の例外が発生したり、バインディングコンテキストの初期化と競合したりするおそれがあります。
  4. Device.BeginInvokeOnMainThread を使用しないでください。 非推奨です。代わりに Microsoft.Maui.ApplicationModel の MainThread.BeginInvokeOnMainThread() または MainThread.InvokeOnMainThreadAsync() を使用してください。
  5. RuntimeInformation でプラットフォームを直接判定しないでください。 プラットフォーム検出における MAUI のクロスプラットフォーム抽象化である DeviceInfo.Platform の比較(DevicePlatform.Android、DevicePlatform.iOS)を使用してください。
  6. x:DataType なしで {Binding} を使用しないでください。 コンパイル済みバインディングを有効にするため、ページとデータテンプレートには必ず x:DataType を設定してください。リフレクションベースのバインディングは遅く、ビルド時にエラーが検出されません。
  7. ページは通常、Singleton ではなく Transient にしてください。 ページを Singleton にすると、古いデータが残ったり、バインディングが保持されてメモリリークが発生したりします。状態を保持する必要がある場合(タブ付きページなど)は、ViewModel を Singleton にし、ページを Transient にしてください。
  8. タブ以外のページでは Shell のルート登録を忘れないでください。 ナビゲーションスタックに追加するページ(GoToAsync 経由)は、AppShell コンストラクター内で Routing.RegisterRoute を使って登録する必要があります。登録しないと、ナビゲーション時に RouteNotFoundException がスローされます。

前提条件

  • .NET 8.0 以降(.NET MAUI は .NET 8 以降に付属します)
  • MAUI ワークロード:dotnet workload install maui
  • プラットフォーム SDK:Android SDK(API 21 以降)、Xcode(macOS のみ、iOS/Mac Catalyst 用)、Windows App SDK(Windows 用)
  • MAUI ワークロードを含む Visual Studio 2022 以降、.NET MAUI 拡張機能を含む VS Code、または JetBrains Rider 2024.2 以降

参考資料

コメント