.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 でも考え方は同じです。
NoUnderlinePicker : Pickerを定義し、UnderlineColorプロパティを追加- Android 用に
NoUnderlinePickerHandler : PickerHandlerを用意 - Handler の
ConnectHandlerでBackgroundTintListを設定 - 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 感を弱めて柔らかい印象に |
| エラー状態の強調 | 下線を Red | White | テキスト色とエラーメッセージも赤系にそろえると統一感が出る |
| 検索ボックス風 | 下線なし | #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 をきれいに作り込んでいきましょう。

コメント