mcpField
M
maui-custom-handlers

maui-custom-handlers

github.com/davidortinau開発17648 回インストール16 週間前に更新
リポジトリ

maui-custom-handlers について

maui-custom-handlersは、.NET MAUIのハンドラーをカスタマイズ、作成する開発者向けのリファレンススキルです。マッパーの選択やプラットフォーム別の構成に加え、全体への適用やネイティブイベントのリークなどの注意点を説明します。

主な機能

  1. 01既存ハンドラーの変更と新規作成を選択
  2. 02AppendToMapping、PrependToMapping、ModifyMappingを比較
  3. 03PropertyMapperとCommandMapperの違いを解説
  4. 04プラットフォーム別partialでハンドラーを構成
  5. 05注意点と実装チェックリストを提示

活用シーン

  1. 01特定プラットフォームで組み込みコントロールを調整
  2. 02選択したコントロールだけにハンドラー動作を適用
  3. 03ネイティブ実装を持つクロスプラットフォームコントロールを作成
  4. 04ハンドラー登録やプラットフォーム別partialの問題を調査

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/davidortinau/maui-skills --skill maui-custom-handlers

インストール後、Claude Code で/maui-custom-handlersを入力して読み込まれたか確認します。

同梱ファイルreferences/custom-handlers-api.md

詳細

.NET MAUI カスタムハンドラー

判断:既存のものをカスタマイズするか、新しく作成するか

シナリオ方法
あるプラットフォームで、組み込みコントロールの外観や動作を変更するカスタマイズ — AppendToMapping / PrependToMapping を使用します
コントロールの一部のインスタンスだけに変更を適用するカスタマイズ — コントロールをサブクラス化し、マッパーで型をチェックします
ネイティブ側の実装を持つ、まったく新しいクロスプラットフォームコントロールが必要新しいハンドラーを部分クラスで作成します

⚠️ ModifyMapping よりも AppendToMapping を優先してください。ModifyMapping はデフォルトのマッパーアクションを完全に置き換えます。将来のリリースでフレームワークの動作が追加されても、オーバーライドによってそれが暗黙のうちに失われます。


注意点とよくある間違い

マッパーのカスタマイズはグローバルに適用されます

コントロールのすべてのインスタンスに影響します。インスタンス固有の動作にするには、サブクラスのチェックを使用します。

// ❌ Removes borders from EVERY Entry in the app
EntryHandler.Mapper.AppendToMapping("NoBorder", (handler, view) =>
{
#if ANDROID
    handler.PlatformView.Background = null;
#endif
});

// ✅ Only affects BorderlessEntry instances
EntryHandler.Mapper.AppendToMapping("NoBorder", (handler, view) =>
{
    if (view is not BorderlessEntry) return;
#if ANDROID
    handler.PlatformView.Background = null;
#endif
});
HandlerChanging でネイティブイベントの購読を解除します

ネイティブイベントハンドラーを削除しないと、ネイティブビューがマネージドラッパーより長く存続する可能性があるため、メモリリークが発生します。

// ❌ Subscribes but never unsubscribes — leaks
entry.HandlerChanged += (s, e) =>
{
#if ANDROID
    ((Entry)s!).Handler!.PlatformView.As<Android.Widget.EditText>()!
        .FocusChange += OnNativeFocusChange;
#endif
};

// ✅ Pair subscribe in HandlerChanged with unsubscribe in HandlerChanging
entry.HandlerChanged += OnHandlerChanged;
entry.HandlerChanging += OnHandlerChanging;
部分クラスの名前/名前空間の不一致

共有ハンドラーファイルと各プラットフォームファイルで、名前空間とクラス名を完全に一致させる必要があります。不一致があると、別々のクラスが暗黙のうちに作成されます。コンパイルエラーは発生せず、そのプラットフォームではハンドラーが何もしません。

条件付き using の配置

using PlatformView = ... エイリアスは、共有ハンドラーファイルの先頭(プラットフォームファイルではありません)に配置してください。これにより、ViewHandler<TControl, TPlatformView> 基底クラスのジェネリックが、プラットフォームごとに正しく解決されます。

// ✅ Top of Handlers/VideoPlayerHandler.cs
#if ANDROID
using PlatformView = Android.Widget.VideoView;
#elif IOS || MACCATALYST
using PlatformView = AVKit.AVPlayerViewController;
#elif WINDOWS
using PlatformView = Microsoft.UI.Xaml.Controls.MediaPlayerElement;
#endif
CreatePlatformView() の不足

各プラットフォームの部分クラスは、必ず CreatePlatformView() をオーバーライドする必要があります。省略するとコンパイルエラーになりますが、エラーメッセージはハンドラーではなく基底クラスを指すため、デバッグが難しくなります。


マッパーメソッドの選択

メソッドリスク使用する場面
AppendToMapping低 — デフォルトの後に実行デフォルトを損なわずに動作を追加する場合
PrependToMapping低 — デフォルトの前に実行デフォルトで上書きされる可能性のある初期状態を設定する場合
ModifyMapping⚠️ 高 — デフォルトを置き換えるフレームワークのマッパーロジックを意図的に抑制する場合

PropertyMapper と CommandMapper

マッパー目的動作パターン
PropertyMapperバインド可能なプロパティをネイティブビューに同期するプロパティ値が変更されるたびに実行されます
CommandMapperコントロールからハンドラーへの、結果を返さないアクションを実行する呼び出しごとに一度実行され、戻り値はありません

⚠️ CommandMapper にプロパティ同期ロジックを入れないでください。プロパティが変更されても再実行されないため、ネイティブビューの状態が古いままになります。


チェックリスト — 新しいハンドラー

  • クロスプラットフォームコントロールが View(または適切な基底クラス)を継承している
  • 共有ハンドラーファイルに条件付き using PlatformView = ... エイリアスがある
  • ハンドラーが ViewHandler<TControl, PlatformView> を継承している
  • PropertyMapper がすべてのバインド可能なプロパティをマッピングしている
  • 各プラットフォームの部分クラスが CreatePlatformView() をオーバーライドしている
  • すべての部分クラスで名前空間とクラス名が一致している
  • MauiProgram.cs の ConfigureMauiHandlers でハンドラーが登録されている
  • HandlerChanging でネイティブイベントの購読が解除されている

コメント