dotnet / dotnet/maui

[Proposal][net11.0][XSG] Introduce trimming and AOT-safe EventTrigger<T> for XAML Source Generator

Open
#33,591 0 comments 0 reactions 0 assignees View on GitHub
perf/app-size xsg
Dominant language
C#
Stars
23.3k
Forks
2k
Avg merge
1d 15h
Merged PRs (30d)
290

Description

## Summary

Introduce two generic event trigger classes that use compile-time delegates instead of runtime reflection. This enables the XAML Source Generator to produce 100% trimming-safe and AOT-compatible code with zero reflection.

## Motivation

The current `EventTrigger` is incompatible with trimming and AOT:

```csharp
// Current implementation uses reflection
_eventinfo = bindable.GetType().GetRuntimeEvent(Event);
_handlerdelegate = ((EventHandler)OnEventTriggered).Method.CreateDelegate(_eventinfo.EventHandlerType, this);
```

**Problems:**
- ❌ **Trimming unsafe** - event name is a string, trimmer can't trace it
- ❌ **AOT incompatible** - `CreateDelegate()` requires runtime code generation
- ❌ **No compile-time validation** - typos fail at runtime

## Proposed Solution

Two generic classes that accept add/remove delegates - completely reflection-free:

```csharp
// For events using EventHandler (Entry.Focused, Entry.TextChanged, etc.)
new EventTrigger(
static (e, h) => e.Focused += h,
static (e, h) => e.Focused -= h);

// For events using plain EventHandler (Button.Clicked, etc.)
new EventTrigger(
static (b, h) => b.Clicked += h,
static (b, h) => b.Clicked -= h);
```

**Prototype:** https://gist.github.com/simonrozsival/5b27ff816b2a24f1dc5466bf55a2a57a

## Why Two Classes?

`EventHandler` and `EventHandler` are incompatible delegate types in .NET - they cannot be unified in a single generic class without reflection. By having two classes, each can use direct method assignment:

```csharp
_handler = OnEventTriggered; // ✅ Direct assignment, no reflection!
```

## Comparison

| Aspect | `EventTrigger` (current) | `EventTrigger` (proposed) |
|--------|--------------------------|------------------------------|
| **Trimming** | ❌ Unsafe | ✅ Safe |
| **AOT** | ❌ Requires reflection | ✅ 100% reflection-free |
| **Compile-time validation** | ❌ Runtime errors | ✅ Build errors |
| **Handler creation** | `Delegate.CreateDelegate()` | Direct method assignment |

## Implementation Plan

1. Add `EventTrigger` and `EventTrigger` classes
2. Update XSG to emit appropriate class based on event handler type
3. Keep existing `EventTrigger` with `[RequiresUnreferencedCode]` for compatibility

## Open Questions

1. Should we add a feature switch to control XSG behavior?
2. Public API or internal (XSG-only)?
3. Naming preference?

Contributor guide

Open the contributing guide

Research direction

Start by reviewing the linked prototype and the current EventTrigger implementation, then trace how the XAML Source Generator emits event triggers. Done means the two generic trigger variants are reflection-free and trimming/AOT-safe, the generator selects the appropriate variant, and the existing compatibility path remains annotated as planned.

Written by the indexing model from the issue text.

Assessment

Tech stack
csharp
Domain
tooling
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
30/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.