.NET MAUIでAndroidのEntry・Picker下線を消して背景色も自由に変更する方法

.NET MAUI で Android 向けの画面を作っていると、Entry や Picker の「下線(アンダーライン)」だけを消しつつ、背景色は XAML 側で自由に変えたい、というニーズがよくあります。ところが、既存のノウハウ通りに背景を透過させると、今度は BackgroundColor が効かなくなりがちです。この記事では、Android の BackgroundTintList を正しく理解しながら、「下線を消す」「背景色を好きな色にする」「ページごとに切り替える」を全部同時に実現する具体的な方法を整理します。

目次

.NET MAUI(Android)の Entry / Picker における「下線」と「背景」の正体

まずは「なぜ下線だけを消すのが難しいのか?」という背景から整理しておきます。

.NET MAUI の Entry / Picker は、Android では内部的に AppCompatEditText や AppCompatSpinner にマッピングされます。Material Design っぽい下線は、これらのコントロールの 背景 Drawable に対するティント(色付け)として表現されています。

要素役割MAUI からよく触る API
背景 Drawable角丸・下線の形・枠線など「形」を決めるBackground / BackgroundColor
背景 Tint(BackgroundTintList)背景 Drawable に対して「何色で描くか」を決めるBackgroundTintList(Android ネイティブ側)

よくある「下線を消すテクニック」では、背景 Drawable を差し替えたり透過にしたりしますが、そのときに MAUI 側の BackgroundColor が無視されてしまうことがあります。これは、

  • 背景 Drawable をゼロから作り直してしまう
  • それによって MAUI が期待している描画フローと違う状態になる

といった理由から起こります。

そこで発想を変えて、

  • 背景 Drawable 自体は極力いじらない
  • 下線の色(=ティント)だけを触る

という方針にすると、XAML 側の BackgroundColor とうまく共存できます。

基本方針:BackgroundTintList だけを操作して下線を制御する

Entry / Picker の下線は BackgroundTintList で制御できます。ポイントは次の通りです。

  • 下線を消したい → BackgroundTintList = Transparent
  • 下線の色を変えたい → BackgroundTintList = 任意の色
  • 背景の塗りつぶしは Background / BackgroundColor に任せる

.NET MAUI の Handler の Mapper を使うと、Android のネイティブコントロールに対して手軽に処理を差し込めます。まずはシンプルに「すべての Entry / Picker の下線を消す」例です。

// MauiProgram.cs など(グローバルに適用される例)
using Microsoft.Maui;
using Microsoft.Maui.Controls;
using Microsoft.Maui.Handlers;
using Microsoft.Maui.Platform;

#if ANDROID
using Android.Content.Res;
#endif

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

#if ANDROID
        EntryHandler.Mapper.AppendToMapping("NoUnderline", (handler, view) =>
        {
            handler.PlatformView.BackgroundTintList =
                ColorStateList.ValueOf(Colors.Transparent.ToAndroid());
        });

        PickerHandler.Mapper.AppendToMapping("NoUnderline", (handler, view) =>
        {
            handler.PlatformView.BackgroundTintList =
                ColorStateList.ValueOf(Colors.Transparent.ToAndroid());
        });
#endif

        return builder.Build();
    }
}

このコードでは Background や BackgroundColor には触れていません。そのため、XAML 側で

<Entry BackgroundColor="#FFF8E1" />

と指定すれば、背景色はそのまま効きつつ、Android の下線だけ透明になります。

BackgroundTintList を使うときの注意点

  • BackgroundTintList は Android 固有の API なので、#if ANDROID で囲っておく
  • Mapper に設定した内容は「以降に生成される全てのコントロール」に効く(=グローバル)
  • そのため「ページごとに色を変えたい」場合は、適用方法を工夫する必要がある

ページごとに Entry / Picker の下線色を切り替える実装例

次に、「画面 A は下線なし」「画面 B は赤い下線」のようにページ単位で切り替えたいケースを考えます。

