# Maui Custom Handlers

> >

- **Type:** Skill
- **Install:** `agentstack add skill-davidortinau-maui-skills-maui-custom-handlers`
- **Verified:** Yes — security-reviewed for prompt injection and unsafe behavior
- **Seller:** [davidortinau](https://agentstack.voostack.com/s/davidortinau)
- **Installs:** 0
- **Category:** [Agent Skills](https://agentstack.voostack.com/c/agent-skills)
- **Latest version:** 0.1.0
- **License:** MIT
- **Upstream author:** [davidortinau](https://github.com/davidortinau)
- **Source:** https://github.com/davidortinau/maui-skills/tree/main/plugins/maui-skills/skills/maui-custom-handlers

## Install

```sh
agentstack add skill-davidortinau-maui-skills-maui-custom-handlers
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## About

# .NET MAUI Custom Handlers

## Decision: Customize Existing vs. Create New

| 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.

---

## Gotchas & Common Mistakes

### Mapper customizations are global

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

```csharp
// ❌ 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.

```csharp
// ❌ Subscribes but never unsubscribes — leaks
entry.HandlerChanged += (s, e) =>
{
#if ANDROID
    ((Entry)s!).Handler!.PlatformView.As()!
        .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`
base-class generic resolves correctly per platform.

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

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

---

## PropertyMapper vs. CommandMapper

| 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.

---

## Checklist — New Handler

- [ ] Cross-platform control inherits `View` (or appropriate base)
- [ ] Shared handler file has conditional `using PlatformView = ...` aliases
- [ ] Handler inherits `ViewHandler`
- [ ] `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`

## Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [davidortinau](https://github.com/davidortinau)
- **Source:** [davidortinau/maui-skills](https://github.com/davidortinau/maui-skills)
- **License:** MIT

Install and usage instructions live in the source repository linked above.

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** no
- **Filesystem access:** no
- **Shell / process execution:** no
- **Environment & secrets:** no
- **Dynamic code execution:** no

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: passed — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/skill-davidortinau-maui-skills-maui-custom-handlers
- Seller: https://agentstack.voostack.com/s/davidortinau
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
