ハンドラーを使用してコントロールをカスタマイズする
ハンドラーをカスタマイズして、コントロールの 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 行テキスト入力コントロールです。 は EntryHandler
、 Entry プラットフォームごとに次のネイティブ ビューにビューをマップします。
- 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 子フォルダーに配置する必要があります。 たとえば、次のコードは、プロジェクトの CustomizeEntryPartialMethodsPage
Platforms>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 フォルダーのサブフォルダーに配置する必要がないようにマルチターゲットを構成する方法については、「 マルチターゲットの構成」を参照してください。