[net11.0] Trimmable element handlers#29952
Conversation
There was a problem hiding this comment.
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>andCreateHandler(Context)on Android renderers - Add
MauiCssEnabledMSBuild 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
MauiCssEnabledbuild 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>
src/Controls/src/Core/Compatibility/Handlers/TableView/Android/TableViewRenderer.cs
Outdated
Show resolved
Hide resolved
src/Controls/src/Core/Compatibility/Handlers/ListView/Android/ListViewRenderer.cs
Outdated
Show resolved
Hide resolved
src/Controls/src/Core/Compatibility/Handlers/Android/FrameRenderer.cs
Outdated
Show resolved
Hide resolved
|
/azp run |
|
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. |
33afe18 to
c49e57e
Compare
|
@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. |
|
@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. |
|
@copilot oops, try now! |
|
@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. |
|
@copilot there are several errors popping up:
Fix them and make sure the product can be built. |
|
@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. |
f5c8447 to
f097792
Compare
|
It's not clear to me @simonrozsival how one can create their own Edit: yes it seems DI win, thanks! So this is basically a feature for library authors to make their handlers trimmable. |
|
@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. |
f097792 to
6695f72
Compare
|
🚀 Dogfood this PR with:
curl -fsSL https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.sh | bash -s -- 29952Or
iex "& { $(irm https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.ps1) } 29952" |
6695f72 to
d3fb530
Compare
|
/azp run maui-pr-devicetests,maui-pr-uitests |
|
Azure Pipelines successfully started running 2 pipeline(s). |
|
/azp run maui-pr-devicetests,maui-pr-uitests |
|
Azure Pipelines successfully started running 2 pipeline(s). |
|
/azp run maui-pr,maui-pr-devicetests,maui-pr-uitests |
|
Azure Pipelines successfully started running 3 pipeline(s). |
|
/azp run maui-pr-devicetests,maui-pr-uitests |
|
Azure Pipelines successfully started running 2 pipeline(s). |
|
@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? |
### 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>
|
/azp run maui-pr-devicetests,maui-pr-uitests |
|
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. |
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>
f97fbcc to
5327392
Compare
|
/azp run maui-pr,maui-pr-devicetests,maui-pr-uitests |
|
Azure Pipelines successfully started running 3 pipeline(s). |
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>
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 forAppHostBuilderExtensions.AddControlsHandlers()and makes handler associations visible at the type level, improving trimmability.Key changes
[ElementHandler]attributeElementHandlerAttributeon view types (e.g.,[ElementHandler(typeof(ButtonHandler))]) declaratively associates a view with its handlerInherited = false— each concrete type that needs a different handler must declare its own attributeBaseTypechain at resolution time, so derived types without their own attribute inherit the nearest ancestor's handlerHandler resolution in
MauiHandlersFactoryBoth
GetHandler(Type)andGetHandlerType(Type)follow the same 4-step resolution order:typeof(Button)) is registered viaAddHandler<Button, MyButtonHandler>(), that wins. DI-registered handlers are instantiated throughMauiFactory.GetService().[ElementHandler]attribute — walk the type'sBaseTypechain looking for the attribute. Attribute-based handlers are instantiated viaActivator.CreateInstance().Button→IButton) usingRegisteredHandlerServiceTypeSet, then look up the interface in DI. This is the path for third-party/custom handler overrides registered viaAddHandler<IButton, ...>().IContentViewfallback — if the type implementsIContentView, returnContentViewHandler.GetHandler()is the primary API — it returns an instantiatedIElementHandler.GetHandlerType()returns only theTypeand 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 theMissingMethodExceptionand falls back toActivatorUtilities.CreateInstance()with DI injection. This fallback path is cached per-type in aHashSet<Type>to avoid repeated exception throwing.Mapper remapping via static constructors
RemappingHelper.EnsureBaseTypeRemapped(), which accesses as_forceStaticConstructorfield on the parent type to force the runtime to run the parent's static constructor firstRemappingDebugHelper.AssertBaseClassForRemapping(DEBUG-only) validates that no intermediate type in the hierarchy is accidentally skippedCleanup
IElementHandlerWithAndroidContextinterface andElementHandlerWithAndroidContextAttributeRemapForControls(this MauiAppBuilder)extension method and oldRemapForControls()/RemapIfNeeded()patternIMauiHandlersFactory.GetHandler()to its original signature (GetHandler(Type)/GetHandler<T>()) — no longer requiresIMauiContextparameterMauiContext/HandlersContextStubdeclarations from tests and benchmarks