ハンドラーを使用して .NET MAUI コントロールをカスタマイズする

.NET マルチプラットフォーム アプリ UI (.NET MAUI) は、データの表示、アクションの開始、アクティビティの表示、コレクションの表示、データの選択などを行うために使用できるコントロールのコレクションを提供します。 各コントロールには、コントロールを抽象化するインターフェイス表現があります。 これらのインターフェイスを実装するクロスプラットフォーム コントロールは、 仮想ビューと呼ばれます。 ハンドラーは 、これらの仮想ビューを各プラットフォーム上のネイティブ コントロールにマップし、基になるネイティブ コントロールを作成し、そのプロパティをクロスプラットフォーム コントロールにマッピングする役割を担います。 たとえば、.NET MAUI ハンドラー iOSでは、.NET MAUI Button をiOS UIButton コントロールにマップします。 Androidでは、コントロールButtonAppCompatButtonマップされます。

Button handler architecture.

ハンドラーは、コントロール固有のインターフェイスを介してアクセスされます。たとえばIButtonButton、 これにより、クロスプラットフォーム コントロールがハンドラーを参照する必要が回避され、ハンドラーがクロスプラットフォーム コントロールを参照する必要がなくなります。 各ハンドラーは、クロスプラットフォーム コントロール API をネイティブ コントロール API にマップする マッパー を提供します。

ハンドラーをカスタマイズして、コントロールの API を通じて可能なカスタマイズ以外のクロスプラットフォーム コントロールの外観と動作を拡張できます。 このカスタマイズは、ハンドラーのマッパーを次のいずれかの方法で変更することによって実現されます。

  • PrependToMapping.NET MAUI コントロール マッピングが適用される前にハンドラーのマッパーを変更します。
  • ModifyMapping既存のマッピングを変更します。
  • AppendToMapping.NET MAUI コントロール マッピングが適用された後にハンドラーのマッパーを変更します。

これらの各メソッドには、2 つの引数を必要とする同じシグネチャがあります。

  • ベースの stringキー。 .NET MAUI によって提供されるマッピングの 1 つを変更する場合は、.NET MAUI で使用されるキーを指定する必要があります。 .NET MAUI コントロール マッピングで使用されるキー値は、インターフェイス名とプロパティ名などに nameof(IEntry.IsPassword)基づいています。 各クロスプラットフォーム コントロールを抽象化するインターフェイスとそのプロパティについては、 こちらを参照してください。 それ以外の場合、このキーには、型によって公開されるプロパティの名前に対応する必要のない任意の値を指定できます。 たとえば、 MyCustomization カスタマイズとしてネイティブ コントロールの変更を実行して、キーとして指定できます。
  • Actionハンドラーのカスタマイズを実行するメソッドを表すオブジェクト。 次 Action の 2 つの引数を指定します。
    • handlerカスタマイズするハンドラーのインスタンスを提供する引数。
    • viewハンドラーが実装するクロスプラットフォーム コントロールのインスタンスを提供する引数。

重要

ハンドラーのカスタマイズはグローバルであり、特定のコントロール インスタンスのスコープではありません。 ハンドラーのカスタマイズは、アプリ内の任意の場所で行うことができます。 ハンドラーをカスタマイズすると、アプリ内のあらゆる場所で、その種類のすべてのコントロールに影響します。

各ハンドラー クラスは、そのプロパティを介してクロスプラットフォーム コントロールを実装するネイティブ コントロールを PlatformView 公開します。 このプロパティにアクセスして、ネイティブ コントロール プロパティの設定、ネイティブ コントロール メソッドの呼び出し、ネイティブ コントロール イベントのサブスクライブを行うことができます。 さらに、ハンドラーによって実装されるクロスプラットフォーム コントロールは、その VirtualView プロパティを介して公開されます。

ハンドラーは、コンパイラ プリプロセッサ ディレクティブを使用してプラットフォームごとにカスタマイズし、プラットフォームに基づいてマルチターゲット コードにカスタマイズできます。 または、部分クラスを使用して、コードをプラットフォーム固有のフォルダーとファイルに整理することもできます。 条件付きコンパイルの詳細については、「 条件付きコンパイル」を参照してください。

ハンドラー ベースの .NET MAUI ビューを実装する型名の一覧については、「 ハンドラーベースのビュー」を参照してください。

マッパーを使用してコントロールをカスタマイズする

.NET MAUI Entry は、インターフェイスを実装 IEntry する 1 行のテキスト入力コントロールです。 iOSではEntryHandler、iOS UITextField コントロールにマップEntryされます。 Androidでは、コントロールEntryAppCompatEditTextマップされ、WindowsEntryではコントロールにTextBoxマップされます。

Entry handler architecture.

