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

サンプルを参照します。 サンプルを参照する

ハンドラーをカスタマイズして、コントロールの 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 Entry ビューは、インターフェイスを実装 IEntry する 1 行テキスト入力コントロールです。 は EntryHandlerEntry プラットフォームごとに次のネイティブ ビューにビューをマップします。

  • iOS/Mac Catalyst: UITextField
  • Android: AppCompatEditText
  • Windows: TextBox

次の図は、 を Entry 使用してビューをネイティブ ビューにマップする方法を EntryHandler示しています。

エントリ ハンドラーのアーキテクチャ。

クラスのEntryHandlerプロパティ マッパーはEntry、クロスプラットフォーム コントロールのプロパティをネイティブ ビュー API にマップします。 これにより、 で Entryプロパティが設定されると、基になるネイティブ ビューが必要に応じて更新されます。

プロパティ マッパーは、各プラットフォームでカスタマイズ Entry するように変更できます。

namespace CustomizeHandlersDemo.Views;

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

    void ModifyEntry()
    {
        Microsoft.Maui.Handlers.EntryHandler.Mapper.AppendToMapping("MyCustomization", (handler, view) =>
        {
#if ANDROID
            handler.PlatformView.SetSelectAllOnFocus(true);
#elif IOS || MACCATALYST
            handler.PlatformView.EditingDidBegin += (s, e) =>
            {
                handler.PlatformView.PerformSelector(new ObjCRuntime.Selector("selectAll"), null, 0.0f);
            };
#elif WINDOWS
            handler.PlatformView.GotFocus += (s, e) =>
            {
                handler.PlatformView.SelectAll();
            };
#endif
        });
    }
}

この例では、カスタマイズは Entry ページ クラスで行われます。 したがって、 のインスタンスCustomizeEntryPageが作成されると、Android、iOS、および Windows 上のすべてのEntryコントロールがカスタマイズされます。 カスタマイズは、各プラットフォームのクロスプラットフォーム コントロールにマップされるネイティブ ビューへのアクセスを提供する handlers PlatformView プロパティにアクセスすることによって実行されます。 その後、ネイティブ コードは、フォーカスが取得されたときに 内のすべてのテキストを選択してハンドラーを Entry カスタマイズします。

マッパーの詳細については、「 ッパー」を参照してください。

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

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

namespace CustomizeHandlersDemo.Controls
{
    internal class MyEntry : Entry
    {
    }
}

その後、プロパティ マッパーを使用して を EntryHandlerカスタマイズして、インスタンスにのみ必要な変更を MyEntry 実行できます。

Microsoft.Maui.Handlers.EntryHandler.Mapper.AppendToMapping("MyCustomization", (handler, view) =>
{
    if (view is MyEntry)
    {
#if ANDROID
        handler.PlatformView.SetSelectAllOnFocus(true);
#elif IOS || MACCATALYST
        handler.PlatformView.EditingDidBegin += (s, e) =>
        {
            handler.PlatformView.PerformSelector(new ObjCRuntime.Selector("selectAll"), null, 0.0f);
        };
#elif WINDOWS
        handler.PlatformView.GotFocus += (s, e) =>
        {
            handler.PlatformView.SelectAll();
        };
#endif
    }
});

ハンドラーのカスタマイズがクラスで App 実行される場合、アプリ内のすべての MyEntry インスタンスは、ハンドラーの変更に従ってカスタマイズされます。

ハンドラーのライフサイクルを使用してコントロールをカスタマイズする

すべてのハンドラーベースの .NET MAUI コントロールでは、サポートとHandlerChangedイベントがサポートHandlerChangingされます。 クロス HandlerChanged プラットフォーム コントロールを実装するネイティブ ビューが使用可能で初期化されると、イベントが発生します。 イベントは HandlerChanging 、コントロールのハンドラーがクロスプラットフォーム コントロールから削除されるときに発生します。 ハンドラー ライフサイクル イベントの詳細については、「ハンドラーの ライフサイクル」を参照してください。

ハンドラーのライフサイクルを使用して、ハンドラーのカスタマイズを実行できます。 たとえば、ネイティブ ビュー イベントをサブスクライブしてサブスクライブ解除するには、カスタマイズするクロスプラットフォーム コントロールの イベントと HandlerChanging イベントのイベント ハンドラーHandlerChangedを登録する必要があります。

<Entry HandlerChanged="OnEntryHandlerChanged"
       HandlerChanging="OnEntryHandlerChanging" />

