maui-accessibility
Guide for making .NET MAUI apps accessible — screen reader support via SemanticProperties, heading levels, AutomationProperties…
Guide for creating custom .NET MAUI handlers, customizing existing handlers with property mappers, and implementing platform-specific native views. Covers PrependToMapping/ModifyMapping/AppendToMapping, PropertyMapper, CommandMapper, partial handler classes, and handler
$ npx -y skills add davidortinau/maui-skills --skill maui-custom-handlers --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
/maui-custom-handlersContext preview
The summary Claude sees to decide when to auto-load this skill.
Guide for creating custom .NET MAUI handlers, customizing existing handlers with property mappers, and implementing platform-specific native views. Covers PrependToMapping/ModifyMapping/AppendToMapping, PropertyMapper, CommandMapper, partial handler classes, and handler
name: maui-custom-handlers description: > Guide for creating custom .NET MAUI handlers, customizing existing handlers with property mappers, and implementing platform-specific native views. Covers PrependToMapping/ModifyMapping/AppendToMapping, PropertyMapper, CommandMapper, partial handler classes, and handler registration. USE FOR: "custom handler", "PropertyMapper", "AppendToMapping", "PrependToMapping", "ModifyMapping", "CommandMapper", "platform-specific rendering", "native view", "handler registration", "custom control renderer". DO NOT USE FOR: platform API calls without custom controls (use maui-platform-invoke), data binding (use maui-data-binding), or gesture handling (use maui-gestures).
| Scenario | Approach | |---|---| | Change how a built-in control looks/behaves on one platform | **Customize** — use `AppendToMapping` / `PrependToMapping` | | Need the change on only some instances of a control | **Customize** — subclass the control + type-check in mapper | | Need a completely new cross-platform control with native backing | **Create 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.
---
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
});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;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.
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
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.
---
| Method | Risk | Use when | |---|---|---| | `AppendToMapping` | Low — runs after default | Adding behaviour without breaking defaults | | `PrependToMapping` | Low — runs before default | Setting initial state that the default can override | | `ModifyMapping` | ⚠️ High — replaces default | You intentionally want to suppress the framework's mapper logic |
---
| Mapper | Purpose | Pattern | |---|---|---| | `PropertyMapper` | Sync a bindable property to the native view | Runs whenever the property value changes | | `CommandMapper` | Fire-and-forget action from control → handler | Runs 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.
---
A collection of 41 skills for .NET MAUI development, UI/UX design reasoning, and Xamarin migration, designed for use with GitHub Copilot CLI and Claude Code.
Guide for making .NET MAUI apps accessible — screen reader support via SemanticProperties, heading levels, AutomationProperties…
.NET MAUI view animations, custom animations, easing functions, rotation, scale, translation, and fade effects. USE FOR: "animate…
.NET MAUI app icon configuration, splash screen setup, SVG to PNG conversion at build time, composed/adaptive icons, and…
.NET MAUI app lifecycle guidance covering the four app states (not running, running, deactivated, stopped), cross-platform Window…
Guide for .NET MAUI apps consuming .NET Aspire-hosted backend services. Covers AppHost configuration, service discovery for…
Add authentication to .NET MAUI apps. Covers WebAuthenticator for generic OAuth 2.0 / social login, and MSAL.NET for Microsoft…