ここではあえて簡単な方法として、各ページのコンストラクターで Mapper.AppendToMapping を呼び出すパターンを紹介します。

MainPage:下線なし(透明)

public partial class MainPage : ContentPage
{
    public MainPage()
    {
        InitializeComponent();

#if ANDROID
        EntryHandler.Mapper.AppendToMapping("NoUnderline", (h, v) =>
        {
            h.PlatformView.BackgroundTintList =
                Android.Content.Res.ColorStateList.ValueOf(Colors.Transparent.ToAndroid());
        });

        PickerHandler.Mapper.AppendToMapping("NoUnderline", (h, v) =>
        {
            h.PlatformView.BackgroundTintList =
                Android.Content.Res.ColorStateList.ValueOf(Colors.Transparent.ToAndroid());
        });
#endif
    }
}

NewPage1:下線を赤色にする

public partial class NewPage1 : ContentPage
{
    public NewPage1()
    {
        InitializeComponent();

#if ANDROID
        var red = Android.Content.Res.ColorStateList.ValueOf(Colors.Red.ToAndroid());

        EntryHandler.Mapper.AppendToMapping("NoUnderline", (h, v) =>
        {
            h.PlatformView.BackgroundTintList = red;
        });

        PickerHandler.Mapper.AppendToMapping("NoUnderline", (h, v) =>
        {
            h.PlatformView.BackgroundTintList = red;
        });
#endif
    }
}

このようにページのコンストラクター内で Mapper を触ると、

  • そのページが作られたタイミング以降に生成される Entry / Picker すべてに下線設定が適用される

という動きになります。

Mapper.AppendToMapping の落とし穴

ただし重要なポイントとして、Mapper は静的(グローバル)です。ページごとに呼んでいるように見えても、実際にはアプリ全体に対して上書き/追記されていきます。

パターンメリットデメリット
Constructor で AppendToMapping実装が簡単・サンプルが少ないときは手早いどのタイミングで何色になっているかを追いにくい/ページを閉じても元に戻らない
MauiProgram で一括設定アプリ全体で統一したスタイルにしたいときに便利ページごとに色を変えるのが難しい
カスタムコントロール+ハンドラー影響範囲をコントロール単位に閉じ込められる実装コードは増えるが、保守性は高い

実務で「ページ単位/コントロール単位で安全に切り替えたい」のであれば、カスタムコントロール+カスタムハンドラーに寄せていくと安心です。

推奨:カスタムコントロール+カスタムハンドラーで下線と背景色をコントロールする

ここからは、より堅牢で再利用しやすい実装パターンを紹介します。

  • カスタム Entry:NoUnderlineEntry
  • カスタム Picker:NoUnderlinePicker(必要なら)
  • Android 専用カスタムハンドラー:下線色を BindableProperty から受け取る

NoUnderlineEntry(カスタム Entry)の実装

using Microsoft.Maui.Controls;

namespace YourApp.Controls
{
    // 下線色をバインド可能にしたカスタム Entry
    public class NoUnderlineEntry : Entry
    {
        public static readonly BindableProperty UnderlineColorProperty =
            BindableProperty.Create(
                nameof(UnderlineColor),
                typeof(Color),
                typeof(NoUnderlineEntry),
                Colors.Transparent);

        public Color UnderlineColor
        {
            get => (Color)GetValue(UnderlineColorProperty);
            set => SetValue(UnderlineColorProperty, value);
        }
    }
}

このクラスは、通常の Entry に UnderlineColor というバインド可能プロパティを追加しただけです。実際に Android ネイティブの BackgroundTintList を触るのは、ハンドラー側で行います。

NoUnderlineEntryHandler(Android 用ハンドラー)の実装

#if ANDROID
using Android.Content.Res;
using Microsoft.Maui.Handlers;
using Microsoft.Maui.Platform;
using YourApp.Controls;

