.NET MAUI クラスライブラリの基礎と使い方:カスタムコントロール・共通部品を再利用する実践ガイド

.NET MAUI でアプリ開発をしていると「共通のカスタムコントロールやサービスをまとめておきたい」「複数アプリで同じ UI 部品を使いたい」と感じる場面が必ず出てきます。そこで登場するのが .NET MAUI クラスライブラリです。このページでは、通常の .NET クラスライブラリとの違いから、作り方・使いどころ・具体的なコード例・設計のコツまで、はじめての人でも実務でそのまま使えるレベルで解説します。

目次

.NET MAUI クラスライブラリとは?概要と役割

.NET MAUI クラスライブラリは、一言でいうと「複数の MAUI アプリから共通で使う MAUI 向けコードをまとめておくための入れ物」です。 普通の .NET クラスライブラリと違い、MAUI の UI API(XAML、コントロール、ハンドラー、リソース)に直接アクセスできるように設計されています。

たとえば次のような「共通部品」を 1 つのライブラリにまとめておき、複数の MAUI アプリから参照して使う、というのが典型的な使い方です。

  • カスタムコントロール(独自の Entry / Button / ListView など)
  • 共通の Behavior / Converter / Attached Property
  • HTTP クライアントやリポジトリなどのサービス層
  • スタイル・色・マージンなどの ResourceDictionary
  • フォント・画像・アイコンなどの共有リソース

通常の .NET クラスライブラリとの違い

「普通の .NET クラスライブラリにまとめればよくない?」と思うかもしれません。違いを整理してみましょう。

項目.NET MAUI クラスライブラリ通常の .NET クラスライブラリ
MAUI の UI API へのアクセス可能(Microsoft.Maui.Controls などを前提にした構成)基本的に想定されていない(UI 部分は持たない前提)
XAML(ページ・コントロール)定義可能。カスタムコントロールやビューを含められる基本的に対象外
MAUI リソース(フォント・画像等)MauiImage, MauiFont などのビルドアクションを使用可能通常は使用しない
プラットフォーム別コードマルチターゲット・部分クラス・条件コンパイルが前提マルチターゲットは可能だが MAUI 前提ではない
主な用途MAUI アプリ向けの共通 UI・共通サービスドメインロジックや共通ユーティリティ(UI 非依存)

UI 部品(コントロールやスタイルなど)を共通化したい場合は、.NET MAUI クラスライブラリを選ぶのが自然です。一方、業務ロジックだけを切り出すなら通常の .NET クラスライブラリや .NET Standard ライブラリを使う、といったように役割で使い分けるのがポイントです。

.NET MAUI クラスライブラリに入れるもの・入れないもの

ライブラリに入れると便利なもの

実務でよくまとめられる代表的なものを、用途別に整理します。

カテゴリ具体例ポイント
カスタムコントロールNumericEntry、ValidatedEntry、IconButton、Badge付きタブなど複数アプリで同じ UI 仕様を再利用可能。テストもまとめて行える
Behavior / Converter数字のみ入力、必須チェック、日付フォーマット変換などXAML から簡単に使える「UI 用ロジック」を集約
共通サービスAPI クライアント、ローカル DB アクセス、ログ出力、設定管理DI で差し替え可能にしておくとテストと拡張が楽になる
ResourceDictionaryカラーセット、スタイル、マージン、コントロールテンプレート「ブランドガイドライン」をコード化したイメージで管理
フォント・画像共通アイコン、ロゴ、共通フォント同じ見た目をどのアプリでも実現できる

ライブラリに入れないほうがよいもの

  • 各アプリ固有の画面・ナビゲーション構成
  • アプリごとに完全に異なる業務ロジック
  • 認証方式など、アプリ単位で変わる可能性が高い実装(共通化しすぎないほうが安全な部分)

あくまで「複数アプリで本当に共通にしたい部分だけをライブラリに逃がす」のがシンプルで長続きする設計です。

