Now liveThe Skillselion MCP - thousands of ranked skills, loaded into your agent mid-task. No install.Get it →
davidortinau avatar

Maui Custom Handlers

  • 30 installs
  • 163 repo stars
  • Updated July 6, 2026
  • davidortinau/maui-skills

Creates custom .NET MAUI handlers, customizes existing handlers with property mappers, and implements platform-specific native views.

About

Guides creating custom .NET MAUI handlers, customizing existing handlers via property mappers, and implementing platform-specific native views. A developer uses it when they need native-level control over how MAUI controls render on each platform.

  • Custom handlers and property mapper customization
  • Platform-specific native view implementation

Maui Custom Handlers by the numbers

  • 30 all-time installs (skills.sh)
  • Ranked #665 of 1,039 Mobile Development skills by installs in the Skillselion catalog
  • Data as of Aug 3, 2026 (Skillselion catalog sync)
npx skills add https://github.com/davidortinau/maui-skills --skill maui-custom-handlers

Add your badge

Show developers this skill is listed on Skillselion. Paste this into your README.

Listed on Skillselion
Installs30
repo stars163
Last updatedJuly 6, 2026
Repositorydavidortinau/maui-skills

What it does

Creates custom .NET MAUI handlers, customizes existing handlers with property mappers, and implements platform-specific native views.

Files

SKILL.mdMarkdownGitHub ↗

.NET MAUI Custom Handlers

Decision: Customize Existing vs. Create New

ScenarioApproach
Change how a built-in control looks/behaves on one platformCustomize — use AppendToMapping / PrependToMapping
Need the change on only some instances of a controlCustomize — subclass the control + type-check in mapper
Need a completely new cross-platform control with native backingCreate new handler with partial classes
⚠️ Prefer `AppendToMapping` over ModifyMapping. ModifyMapping replaces
the default mapper action entirely — if the framework adds behaviour in a
future release, your override silently drops it.

---

Gotchas & Common Mistakes

Mapper customizations are global

Every instance of the control is affected. Guard with a subclass check for instance-specific behaviour:

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

Unsubscribe native events in HandlerChanging

Failing to remove native event handlers causes memory leaks because the native view may outlive the managed wrapper.

// ❌ 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;

Partial class name/namespace mismatch

Namespace and class name must match exactly across the shared handler file and every platform file. A mismatch silently creates separate classes — no compiler error, just a handler that does nothing on that platform.

Conditional using placement

The using PlatformView = ... aliases must be at the top of the shared handler file (not the platform files) so the ViewHandler<TControl, TPlatformView> base-class generic resolves correctly per platform.

// ✅ 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

Missing CreatePlatformView()

Each platform partial must override CreatePlatformView(). Omitting it produces a compile error — but the error message points at the base class, not your handler, making it confusing to debug.

---

Mapper Method Selection

MethodRiskUse when
AppendToMappingLow — runs after defaultAdding behaviour without breaking defaults
PrependToMappingLow — runs before defaultSetting initial state that the default can override
ModifyMapping⚠️ High — replaces defaultYou intentionally want to suppress the framework's mapper logic

---

PropertyMapper vs. CommandMapper

MapperPurposePattern
PropertyMapperSync a bindable property to the native viewRuns whenever the property value changes
CommandMapperFire-and-forget action from control → handlerRuns once per invocation, no return value
⚠️ Don't put property sync logic in CommandMapper — it won't re-run when
the property changes, leading to stale native views.

---

Checklist — New Handler

  • [ ] Cross-platform control inherits View (or appropriate base)
  • [ ] Shared handler file has conditional using PlatformView = ... aliases
  • [ ] Handler inherits ViewHandler<TControl, PlatformView>
  • [ ] PropertyMapper maps every bindable property
  • [ ] Each platform partial overrides CreatePlatformView()
  • [ ] Namespace + class name identical across all partial files
  • [ ] Handler registered in MauiProgram.cs via ConfigureMauiHandlers
  • [ ] Native event subscriptions cleaned up in HandlerChanging

Related skills

This week in AI coding

Five minutes, every Monday - the tools, releases and tactics for developers.

unsubscribe anytime.