namespace YourApp.Platforms.Android
{
    public class NoUnderlineEntryHandler : EntryHandler
    {
        protected override void ConnectHandler(
            AndroidX.AppCompat.Widget.AppCompatEditText platformView)
        {
            base.ConnectHandler(platformView);

            if (VirtualView is NoUnderlineEntry entry)
            {
                ApplyUnderline(platformView, entry);
            }
        }

        protected override void DisconnectHandler(
            AndroidX.AppCompat.Widget.AppCompatEditText platformView)
        {
            base.DisconnectHandler(platformView);
        }

        // プロパティ変更にも対応させたい場合は Mapper を拡張してもよい
        public static void MapUnderlineColor(NoUnderlineEntryHandler handler, NoUnderlineEntry entry)
        {
            ApplyUnderline(handler.PlatformView, entry);
        }

        static void ApplyUnderline(AndroidX.AppCompat.Widget.AppCompatEditText v, NoUnderlineEntry e)
        {
            var color = e.UnderlineColor.ToAndroid();
            v.BackgroundTintList = ColorStateList.ValueOf(color);
        }
    }
}
#endif

ConnectHandler で初期状態の下線色を反映し、必要があれば MapUnderlineColor を Mapper に登録してプロパティ変更にも対応させる、という構成です。

MauiProgram でのカスタムハンドラー登録

using YourApp.Controls;
using YourApp.Platforms.Android;

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

        builder
            .UseMauiApp<App>()
            .ConfigureMauiHandlers(handlers =>
            {
#if ANDROID
                handlers.AddHandler(typeof(NoUnderlineEntry), typeof(NoUnderlineEntryHandler));
#endif
            });

        return builder.Build();
    }
}

これで、XAML 側では普通のコントロールと同じ感覚で使えるようになります。

XAML でページごとに色と背景を切り替える

<ContentPage
    xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
    xmlns:controls="clr-namespace:YourApp.Controls">

    <VerticalStackLayout Padding="24">

        <Label Text="下線なし+背景クリーム" />
        <controls:NoUnderlineEntry
            UnderlineColor="Transparent"
            BackgroundColor="#FFF8E1"
            Placeholder="背景色だけ付けたい Entry" />

        <Label Text="赤い下線+背景白" Margin="0,16,0,0" />
        <controls:NoUnderlineEntry
            UnderlineColor="Red"
            BackgroundColor="White"
            Placeholder="エラー入力用など" />

    </VerticalStackLayout>
</ContentPage>

このようにすると、

  • 背景色は MAUI の BackgroundColor で自由に指定
  • 下線色は UnderlineColor でページごと/コントロールごとにバインド可能

という形で分離できるため、スタイルの整理がかなり楽になります。

Picker 版(NoUnderlinePicker)の実装イメージ

Picker でも考え方は同じです。

  1. NoUnderlinePicker : Picker を定義し、UnderlineColor プロパティを追加
  2. Android 用に NoUnderlinePickerHandler : PickerHandler を用意
  3. Handler の ConnectHandler で BackgroundTintList を設定
  4. MauiProgram で AddHandler(typeof(NoUnderlinePicker), typeof(NoUnderlinePickerHandler)) を登録

コードは Entry 版とほぼ同じ構造になるので、複製してクラス名・型を置き換えるだけで実装できます。

背景そのものを塗りつぶしたい場合:Drawable 差し替えのサンプル

ここまでは「下線だけをいじる」アプローチでしたが、実務では

  • 角丸の塗りつぶし背景
  • 枠線なし(=完全なフラットデザイン)

のようなデザインを求められることも多いです。その場合は、下線を透明にしつつ、背景 Drawable を自前で構築するという手もあります。

Mapper を使ったシンプルな例を示します。

#if ANDROID
using Android.Content.Res;
using Android.Graphics.Drawables;
using Microsoft.Maui.Devices;
using Microsoft.Maui.Handlers;
using Microsoft.Maui.Platform;