.NET MAUI クラスライブラリを使うメリット

再利用性・保守性の向上

MAUI アプリが 1 つだけのうちは、すべてのコードを 1 プロジェクトに詰め込んでも大きな問題には見えません。しかし、次のような状況になると一気に破綻しやすくなります。

  • BtoC 向け・社内向けなど、似たような機能を持つアプリが 2 個、3 個と増える
  • チームごとに別アプリを担当しているが、見た目や UX はそろえたい
  • あとから仕様変更が入り、すべてのアプリで共通仕様に変えたい

このとき、共通部分がクラスライブラリにまとまっているかどうかで保守コストが劇的に変わります。

項目クラスライブラリなしクラスライブラリあり
同じバグ修正アプリごとにコピペしたコードを全部修正ライブラリ側を修正し、各アプリは再ビルドのみ
UI の仕様変更画面単位でスタイル修正。漏れやズレが出やすい共通スタイル・コントロールを書き換えるだけ
レビューのしやすさプロジェクトごとに似たコードが散乱共通部品にレビューを集中できる

責務分離による設計の見通し改善

MAUI クラスライブラリを導入すると、自然とプロジェクトの責務が分離されるようになります。

  • アプリプロジェクト:画面構成、ナビゲーション、アプリ固有のロジック
  • MAUI クラスライブラリ:UI コンポーネント、共通サービス、共通リソース
  • 通常のクラスライブラリ:ドメインロジック、ビジネスルール

これにより「このバグはどこを直せばいいのか」「この仕様はどの層に属するのか」が分かりやすくなり、チーム開発でも迷子になりにくくなります。

NuGet 化による配布のしやすさ

MAUI クラスライブラリは、そのまま NuGet パッケージとして配布できます。 社内の共通 UI フレームワークとして提供したり、OSS として公開したりといった展開もしやすくなります。

  • CI でビルド&パッケージングして社内 NuGet サーバーに push
  • バージョン番号で互換性を管理しながら、アプリ側はバージョンアップだけで恩恵を受ける

.NET MAUI クラスライブラリの作り方(Visual Studio)

プロジェクトの新規作成手順

  1. Visual Studio の「新しいプロジェクトの作成」を開く
  2. 検索ボックスに「MAUI」と入力し、「.NET MAUI クラス ライブラリ」テンプレートを選択
  3. プロジェクト名(例:MyCompany.Maui.Shared)を入力し、作成
  4. 必要に応じて CommunityToolkit.Maui などの NuGet パッケージを追加
  5. フォルダー構成や名前空間を整えつつ、カスタムコントロール・サービス・リソースを実装

フォルダー構成の一例

MyCompany.Maui.Shared/
  Controls/
    NumericEntry.cs
    IconButton.xaml
    IconButton.xaml.cs
  Behaviors/
    NumericOnlyBehavior.cs
  Converters/
    StringToBoolConverter.cs
  Services/
    IApiClient.cs
    ApiClient.cs
  Resources/
    Styles.xaml
    Colors.xaml
  Fonts/
    MyFont-Regular.ttf
  • Controls:共通で使うカスタムコントロール
  • Behaviors / Converters:UI 用の簡易ロジック
  • Services:HTTP クライアントなどの共通サービス
  • Resources:スタイル・カラーなどの XAML リソース
  • Fonts:共通フォントファイル(ビルドアクションを後述)

csproj のポイント(マルチターゲットなど)

MAUI クラスライブラリでは、TargetFrameworks に複数のプラットフォームを指定しておくことで、プラットフォームごとに異なる実装を利用できるようになります。

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFrameworks>net8.0-android;net8.0-ios;net8.0-windows10.0.19041.0</TargetFrameworks>
    <UseMaui>true</UseMaui>
  </PropertyGroup>

  <ItemGroup>
    <MauiFont Include="Fonts\MyFont-Regular.ttf" Alias="MyFont" />
    <MauiImage Include="Resources\Images\*.png" />
  </ItemGroup>