このクラスのEntryHandlerマッパーはEntry、クロスプラットフォーム コントロール API をネイティブ コントロール API にマップします。 このマッパーは、各プラットフォームでコントロールをカスタマイズするように変更できます。

using Microsoft.Maui.Platform;

namespace CustomizeHandlersDemo;

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

        ModifyEntry();
    }

    void ModifyEntry()
    {
        Microsoft.Maui.Handlers.EntryHandler.Mapper.AppendToMapping("MyCustomization", (handler, view) =>
        {
#if ANDROID
            handler.PlatformView.SetBackgroundColor(Colors.Transparent.ToPlatform());
#elif IOS
            handler.PlatformView.BorderStyle = UIKit.UITextBorderStyle.None;
#elif WINDOWS
            handler.PlatformView.FontWeight = Microsoft.UI.Text.FontWeights.Thin;
#endif
        });
    }
}

この例では、カスタマイズは Entry ページ クラスで行われます。 そのため、Android、iOS、WindowsのすべてのEntryコントロールは、インスタンスが作成されるとCustomizeEntryPageカスタマイズされます。 コンパイラの前処理ディレクティブを使用して、次のカスタマイズが実行されます。

  • Androidでは、背景色Entryは透明に設定されます。
  • iOSでは、境界線は Entry.
  • Windowsでは、フォントEntryの太さが細く設定されます。

特定のコントロール インスタンスをカスタマイズする

ハンドラーはグローバルであり、コントロールのハンドラーをカスタマイズすると、同じ種類のすべてのコントロールがアプリでカスタマイズされます。 ただし、特定のコントロール インスタンスのハンドラーは、コントロールをサブクラス化し、そのコントロールがサブクラス化された型の場合にのみ基本コントロール型のハンドラーを変更することによってカスタマイズできます。 たとえば、複数Entryのコントロールを含むページで特定Entryのコントロールをカスタマイズするには、まずコントロールをサブクラス化するEntry必要があります。

namespace CustomizeHandlersDemo;

public class MyEntry : Entry
{
}

その後、マッパーを EntryHandler使用して、インスタンスに対して必要な変更を実行するように MyEntry カスタマイズできます。

Microsoft.Maui.Handlers.EntryHandler.Mapper.AppendToMapping(nameof(IView.Background), (handler, view) =>
{
    if (view is MyEntry)
    {
#if ANDROID
        handler.PlatformView.SetBackgroundColor(Colors.Red.ToPlatform());
#elif IOS
        handler.PlatformView.BackgroundColor = Colors.Red.ToPlatform();
        handler.PlatformView.BorderStyle = UIKit.UITextBorderStyle.Line;
#elif WINDOWS
        handler.PlatformView.Background = Colors.Red.ToPlatform();
#endif
    }
});

その後、アプリ内のすべての MyEntry インスタンスは、ハンドラーの変更に従ってカスタマイズされます。

ハンドラーのライフサイクル

すべてのハンドラー ベースの .NET MAUI コントロールは、次の 2 つのハンドラー ライフサイクル イベントをサポートします。

  • HandlerChanging は、クロスプラットフォーム コントロール用に新しいハンドラーが作成されようとしているとき、および既存のハンドラーがクロスプラットフォーム コントロールから削除されようとしているときに発生します。 HandlerChangingEventArgsこのイベントに付随するオブジェクトにはNewHandler、型IElementHandlerのプロパティがありますOldHandler。 プロパティが NewHandler 存在しない null場合、イベントはクロスプラットフォーム コントロール用に新しいハンドラーが作成されようとしていることを示します。 OldHandlerプロパティが存在しないnull場合、イベントは、既存のネイティブ コントロールがクロスプラットフォーム コントロールから削除されようとしていることを示します。そのため、ネイティブ イベントはすべて未配線にし、その他のクリーンアップを実行する必要があります。
  • HandlerChanged は、クロスプラットフォーム コントロールのハンドラーが作成された後に発生します。 このイベントは、クロスプラットフォーム コントロールを実装するネイティブ コントロールが使用可能であり、クロスプラットフォーム コントロールに設定されているすべてのプロパティ値がネイティブ コントロールに適用されていることを示します。

Note

イベントは HandlerChanging 、イベントの前にクロスプラットフォーム コントロールで HandlerChanged 発生します。

これらのイベントに加えて、各クロスプラットフォーム コントロールには、イベントが発生したときにHandlerChanged呼び出されるオーバーライド可能なOnHandlerChangedメソッドとOnHandlerChanging、イベントが発生したときにHandlerChanging呼び出されるメソッドもあります。

ネイティブ コントロール イベントをサブスクライブする