EntryHandler.Mapper.AppendToMapping("NoUnderlineFill", (h, v) =>
{
    // 下線は完全に無効化
    h.PlatformView.BackgroundTintList =
        ColorStateList.ValueOf(Android.Graphics.Color.Transparent);

    // 角丸+塗りつぶし背景
    var density = (float)DeviceDisplay.MainDisplayInfo.Density;
    var radius = 8f * density; // 8dp を px に変換

    var drawable = new GradientDrawable();
    drawable.SetColor(Colors.LightYellow.ToAndroid()); // 塗りつぶし色
    drawable.SetCornerRadius(radius);
    drawable.SetStroke(0, Android.Graphics.Color.Transparent); // 枠線なし

    h.PlatformView.Background = drawable;
});
#endif

この方法の特徴は次の通りです。

項目内容
メリット完全に思い通りの見た目(角丸・塗りつぶし・枠線有無など)を実現しやすい
デメリットAndroid ネイティブ依存度が高くなり、XAML 側の BackgroundColor とは競合しやすい
おすすめ用途ログイン画面やフォームなど、特定画面でだけ使う専用デザインコンポーネント

MAUI 側の BackgroundColor と両立させたい場合は、

  • Drawable にも色を設定し、XAML 側では背景色を固定にする
  • または XAML から色を受け取って Drawable に反映させる(カスタムコントロール+ハンドラー)

といった工夫が必要になります。

.NET バージョン差異と ToAndroid / ToPlatform の注意点

.NET MAUI はバージョンにより若干 API が変わることがあります。特に Colors.Xxx.ToAndroid() 系の拡張メソッドは、環境によって ToPlatform() に変わっている場合があります。

環境の例拡張メソッド備考
.NET 7 / .NET 8 の一部テンプレートColors.Red.ToAndroid()古い記事やサンプルコードでよく見る書き方
新しめの .NET MAUI テンプレートColors.Red.ToPlatform()プラットフォームごとに同じメソッド名で統一する意図

コンパイルエラーが出る場合は、

  • 自分のプロジェクトでどちらのメソッドが使えるか
  • 使用している名前空間(Microsoft.Maui.Platform)がインポートされているか

を確認し、

  • ToAndroid() を ToPlatform() に読み替える
  • もしくはその逆

を行ってください。

実務で役立つパターン集:下線+背景色の組み合わせ例

ここからは、実際の画面設計でよくあるパターンに落とし込んでみます。

目的下線設定背景色の例実装のポイント
シンプルなフォーム入力下線なし(Transparent)#FFF8E1(薄いクリーム)視認性を保ちつつ、Material 感を弱めて柔らかい印象に
エラー状態の強調下線を RedWhiteテキスト色とエラーメッセージも赤系にそろえると統一感が出る
検索ボックス風下線なし#EEEEEE(薄いグレー)、角丸+アイコンDrawable で角丸+アイコン(CompoundDrawable)を組み合わせると UI ぽくなる
ダークテーマ対応フォーム下線はライトグレー背景は黒に近いグレーAppThemeBinding でライト/ダークごとに UnderlineColor を切り替える

MAUI の強みは、XAML 側でテーマ切り替えやスタイル統一がしやすいことです。今回紹介したように、

  • 下線:Android ハンドラー側(BackgroundTintList)
  • 背景:XAML 側の BackgroundColor / スタイル

と責務分離しておくと、デザイナーからの仕様変更にも対応しやすくなります。

AppThemeBinding を使ってライト/ダークごとに下線色を変える

もう一歩踏み込んで、ライトテーマとダークテーマで下線色を自動切り替えする例を見てみます。

まず、App.xaml にリソースを定義します。

<Application.Resources>
    <ResourceDictionary>
        <AppThemeBinding x:Key="EntryUnderlineColor"
                         Light="Transparent"
                         Dark="#55FFFFFF" />

        <Style TargetType="controls:NoUnderlineEntry">
            <Setter Property="UnderlineColor"
                    Value="{StaticResource EntryUnderlineColor}" />
            <Setter Property="BackgroundColor" Value="{AppThemeBinding
                    Light='White', Dark='#222222'}" />
            <Setter Property="TextColor" Value="{AppThemeBinding
                    Light='Black', Dark='White'}" />
        </Style>
    </ResourceDictionary>
