Skip to content

[net11.0] Trimmable element handlers#29952

Open
simonrozsival wants to merge 7 commits intonet11.0from
dev/simonrozsival/net10.0-use-element-handler-attributes
Open

[net11.0] Trimmable element handlers#29952
simonrozsival wants to merge 7 commits intonet11.0from
dev/simonrozsival/net10.0-use-element-handler-attributes

Conversation

@simonrozsival
Copy link
Copy Markdown
Member

@simonrozsival simonrozsival commented Jun 12, 2025

This is a follow-up to #28357

Summary

All built-in controls now use [ElementHandler] attributes instead of DI registration for handler resolution. This removes the need for AppHostBuilderExtensions.AddControlsHandlers() and makes handler associations visible at the type level, improving trimmability.

Key changes

[ElementHandler] attribute

  • New public ElementHandlerAttribute on view types (e.g., [ElementHandler(typeof(ButtonHandler))]) declaratively associates a view with its handler
  • Inherited = false — each concrete type that needs a different handler must declare its own attribute
  • The attribute walks the BaseType chain at resolution time, so derived types without their own attribute inherit the nearest ancestor's handler

Handler resolution in MauiHandlersFactory

Both GetHandler(Type) and GetHandlerType(Type) follow the same 4-step resolution order:

  1. Exact DI registration — if the concrete type (e.g., typeof(Button)) is registered via AddHandler<Button, MyButtonHandler>(), that wins. DI-registered handlers are instantiated through MauiFactory.GetService().
  2. [ElementHandler] attribute — walk the type's BaseType chain looking for the attribute. Attribute-based handlers are instantiated via Activator.CreateInstance().
  3. Interface-based DI registration — map the concrete type to its virtual view interface (e.g., ButtonIButton) using RegisteredHandlerServiceTypeSet, then look up the interface in DI. This is the path for third-party/custom handler overrides registered via AddHandler<IButton, ...>().
  4. IContentView fallback — if the type implements IContentView, return ContentViewHandler.

GetHandler() is the primary API — it returns an instantiated IElementHandler. GetHandlerType() returns only the Type and is used by code that needs to compare handler types without instantiating them (e.g., Compatibility layer cell renderers).

ElementExtensions.ToHandler() / SetHandler()

These methods call GetHandler() directly. When a handler requires constructor parameters (no parameterless constructor), ToHandler() catches the MissingMethodException and falls back to ActivatorUtilities.CreateInstance() with DI injection. This fallback path is cached per-type in a HashSet<Type> to avoid repeated exception throwing.

Mapper remapping via static constructors

  • Controls-layer types override Core-layer mapper entries in their static constructors
  • Base-class static constructor ordering is enforced via RemappingHelper.EnsureBaseTypeRemapped(), which accesses a s_forceStaticConstructor field on the parent type to force the runtime to run the parent's static constructor first
  • RemappingDebugHelper.AssertBaseClassForRemapping (DEBUG-only) validates that no intermediate type in the hierarchy is accidentally skipped

Cleanup

  • Removed IElementHandlerWithAndroidContext interface and ElementHandlerWithAndroidContextAttribute
  • Removed dead RemapForControls(this MauiAppBuilder) extension method and old RemapForControls()/RemapIfNeeded() pattern
  • Fixed Slider not having a static constructor (was only called explicitly from the removed extension method)
  • Restored IMauiHandlersFactory.GetHandler() to its original signature (GetHandler(Type) / GetHandler<T>()) — no longer requires IMauiContext parameter
  • Removed unused MauiContext/HandlersContextStub declarations from tests and benchmarks

Copilot AI review requested due to automatic review settings June 12, 2025 07:43
@simonrozsival simonrozsival requested a review from a team as a code owner June 12, 2025 07:43
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR transitions built-in controls from DI registration to using [ElementHandler] attributes, introduces IElementHandlerWithAndroidContext<T> for Android renderers, and adds a new MSBuild property to toggle CSS support.

  • Apply [ElementHandler] to core controls and cells instead of DI
  • Introduce IElementHandlerWithAndroidContext<T> and CreateHandler(Context) on Android renderers
  • Add MauiCssEnabled MSBuild property and corresponding runtime host configuration

Reviewed Changes