ハンドラーは、条件付きコンパイルを使用するか、部分クラスを使用してプラットフォーム固有のフォルダーとファイルにコードを整理することで、プラットフォームごとにカスタマイズできます。 各アプローチについて順番に説明します。次に、 を Entry カスタマイズして、フォーカスを取得したときにすべてのテキストが選択されるようにします。

条件付きコンパイル

イベントと HandlerChanging イベントのイベント ハンドラーHandlerChangedを含む分離コード ファイルを、条件付きコンパイルを使用する次の例に示します。

#if ANDROID
using AndroidX.AppCompat.Widget;
#elif IOS || MACCATALYST
using UIKit;
#elif WINDOWS
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml;
#endif

namespace CustomizeHandlersDemo.Views;

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

    void OnEntryHandlerChanged(object sender, EventArgs e)
    {
        Entry entry = sender as Entry;
#if ANDROID
        (entry.Handler.PlatformView as AppCompatEditText).SetSelectAllOnFocus(true);
#elif IOS || MACCATALYST
        (entry.Handler.PlatformView as UITextField).EditingDidBegin += OnEditingDidBegin;
#elif WINDOWS
        (entry.Handler.PlatformView as TextBox).GotFocus += OnGotFocus;
#endif
    }

    void OnEntryHandlerChanging(object sender, HandlerChangingEventArgs e)
    {
        if (e.OldHandler != null)
        {
#if IOS || MACCATALYST
            (e.OldHandler.PlatformView as UITextField).EditingDidBegin -= OnEditingDidBegin;
#elif WINDOWS
            (e.OldHandler.PlatformView as TextBox).GotFocus -= OnGotFocus;
#endif
        }
    }

#if IOS || MACCATALYST                   
    void OnEditingDidBegin(object sender, EventArgs e)
    {
        var nativeView = sender as UITextField;
        nativeView.PerformSelector(new ObjCRuntime.Selector("selectAll"), null, 0.0f);
    }
#elif WINDOWS
    void OnGotFocus(object sender, RoutedEventArgs e)
    {
        var nativeView = sender as TextBox;
        nativeView.SelectAll();
    }
#endif
}

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

イベント ハンドラーと OnGotFocus イベント ハンドラーはOnEditingDidBegin、それぞれのプラットフォームの のEntryネイティブ ビューにアクセスし、 内のすべてのテキストを選択しますEntry

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

部分クラス

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

namespace CustomizeHandlersDemo.Views;

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

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

    void OnEntryHandlerChanged(object sender, EventArgs e) => ChangedHandler(sender, e);
    void OnEntryHandlerChanging(object sender, HandlerChangingEventArgs e) => ChangingHandler(sender, e);
}

重要

クロスプラットフォーム部分クラスは、プロジェクトのどの Platforms 子フォルダーにも配置しないでください。

この例では、2 つのイベント ハンドラーは、 と ChangingHandlerという名前ChangedHandlerの部分メソッドを呼び出します。このメソッドのシグネチャはクロスプラットフォーム部分クラスで定義されています。 その後、部分メソッドの実装はプラットフォーム固有の部分クラスで定義されます。これは、ビルド システムが特定のプラットフォーム用にビルドするときにネイティブ コードのビルドのみを試行するように、適切な Platforms 子フォルダーに配置する必要があります。 たとえば、次のコードは、プロジェクトの CustomizeEntryPartialMethodsPagePlatforms>Windows フォルダー内の クラスを示しています。

using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;

namespace CustomizeHandlersDemo.Views
{
    public partial class CustomizeEntryPartialMethodsPage : ContentPage
    {
        partial void ChangedHandler(object sender, EventArgs e)
        {
            Entry entry = sender as Entry;
            (entry.Handler.PlatformView as TextBox).GotFocus += OnGotFocus;
        }

        partial void ChangingHandler(object sender, HandlerChangingEventArgs e)
        {
            if (e.OldHandler != null)
            {
                (e.OldHandler.PlatformView as TextBox).GotFocus -= OnGotFocus;
            }
        }

        void OnGotFocus(object sender, RoutedEventArgs e)
        {
            var nativeView = sender as TextBox;
            nativeView.SelectAll();
        }
    }
}

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

.NET MAUI プロジェクトの Platforms フォルダーの編成については、「 部分クラスとメソッド」を参照してください。 プラットフォーム コードを Platforms フォルダーのサブフォルダーに配置する必要がないようにマルチターゲットを構成する方法については、「 マルチターゲットの構成」を参照してください。