</Application.Resources>

こうしておくと、ページ側では何も意識せずに

<controls:NoUnderlineEntry Placeholder="ユーザー名" />

と書くだけで、

  • ライトテーマ:下線なし+背景白+文字黒
  • ダークテーマ:半透明の白い下線+暗めの背景+文字白

といった感じに自動で切り替わります。

プラットフォームエフェクトを使うという選択肢

ここまでカスタムハンドラーで解説してきましたが、.NET MAUI では「プラットフォームエフェクト」で特定のコントロールにだけネイティブ処理を差し込むこともできます。

エフェクトを使う場合の特徴は次の通りです。

項目内容
適用単位コントロール単位(XAML 側で Effects を指定したものだけに適用)
メリット既存コントロールにも後付けで適用しやすい/影響範囲を限定しやすい
デメリット実装がやや冗長/カスタムコントロールとの組み合わせ設計が必要

大規模プロジェクトでは、

  • 見た目を変えたいコンポーネントは「カスタムコントロール+ハンドラー」で標準化
  • 既存画面にだけ局所的な変更を加えたいときにエフェクトを使う

という棲み分けにすると、コードが整理しやすくなります。

ライフサイクルで Mapper を切り替える上級テクニック

よりトリッキーな方法として、ページのライフサイクル(OnAppearing / OnDisappearing)で Mapper を付け替えるというやり方もあります。

  • OnAppearing で EntryHandler.Mapper.AppendToMapping / ModifyMapping して下線色を設定
  • OnDisappearing で元の設定に戻す

ただし、

  • Mapper が静的である以上、別ページで生成中のコントロールに影響してしまう可能性がある
  • 戻し忘れや例外などで「一部のページでおかしな色が残る」事故が起こりやすい

といったリスクがあるため、明確な理由がない限りはカスタムコントロール方式を優先した方が安全です。

よくあるトラブルと対処法

症状原因の例対処のポイント
Entry の背景色が XAML で指定したものと違うAndroid 側で Drawable を差し替えている/テーマの Layer が重なっているDrawable 差し替えを一旦やめて BackgroundTintList のみに戻す/カスタムコントロール側で統一
特定のページだけ下線色が変わらないMapper の設定順・タイミングの問題ページ単位ではなくコントロール単位(カスタムハンドラー)に寄せていく
バージョンアップ後にコンパイルエラーToAndroid() → ToPlatform() などの API 変更名前空間と拡張メソッド名を確認し、自分の環境に合わせて読み替え
ダークモードで文字が読みにくいBackgroundColor と TextColor のペア設計が不十分AppThemeBinding でライト/ダークそれぞれの色をきちんと指定し、コントラストをチェック

まとめ:下線と背景色を分離して考えると設計が楽になる

最後に、.NET MAUI(Android)の Entry / Picker の下線と背景色を制御するポイントを整理します。

  • 下線は BackgroundTintList、背景は Background / BackgroundColor と役割を分けて考える
  • 「下線を消したいだけ」のときは、BackgroundTintList = Transparent を設定し、背景の Drawable には触らない
  • ページごと/コントロールごとに色を変えたいときは、カスタムコントロール+カスタムハンドラーで影響範囲を限定する
  • 特別なデザイン(角丸塗りつぶしなど)が必要な場合は Drawable 差し替えを検討するが、その場合は XAML 側の BackgroundColor との関係を意識する
  • .NET のバージョン差異で ToAndroid() / ToPlatform() が変わることがあるので、手元の環境に合わせて読み替える

この方針に沿っておけば、

  • 下線を消したい
  • 背景色を好きに変えたい
  • ページごとに色を切り替えたい

といった要件を無理なく同時に満たすことができます。Material Design っぽいデフォルトの見た目に縛られず、自分たちのアプリに合った UI をきれいに作り込んでいきましょう。

この記事を書いた人

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

コメント

コメントする

目次