</Project>

ここで <UseMaui>true</UseMaui> を指定することで、MAUI 向けのビルドアクション(MauiImage, MauiFont など)が使えるようになる点が重要です。

具体例:数字のみ入力できる Entry をライブラリ化する

ライブラリ側:NumericEntry の実装

数字以外の文字を自動的に除去する NumericEntry を MAUI クラスライブラリ側に定義してみます。

using Microsoft.Maui.Controls;

namespace MyLib.Controls;

public class NumericEntry : Entry
{
    public NumericEntry()
    {
        Keyboard = Keyboard.Numeric;

        TextChanged += (_, e) =&gt;
        {
            if (e.NewTextValue is null)
                return;

            var filtered = new string(e.NewTextValue.Where(char.IsDigit).ToArray());
            if (filtered != e.NewTextValue)
            {
                // 非数字を除去
                Text = filtered;
            }
        };
    }
}

ポイントは以下の通りです。

  • Keyboard を Numeric に固定しているので、ソフトウェアキーボードが数字向けになります。
  • TextChanged イベントで入力値を監視し、数字以外の文字を除去しています。
  • UI に密接に関係する処理なので、MAUI クラスライブラリ側に置くと再利用しやすくなります。

アプリ側(XAML)から利用する

MAUI アプリプロジェクト側では、ライブラリをプロジェクト参照または NuGet 参照で追加したうえで、XAML から次のように利用します。

&lt;?xml version="1.0" encoding="utf-8" ?&gt;
&lt;ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:controls="clr-namespace:MyLib.Controls;assembly=MyLib"&gt;

    &lt;VerticalStackLayout Padding="24"&gt;
        &lt;controls:NumericEntry Placeholder="数字のみ" /&gt;
    &lt;/VerticalStackLayout&gt;
&lt;/ContentPage&gt;

アプリ側に NumericEntry のコードは一切なくても、名前空間とアセンブリ名さえ指定すれば利用できるのが MAUI クラスライブラリの利点です。

共通リソース(スタイル・色・フォント)をライブラリで管理する

ライブラリ側で ResourceDictionary を定義

アプリのブランドカラーや標準マージンなどを決めておくと、どのアプリでも統一された見た目を保てます。これを MAUI クラスライブラリで一元管理します。

&lt;ResourceDictionary xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
                    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
                    x:Class="MyLib.Resources.Styles"&gt;

    &lt;Color x:Key="PrimaryColor"&gt;#2196F3&lt;/Color&gt;
    &lt;Color x:Key="PrimaryTextColor"&gt;#FFFFFF&lt;/Color&gt;

    &lt;Style TargetType="Button" x:Key="PrimaryButtonStyle"&gt;
        &lt;Setter Property="BackgroundColor" Value="{StaticResource PrimaryColor}" /&gt;
        &lt;Setter Property="TextColor" Value="{StaticResource PrimaryTextColor}" /&gt;
        &lt;Setter Property="CornerRadius" Value="8" /&gt;
        &lt;Setter Property="Padding" Value="16,10" /&gt;
    &lt;/Style&gt;

&lt;/ResourceDictionary&gt;

アプリ側の App.xaml にマージする

ライブラリで定義した ResourceDictionary は、アプリ側でマージすることで利用できます。

&lt;Application xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:resources="clr-namespace:MyLib.Resources;assembly=MyLib"
             x:Class="MyApp.App"&gt;

    &lt;Application.Resources&gt;
        &lt;ResourceDictionary&gt;
            &lt;ResourceDictionary.MergedDictionaries&gt;
                &lt;resources:Styles /&gt;
            &lt;/ResourceDictionary.MergedDictionaries&gt;
        &lt;/ResourceDictionary&gt;
    &lt;/Application.Resources&gt;