Copilot reviewed 120 out of 120 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
src/Controls/src/Core/ContentPage/ContentPage.Mapper.cs Added static ctor calling RemapForControls and VisualElement.RemapIfNeeded
src/Controls/src/Core/Compatibility/Handlers/TableView/Android/TableViewRenderer.cs Implemented IElementHandlerWithAndroidContext<TableViewRenderer> and CreateHandler
src/Controls/src/Core/Compatibility/Handlers/ListView/Android/ListViewRenderer.cs Implemented IElementHandlerWithAndroidContext<ListViewRenderer> and CreateHandler
src/Controls/src/Core/Compatibility/Handlers/Android/FrameRenderer.cs Implemented IElementHandlerWithAndroidContext<FrameRenderer> and CreateHandler
src/Controls/src/Core/Cells/*.cs Added [ElementHandler<...>] attributes for compatibility cell renderers
src/Controls/src/Core/Button/Button.cs Added [ElementHandler<ButtonHandler>]
src/Controls/src/Core//.Mapper.cs Added static ctors calling RemapForControls / RemapIfNeeded
src/Controls/src/Build.Tasks/nuget/.../Microsoft.Maui.Controls.targets Added MauiCssEnabled property and runtime host configuration option
src/Compatibility/Core/src/{iOS,Android}/Platform.cs Updated GetHandler calls to pass MauiContext
Comments suppressed due to low confidence (1)

src/Controls/src/Build.Tasks/nuget/buildTransitive/netstandard2.0/Microsoft.Maui.Controls.targets:240

  • Added the MauiCssEnabled build property but no corresponding public documentation under /docs/; please update the XML docs to describe this new feature.
<MauiCssEnabled Condition="'$(MauiCssEnabled)' == '' and '@(MauiCss->Count())' == '0'">false</MauiCssEnabled>

@simonrozsival simonrozsival marked this pull request as draft June 12, 2025 08:06
@mattleibow
Copy link
Copy Markdown
Member

/azp run

@azure-pipelines
Copy link
Copy Markdown

Azure Pipelines will not run the associated pipelines, because the pull request was updated after the run command was issued. Review the pull request again and issue a new run command.

@PureWeen PureWeen added this to the .NET 10.0-preview6 milestone Jun 17, 2025
@PureWeen PureWeen moved this from Todo to In Progress in MAUI SDK Ongoing Jun 17, 2025
@simonrozsival simonrozsival force-pushed the dev/simonrozsival/net10.0-use-element-handler-attributes branch from 33afe18 to c49e57e Compare June 18, 2025 11:53
@PureWeen PureWeen added platform/macos macOS / Mac Catalyst platform/windows platform/android platform/ios area-architecture Issues with code structure, SDK structure, implementation details labels Aug 7, 2025
@simonrozsival
Copy link
Copy Markdown
Member Author

@copilot there are merge conflicts in this branch. please resolve them. When you're done, write summary of what this PR does and what functionality is missing to fully convert the "old" way of implementing handlers to the "new" way.

Copy link
Copy Markdown
Contributor

Copilot AI commented Nov 25, 2025

@simonrozsival I've opened a new pull request, #32861, to work on those changes. Once the pull request is ready, I'll request review from you.

@simonrozsival
Copy link
Copy Markdown
Member Author

@copilot oops, try now!

Copy link
Copy Markdown
Contributor

Copilot AI commented Nov 25, 2025

@simonrozsival I've opened a new pull request, #32862, to work on those changes. Once the pull request is ready, I'll request review from you.

@simonrozsival
Copy link
Copy Markdown
Member Author

@copilot there are several errors popping up:

  • src\Controls\src\Core\Shell\Shell.cs(29,25): Error CS0246: The type or namespace name 'ShellHandler' could not be found (are you missing a using directive or an assembly reference?)
  • src\Controls\src\Core\Shell\ShellItem.cs(18,25): Error CS0246: The type or namespace name 'ShellItemHandler' could not be found (are you missing a using directive or an assembly reference?)
  • buildtasks/Microsoft.Maui.Controls.Build.Tasks.After.targets(15,3): error MSB4024: The imported project file "/mnt/vss/_work/1/s/.buildtasks/Microsoft.Maui.Controls.targets" could not be loaded. Name cannot begin with the '<' character, hexadecimal value 0x3C. Line 325, position 2. [/mnt/vss/_work/1/s/src/BlazorWebView/tests/DeviceTests/MauiBlazorWebView.DeviceTests.csproj]

Fix them and make sure the product can be built.

Copy link
Copy Markdown
Contributor

Copilot AI commented Dec 1, 2025

@simonrozsival I've opened a new pull request, #32933, to work on those changes. Once the pull request is ready, I'll request review from you.

@simonrozsival simonrozsival force-pushed the dev/simonrozsival/net10.0-use-element-handler-attributes branch from f5c8447 to f097792 Compare February 19, 2026 14:21
@albyrock87
Copy link
Copy Markdown
Contributor

albyrock87 commented Feb 20, 2026

It's not clear to me @simonrozsival how one can create their own Button handler.
Would they still use the DI?
If not, how can the system know which one to use? (The MAUI one or the custom one)

Edit: yes it seems DI win, thanks!

So this is basically a feature for library authors to make their handlers trimmable.

@simonrozsival
Copy link
Copy Markdown
Member Author

@albyrock87 exactly. If you don't use webview in your app, there should be no webview related code in the app.

This change should be as transparent as possible. Any custom handler registered via DI should still work.

@StephaneDelcroix StephaneDelcroix force-pushed the dev/simonrozsival/net10.0-use-element-handler-attributes branch from f097792 to 6695f72 Compare February 27, 2026 15:28
@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Feb 27, 2026

🚀 Dogfood this PR with:

⚠️ WARNING: Do not do this without first carefully reviewing the code of this PR to satisfy yourself it is safe.

curl -fsSL https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.sh | bash -s -- 29952

Or

  • Run remotely in PowerShell:
iex "& { $(irm https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.ps1) } 29952"

@simonrozsival simonrozsival force-pushed the dev/simonrozsival/net10.0-use-element-handler-attributes branch from 6695f72 to d3fb530 Compare March 11, 2026 09:52
@simonrozsival
Copy link
Copy Markdown
Member Author

/azp run maui-pr-devicetests,maui-pr-uitests

@azure-pipelines
Copy link
Copy Markdown

Azure Pipelines successfully started running 2 pipeline(s).

@simonrozsival
Copy link
Copy Markdown
Member Author

/azp run maui-pr-devicetests,maui-pr-uitests

@azure-pipelines
Copy link
Copy Markdown

Azure Pipelines successfully started running 2 pipeline(s).

@simonrozsival
Copy link
Copy Markdown
Member Author

/azp run maui-pr,maui-pr-devicetests,maui-pr-uitests

@azure-pipelines
Copy link
Copy Markdown

Azure Pipelines successfully started running 3 pipeline(s).

@simonrozsival
Copy link
Copy Markdown
Member Author

/azp run maui-pr-devicetests,maui-pr-uitests

@azure-pipelines
Copy link
Copy Markdown

Azure Pipelines successfully started running 2 pipeline(s).

@simonrozsival
Copy link
Copy Markdown
Member Author

@jfversluis @mattleibow could you please have a look at this PR and let me know what you think about it? could we merge this into net11.0 soon?

jfversluis pushed a commit that referenced this pull request Mar 20, 2026
### Description of Change

This PR will allow trimming CSS-related code and default styles when
there are no CSS stylesheets used in the MAUI app.
- When `@(MauiCss)` item is added, the CSS feature is enabled
- When the app does not have any `@(MauiCss)` but devs want to use
`Style.Parse` to parse styles at runtime, they must enable
`$(MauiCssEnabled)=true`

Most of the changes in this PR are around moving assembly-level
attributes into a local list. This will allow us to shave of some
unnecessary code from most apps:
```c#
// before
[assembly: StyleProperty("background-color", typeof(VisualElement), nameof(VisualElement.BackgroundColorProperty))]

// after
new StylePropertyAttribute("background-color", typeof(VisualElement), nameof(VisualElement.BackgroundColorProperty)),
```

Related to #29952

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@simonrozsival
Copy link
Copy Markdown
Member Author

/azp run maui-pr-devicetests,maui-pr-uitests

@azure-pipelines
Copy link
Copy Markdown

Azure Pipelines will not run the associated pipelines, because the pull request was updated after the run command was issued. Review the pull request again and issue a new run command.

simonrozsival and others added 5 commits March 31, 2026 09:52
Squashed for clean rebase onto net11.0.
Handlers resolved via [ElementHandler] attribute use
Activator.CreateInstance(), not ActivatorUtilities.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add [DynamicallyAccessedMembers(PublicConstructors)] to the 'handlerType'
out parameter so it satisfies the return annotation on GetHandlerType().
This fixes NETSDK1144 build failures in RunOniOS_MauiReleaseTrimFull.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The method is now a no-op, and warnings-as-errors (CS0618) breaks the
device test build on CI.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Two root cause bugs in [ElementHandler] attribute placement:

1. TabbedPage on iOS/MacCatalyst incorrectly used NavigationRenderer instead
   of TabbedRenderer. This caused TabbedPage to be treated as a NavigationPage,
   leading to crashes on iOS/Mac.

2. ShellItem's [ElementHandler] attribute was placed on FlyoutItem (a derived
   class) instead of ShellItem (the base class). Since TryGetElementHandlerAttribute
   walks the base type chain, TabBar (which also inherits from ShellItem but not
   from FlyoutItem) could not resolve its handler on Windows/Tizen, causing Shell
   test crashes.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@simonrozsival simonrozsival force-pushed the dev/simonrozsival/net10.0-use-element-handler-attributes branch from f97fbcc to 5327392 Compare March 31, 2026 07:53
@simonrozsival
Copy link
Copy Markdown
Member Author

/azp run maui-pr,maui-pr-devicetests,maui-pr-uitests

@azure-pipelines
Copy link
Copy Markdown

Azure Pipelines successfully started running 3 pipeline(s).

simonrozsival and others added 2 commits March 31, 2026 11:03
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
HybridWebViewHandler is marked [RequiresDynamicCode] because it uses
dynamic System.Text.Json serialization. Placing [ElementHandler(typeof(
HybridWebViewHandler))] on HybridWebView creates a hard compile-time
type reference that ILC cannot eliminate via feature switches, causing
NativeAOT compilation failures.

Restore the conditional DI registration pattern behind
RuntimeFeature.IsHybridWebViewSupported, which has [FeatureSwitchDefinition]
and [FeatureGuard(typeof(RequiresDynamicCodeAttribute))] attributes that
allow ILC to eliminate the entire code path (including the typeof reference)
when NativeAOT is used.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area-architecture Issues with code structure, SDK structure, implementation details copilot platform/android platform/ios platform/macos macOS / Mac Catalyst platform/windows

Projects

Status: In Progress

Development

Successfully merging this pull request may close these issues.

7 participants