ハンドラー PlatformView プロパティにアクセスして、ネイティブ コントロール プロパティの設定、ネイティブ コントロール メソッドの呼び出し、ネイティブ コントロール イベントのサブスクライブを行うことができます。 ネイティブ コントロール イベントのサブスクライブは、イベントが発生したときに HandlerChanged 発生する必要があります。これは、クロスプラットフォーム コントロールを実装するネイティブ コントロールが使用可能で初期化されていることを示します。 同様に、ネイティブ イベントからのサブスクライブ解除は、イベントが発生したときに HandlerChanging 発生する必要があります。これは、コントロールのハンドラーがクロスプラットフォーム コントロールから削除されようとしていることを示します。 ハンドラー ライフサイクル イベントの詳細については、「ハンドラーの ライフサイクル」を参照してください。

ネイティブ コントロール イベントをサブスクライブおよびサブスクライブ解除するには、カスタマイズするクロスプラットフォーム コントロールのイベント ハンドラー HandlerChangedHandlerChanging イベントを登録する必要があります。

<Entry HandlerChanged="OnHandlerChanged"
       HandlerChanging="OnHandlerChanging" />

ハンドラーは、コンパイラ プリプロセッサ ディレクティブを使用するか、部分クラスを使用してプラットフォーム固有のフォルダーとファイルにコードを整理することで、プラットフォームごとにカスタマイズできます。 各アプローチについては、オン AndroidをEntryカスタマイズして順番に説明します。

プリプロセッサ ディレクティブの使用

イベントとHandlerChangingイベントのイベント ハンドラーHandlerChangedを含む分離コード ファイルを、プリプロセッサ ディレクティブを使用する次の例に示します。

using Microsoft.Maui.Platform;

namespace CustomizeHandlersDemo;

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

    void OnHandlerChanged(object sender, EventArgs e)
    {
#if ANDROID
        ((sender as Entry).Handler.PlatformView as Android.Views.View).FocusChange += OnFocusChange;
#endif
    }

    void OnHandlerChanging(object sender, HandlerChangingEventArgs e)
    {
        if (e.OldHandler != null)
        {
#if ANDROID
            (e.OldHandler.PlatformView as Android.Views.View).FocusChange -= OnFocusChange;
#endif
        }
    }

#if ANDROID
    void OnFocusChange(object sender, EventArgs e)
    {
        var nativeView = sender as AndroidX.AppCompat.Widget.AppCompatEditText;

        if (nativeView.IsFocused)
            nativeView.SetBackgroundColor(Colors.LightPink.ToPlatform());
        else
            nativeView.SetBackgroundColor(Colors.Transparent.ToPlatform());
    }
#endif        
}

このイベントは HandlerChanged 、クロスプラットフォーム コントロールを実装するネイティブ コントロールが作成および初期化された後に発生します。 そのため、そのイベント ハンドラーでは、ネイティブ イベント サブスクリプションを実行する必要があります。 これには、ネイティブ イベントに PlatformView アクセスできるように、ハンドラーのプロパティをネイティブ コントロールの型 (基本型) にキャストする必要があります。 この例では、イベントは OnHandlerChanged ネイティブ コントロール FocusChange のイベントをサブスクライブします。

イベント ハンドラーは OnFocusChange 、コントロールのネイティブ コントロールに Entry アクセスし、コントロールがフォーカスを取得および失うにつれて背景色を設定します。

イベントは HandlerChanging 、クロスプラットフォーム コントロールから既存のハンドラーが削除される前と、クロスプラットフォーム コントロールの新しいハンドラーが作成される前に発生します。 そのため、そのイベント ハンドラーでは、ネイティブ イベント サブスクリプションを削除し、他のクリーンアップを実行する必要があります。 HandlerChangingEventArgsこのイベントに付随するオブジェクトにはOldHandler、それぞれ古いハンドラーとNewHandler新しいハンドラーに設定されるプロパティがあります。 この例では、イベントによって OnHandlerChanging ネイティブ FocusChange イベントのサブスクリプションが削除されます。

部分クラスの使用

コンパイラ プリプロセッサ ディレクティブを使用してアプリを条件付きでコンパイルするのではなく、部分クラスを使用して、コントロールのカスタマイズ コードをプラットフォーム固有のフォルダーとファイルに整理することもできます。 この方法では、カスタマイズ コードはクロスプラットフォーム部分クラスとプラットフォーム固有の部分クラスに分離されます。 次の例は、クロスプラットフォーム部分クラスを示しています。

namespace CustomizeHandlersDemo;

public partial class CustomizeEntryPage : ContentPage
{
    public CustomizePartialEntryPage()
    {
        InitializeComponent();
    }

    partial void ChangedHandler(object sender, EventArgs e);
    partial void ChangingHandler(object sender, HandlerChangingEventArgs e);

