アイキャッチ画像

.Net MAUIのコントロールをハンドラーでカスタマイズする方法について

.Net MAUIのコントロールをハンドラーでカスタマイズする方法を紹介します。

紹介環境

当記事は以下の環境で作成しています。

開発環境

  • Visual Studio 2022
  • .Net 9

ハンドラーについて

ハンドラーは、プラットフォームごとに存在しており、クロスプラットフォームコントロールをネイティブビューに割り当てる役割をしています。

イメージにすると以下のようになります。

.Net MAUIのハンドラーがネイティブビューに割り当てるイメージ

Microsoftの説明は、以下のリンクから確認できます。

コントロールのカスタマイズ方法について

ハンドラーでコントロールをカスタマイズする場合、全ページに適用するか、特定のページに適用するかで対応方法が異なります。ここでは、それぞれ分けて説明していきます。

全ページに適用する場合

全ページに適用する場合、ハンドラーのマッパーを利用します。マッパーは、クロスプラットフォームのAPIをネイティブビューのAPIに割り当てしています。

マッパーの変更には、以下のいずれかの方法を利用します。

  • PrependToMapping:クロスプラットフォームコントロールのマッパーを適用する前にマッパーを変更します。
  • ModifyMapping:既存のマッパーを変更します。
  • AppendToMapping:クロスプラットフォームコントロールのマッパーを適用された後にマッパーを変更します。

各メソッドは、2つの引数を必要としています。

1つ目の引数は、文字列を指定します。文字列の内容は、クロスプラットフォームのプロパティ名などを指定します。

2つ目の引数は、カスタマイズを行うメソッドを指定します。1つ目の引数がクロスプラットフォームのプロパティ名の場合、指定されたプロパティが変更されることでメソッドが呼び出されます。メソッドは、1つ目の引数に関係なく初回だけ呼び出されます。

以下では、Entryをカスタマイズしています。全ページへ適用するためにAppクラスを利用しています。

#if ANDROID

using AndroidX.AppCompat.Widget;

#endif

using Microsoft.Maui.Handlers;

// 名前空間はプロジェクトに合わせてください。
namespace SampleApp;

public partial class App : Application
{
    public App()
    {
        InitializeComponent();
    }

    protected override Window CreateWindow(IActivationState? activationState)
    {
        EntryHandler.Mapper.AppendToMapping("CustomEntry", (handler, view) =>
        {
            if (view is not Entry entry) return;
            SetBackgroundColor(entry.Text);

#if WINDOWS
            handler.PlatformView.TextChanging += (sender, e) =>
            {
                SetBackgroundColor(sender.Text);
            };
#elif ANDROID
            handler.PlatformView.TextChanged += (sender, e) =>
            {
                SetBackgroundColor((sender as AppCompatEditText)?.Text);
            };
#endif
            void SetBackgroundColor(string? text)
            {
                if (string.IsNullOrEmpty(text))
                {
                    entry.BackgroundColor = new Color(255, 150, 150);
                }
                else
                {
                    entry.BackgroundColor = new Color(255, 255, 255);
                    entry.TextColor = new Color(30, 30, 30);
                }
            }
        });

        return new Window(new AppShell());
    }
}

特定のページで利用したい場合

特定のページに適用する場合、コントロールのHandlerChangingイベントHandlerChangedイベントを利用します。

HandlerChangingイベントは、クロスプラットフォームコントロールのネイティブビューが作成中か削除された時に発火されるイベントになります。ネイティブビューのイベントを削除するときに利用します。

HandlerChangedイベントは、クロスプラットフォームコントロールのネイティブビューが作成された後に発火されるイベントになります。ネイティブビューのAPIを割り当てるときに利用します。

以下では、Entryをカスタマイズしています。

<Entry
    HandlerChanging="Entry_HandlerChanging"
    HandlerChanged="Entry_HandlerChanged" />
#if WINDOWS

using Microsoft.UI.Xaml.Controls;

#elif ANDROID

using AndroidX.AppCompat.Widget;
using Microsoft.Maui.Controls.Platform;

#endif

using Microsoft.Maui.Handlers;
using Microsoft.Maui.Platform;

// 名前空間はプロジェクトに合わせてください。
namespace SampleApp;

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

    private void Entry_HandlerChanging(object sender, HandlerChangingEventArgs e)
    {
#if WINDOWS

        if (e.OldHandler?.PlatformView is TextBox textBox)
        {
            textBox.TextChanging -= TextBox_TextChanging;
        }

#elif ANDROID

        if (e.OldHandler?.PlatformView is AppCompatEditText editText)
        {
            editText.TextChanged -= EditText_TextChanged;
        }

#endif
    }

    private void Entry_HandlerChanged(object sender, EventArgs e)
    {
        if (sender is not Entry entry) return;
#if WINDOWS

        if (entry.Handler?.PlatformView is TextBox textBox)
        {
            textBox.Background = new Color(255, 150, 150).ToPlatform();
            textBox.TextChanging += TextBox_TextChanging;
        }

#elif ANDROID

        if (entry.Handler?.PlatformView is AppCompatEditText editText)
        {
            editText.UpdateBackground(new Color(255, 150, 150));
            editText.TextChanged += EditText_TextChanged;
        }
#endif
    }

#if WINDOWS

    private void TextBox_TextChanging(TextBox sender, TextBoxTextChangingEventArgs args)
    {
        if (string.IsNullOrEmpty(sender.Text))
        {
            sender.Background = new Color(255, 150, 150).ToPlatform();
        }
        else
        {
            sender.Background = new Color(255, 255, 255).ToPlatform();
            sender.Foreground = new Color(30, 30, 30).ToPlatform();
        }
    }

#elif ANDROID

    private void EditText_TextChanged(object? sender, Android.Text.TextChangedEventArgs e)
    {
        if (sender is AppCompatEditText editText)
        {
            if (string.IsNullOrEmpty(editText.Text))
            {
                editText.UpdateBackground(new Color(255, 150, 150));
            }
            else
            {
                editText.UpdateBackground(new Color(255, 255, 255));
                editText.UpdateTextColor(new Color(30, 30, 30));
            }
        }
    }

#endif
}

終わりに

.Net MAUIのコントロールをハンドラーでカスタマイズする際、ページごとにAppendToMappingを利用していました。しかし、Appクラスで利用すると全ページに適用されたので、気づいたときは驚きました。この記事が役に立てば幸いです。

.Net MAUIのコントロールをハンドラーでカスタマイズしている記事を以下に集めました。よければ参考にしてみてください。