次の方法で共有


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

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

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

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

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

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

Von Bedeutung

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

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

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

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

.NET MAUI Entry ビューは、 IEntry インターフェイスを実装する単一行テキスト入力コントロールです。 EntryHandlerは、Entry ビューをプラットフォームごとに次のネイティブ ビューにマップします。

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

次の図は、 Entry ビューが EntryHandlerを介してネイティブ ビューにどのようにマップされるかを示しています。

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

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

Entry プロパティ マッパーは、EntryHandler クラスで、クロスプラットフォーム コントロール プロパティをネイティブ ビュー 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 のカスタマイズはページ クラスで行われます。 そのため、Android、iOS、および Windows 上のすべての Entry コントロールは、 CustomizeEntryPage のインスタンスが作成されるとカスタマイズされます。 カスタマイズは、各プラットフォームのクロスプラットフォーム コントロールにマップされるネイティブ ビューへのアクセスを提供するプロパティ 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 コントロールは、 HandlerChanging イベントと HandlerChanged イベントをサポートします。 HandlerChanged イベントは、クロスプラットフォーム コントロールを実装するネイティブ ビューが使用可能で初期化されると発生します。 HandlerChanging イベントは、コントロールのハンドラーがクロスプラットフォーム コントロールから削除されるときに発生します。 ハンドラーのライフサイクル イベントの詳細については、「 ハンドラーのライフサイクル」を参照してください。

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

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

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

条件付きコンパイル

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

#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
}
#if ANDROID
using Microsoft.Maui.Platform;
#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 MauiAppCompatEditText).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 では、 OnEntryHandlerChanged イベントは、 Entry を実装するネイティブ ビューがフォーカスを取得したときに発生するネイティブ ビュー イベントをサブスクライブします。

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

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

部分クラス

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

  • クロスプラットフォーム部分クラスは通常、メンバーを定義しますが、実装せず、すべてのプラットフォーム向けに構築されます。 このクラスは、プラットフォーム固有のクラスになるため、プロジェクトのどの Platforms 子フォルダーにも配置しないでください。
  • プラットフォーム固有の部分クラスは、通常、クロスプラットフォーム部分クラスで定義されたメンバーを実装し、1 つのプラットフォーム用に構築されます。 このクラスは、選択したプラットフォームの Platforms フォルダーの子フォルダーに配置する必要があります。

次の例は、クロスプラットフォーム部分クラスを示しています。

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);
}

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

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();
        }
    }
}

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

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