    void OnHandlerChanged(object sender, EventArgs e) => ChangedHandler(sender, e);

    void OnHandlerChanging(object sender, HandlerChangingEventArgs e) => ChangingHandler(sender, e);
}

この例では、2 つのイベント ハンドラーは、クロスプラットフォーム部分クラスで定義されている署名を持つ、という名前ChangedHandlerChangingHandlerの部分メソッドを呼び出します。 その後、部分メソッドの実装は、プロジェクトの Platforms>Android フォルダーにあるプラットフォーム固有の部分クラスで定義されます。

using Microsoft.Maui.Platform;

namespace CustomizeHandlersDemo;

public partial class CustomizeEntryPage : ContentPage
{
    partial void ChangedHandler(object sender, EventArgs e)
    {
        ((sender as Entry).Handler.PlatformView as Android.Views.View).FocusChange += OnFocusChange;
    }

    partial void ChangingHandler(object sender, HandlerChangingEventArgs e)
    {
        if (e.OldHandler != null)
        {
            (e.OldHandler.PlatformView as Android.Views.View).FocusChange -= OnFocusChange;
        }
    }

    void OnFocusChange(object sender, EventArgs e)
    {
        var nativeView = sender as AndroidX.AppCompat.Widget.AppCompatEditText;

        if (nativeView.IsFocused)
        {
            nativeView.SetBackgroundColor(Colors.LightPink.ToPlatform());
        }
        else
        {
            nativeView.SetBackgroundColor(Colors.Transparent.ToPlatform());
        }
    }
}

この方法の利点は、コンパイラの前処理ディレクティブが不要であり、部分メソッドを各プラットフォームに実装する必要ができないことです。 実装がプラットフォームで提供されていない場合、メソッドとメソッドのすべての呼び出しはコンパイル時に削除されます。 部分メソッドの詳細については、「 部分メソッド」を参照してください。

ハンドラーベースのビュー

次の表に、.NET MAUI でハンドラー ベースのビューを実装する型を示します。

表示 インターフェイス Handler Mapper
ActivityIndicator IActivityIndicator ActivityIndicatorHandler Mapper
BlazorWebView IBlazorWebView BlazorWebViewHandler BlazorWebViewMapper
Border IBorderView BorderHandler Mapper
Button IButton ButtonHandler Mapper
CarouselView CarouselViewHandler Mapper
CheckBox ICheckBox CheckBoxHandler Mapper
CollectionView CollectionViewHandler Mapper
ContentView IContentView ContentViewHandler Mapper
DatePicker IDatePicker DatePickerHandler Mapper
Editor IEditor EditorHandler Mapper
Ellipse ShapeViewHandler Mapper
Entry IEntry EntryHandler Mapper
GraphicsView IGraphicsView GraphicsViewHandler Mapper
Image IImage ImageHandler Mapper
ImageButton IImageButton ImageButtonHandler Mapper
IndicatorView IIndicatorView IndicatorViewHandler Mapper
Label ILabel LabelHandler Mapper
Line LineHandler Mapper
Path PathHandler Mapper
Picker IPicker PickerHandler Mapper
Polygon PolygonHandler Mapper
Polyline PolylineHandler Mapper
ProgressBar IProgress ProgressBarHandler Mapper
RadioButton IRadioButton RadioButtonHandler Mapper
Rectangle RectangleHandler Mapper
RefreshView IRefreshView RefreshViewHandler Mapper
RoundRectangle RoundRectangleHandler Mapper
ScrollView IScrollView ScrollViewHandler Mapper
SearchBar ISearchBar SearchBarHandler Mapper
Slider ISlider SliderHandler Mapper
Stepper IStepper StepperHandler Mapper
SwipeView ISwipeView SwipeViewHandler Mapper
Switch ISwitch SwitchHandler Mapper
TimePicker ITimePicker TimePickerHandler Mapper
WebView IWebView WebViewHandler Mapper

すべてのハンドラーは名前空間内にあります Microsoft.Maui.Handlers が、次の例外があります。

  • CarouselViewHandler 名前空間 CollectionViewHandler 内にあります Microsoft.Maui.Controls.Handlers.Items
  • LineHandlerPathHandlerPolygonHandlerPolylineHandlerRectangleHandlerおよび RoundRectangleHandler 名前空間内 Microsoft.Maui.Controls.Handlers にあります。

上記の表のインターフェイスは名前空間にあります Microsoft.Maui

レンダラーベースのビュー

次の従来の Xamarin.Forms ビューは、ハンドラーではなくレンダラーによってサポートされ、別のカスタマイズアプローチを使用します。

  • BoxView
  • Frame
  • ListView
  • TableView