&lt;/Application&gt;

こうすることで、アプリ側からは次のように簡単に使えるようになります。

&lt;Button Text="送信" Style="{StaticResource PrimaryButtonStyle}" /&gt;

複数アプリですべて同じスタイルを使う場合でも、スタイルの定義はライブラリの 1 箇所だけで済みます。

共通サービスを MAUI クラスライブラリにまとめる

サービスインターフェースと実装

UI とは直接関係しないサービスコードも、MAUI クラスライブラリ側にまとめておくと、アプリ側のコードがすっきりします。 ここでは、シンプルな HTTP クライアントサービスの例を示します。

public interface IApiClient
{
    Task&lt;string&gt; GetAsync(string path, CancellationToken cancellationToken = default);
}

public class ApiClient : IApiClient
{
    private readonly HttpClient _http;

    public ApiClient(HttpClient http)
    {
        _http = http;
    }

    public Task&lt;string&gt; GetAsync(string path, CancellationToken cancellationToken = default)
        =&gt; _http.GetStringAsync(path, cancellationToken);
}

MauiProgram での DI 登録(アプリ側)

using MyLib.Services;

public static class MauiProgram
{
    public static MauiApp CreateMauiApp()
    {
        var builder = MauiApp.CreateBuilder();

        builder
            .UseMauiApp&lt;App&gt;();

        builder.Services.AddHttpClient&lt;IApiClient, ApiClient&gt;(client =&gt;
        {
            client.BaseAddress = new Uri("https://api.example.com/");
        });

        return builder.Build();
    }
}

サービスの型定義や基本的な実装をライブラリで共有しつつ、BaseAddress などのアプリ固有の設定だけをアプリ側で行うと、責務分担がきれいになります。

プラットフォーム別コードをライブラリ内で扱う方法

マルチターゲット + 条件コンパイル

MAUI クラスライブラリで「Android のときだけこのコードを動かしたい」「Windows だけ別実装にしたい」といったケースでは、次のような手法を組み合わせます。

手法概要向いている場面
条件コンパイル(#if ANDROID など)1 ファイル内でプラットフォームごとにコードを分岐ちょっとした差分だけある場合
部分クラス(partial class)共通部分とプラットフォームごとの差分を別ファイルに分ける差分が大きく、ファイルを分けて管理したい場合
プラットフォーム別フォルダーPlatforms/Android などのフォルダーに別実装を配置MAUI アプリと同じ思想で整理したい場合

条件コンパイルの例:

public static class DeviceInfoHelper
{
    public static string GetPlatformName()
    {
#if ANDROID
        return "Android";
#elif IOS
        return "iOS";
#elif WINDOWS
        return "Windows";
#else
        return "Unknown";
#endif
    }
}

このように、プラットフォーム固有コードもライブラリ側に閉じ込めておけるため、アプリ側のコードはよりシンプルになります。

アプリ側から .NET MAUI クラスライブラリを利用する手順

1. プロジェクト参照または NuGet 参照を追加

  • 同一ソリューション内にライブラリがある場合:
    「プロジェクトを追加」→「プロジェクト参照」からライブラリを選択
  • NuGet パッケージとして配布されている場合:
    NuGet パッケージマネージャーから該当パッケージを追加

2. 必要に応じて MauiProgram で初期化処理

CommunityToolkit など、UseXXX() 拡張メソッドで初期化が必要なパッケージを内部で利用している場合は、アプリ側の MauiProgram で初期化を呼ぶ必要があります。

using CommunityToolkit.Maui;

public static class MauiProgram
{
    public static MauiApp CreateMauiApp()
    {
        var builder = MauiApp.CreateBuilder();

        builder
            .UseMauiApp&lt;App&gt;()
            .UseMauiCommunityToolkit(); // 例

        return builder.Build();
    }
}

3. XAML から名前空間を指定して利用

カスタムコントロール・Behavior・Converter などは、XAML 上で名前空間を指定して利用します。

&lt;ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:controls="clr-namespace:MyLib.Controls;assembly=MyLib"
             xmlns:behaviors="clr-namespace:MyLib.Behaviors;assembly=MyLib"&gt;

    &lt;Entry Placeholder="数字のみ"&gt;
        &lt;Entry.Behaviors&gt;
            &lt;behaviors:NumericOnlyBehavior /&gt;
        &lt;/Entry.Behaviors&gt;
    &lt;/Entry&gt;

    &lt;controls:NumericEntry Placeholder="NumericEntry 版" /&gt;

&lt;/ContentPage&gt;

よくあるハマりどころと対策

症状原因対策
XAML からカスタムコントロールが見つからないxmlns の clr-namespace や assembly が誤っている名前空間・アセンブリ名(プロジェクト名)を再確認する
スタイルや色のリソースが参照できないApp.xaml への MergedDictionaries の追加漏れライブラリ側の ResourceDictionary を App.xaml でマージする
ビルド時にターゲットフレームワークのエラーアプリとライブラリの TargetFrameworks が合っていないMAUI アプリと同じ .NET バージョン・ターゲットを指定する
プラットフォーム固有 API 呼び出しで例外プラットフォーム判定なしで API を呼んでいる条件コンパイル・DeviceInfo・partial class などでプラットフォームを分ける
NuGet 化したときだけ動かないビルドアクションやリソースの設定がパッケージ化に対応していないMauiImage, MauiFont の設定と csproj の内容を再確認する

発展例:社内 MAUI UI フレームワークとして育てる

MAUI クラスライブラリは、作って終わりではなく、「社内共通 UI フレームワーク」として育てていくと真価を発揮します。

  • よく使う画面パターン(検索条件 + 一覧 + 詳細など)をカスタムコントロール化
  • エラー表示やローディング表示の統一コンポーネントを用意
  • ログ出力・例外処理・トラッキングなどの横断的機能を共通化
  • バージョンごとに「Breaking Change の有無」を明確にし、アプリ側が追従しやすいようにする

最初は小さくてもかまいません。まずは 1 つのカスタムコントロール・1 つの ResourceDictionary から始め、アプリ開発の中で「これは毎回書いているな」と感じたものを少しずつライブラリに移していくと、無理なく育てていけます。

まとめ:.NET MAUI クラスライブラリをうまく使うコツ

  • .NET MAUI クラスライブラリは、MAUI アプリ専用の「共通部品置き場」。
  • カスタムコントロール、Behavior / Converter、共通サービス、スタイル・フォント・画像などをまとめて再利用することで、開発効率と保守性が大きく向上する。
  • 通常の .NET クラスライブラリとは役割が違うので、UI を含むかどうかを基準に使い分ける。
  • プラットフォーム固有コードは、マルチターゲット・条件コンパイル・partial class を組み合わせて整理する。
  • リソースは ResourceDictionary と MergedDictionaries で管理し、ブランドデザインをコードとして共有する。
  • 将来的には NuGet 化して、社内共通の MAUI UI フレームワークとして育てていくと、アプリが増えるほど効果が出る。

まずは小さなカスタム Entry や共通スタイルから MAUI クラスライブラリに切り出してみて、実際に複数アプリから使ってみると、その便利さを実感できます。MAUI プロジェクトを育てていくつもりなら、早い段階から .NET MAUI クラスライブラリを導入しておくことを強くおすすめします。

この記事を書いた人

実務の現場で詰まりがちなポイントを地図にするITブログ「IT trip」を運営。Windows/Office(Teams・Excel)からSQL、サーバ運用、ガジェットまで、再現性のある手順と“なぜそうなるか”を丁寧に解説します。読んだらすぐ試せること、そして迷った人の次の一歩が見えることを大切にしています。

コメント

コメントする

目次