Skip to content

Conversation

@simonrozsival
Copy link
Member

Description of Change

This proof of concept explores how we could get rid of the long DI setup code in AppHostBuilderExtensions:

internal static IMauiHandlersCollection AddControlsHandlers(this IMauiHandlersCollection handlersCollection)
{
    handlersCollection.AddHandler<CollectionView, CollectionViewHandler>();
    handlersCollection.AddHandler<CarouselView, CarouselViewHandler>();
    handlersCollection.AddHandler<Application, ApplicationHandler>();
    handlersCollection.AddHandler<ActivityIndicator, ActivityIndicatorHandler>();
    handlersCollection.AddHandler<BoxView, BoxViewHandler>();
    // ...

Listing all the classes this way makes it impossible for the trimmer to remove any of them.

The idea in this PR is to change MauiHandlersFactory to return the handler instance or the handler type from by calling an instance method on the control instead of looking up the handler in the DI container:

partial class Label
{
	protected override IElementHandler? GetHandler(IMauiContext context)
	{
		RemapForControlsIfNeeded();
		return new LabelHandler();
	}

	[return: DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)]
	protected override Type? GetHandlerType()
	{
		RemapForControlsIfNeeded();
		return typeof(LabelHandler);
	}
}
  • if the control isn't used anywhere in the app, the trimmer will remove it and its methods GetHandler and GetHandlerType
  • when these two methods are removed, there should be no other code instantiating the corresponding handler class
  • this means the trimmer can drop the handler class as well

The DI container should still be considered first before calling the instance method on the view for two reasons:

  1. Backwards compatibility: if the developer registers their own handlers in their app/library through DI today, it should continue working. They will need to make changes to their code to make their controls trimmable as well.
  2. Overwriting the defaults: It should be possible to change the default handlers for built in controls. I'm not sure how useful this is, but we should keep that possibility around.

Notes:

  • I'm sharing this Draft to get early feedback. I'm still not 100% sure this is the right way to do it. I'm especially not confident in the way RemapForControls would be handled.
  • I marked the remaining methods as [Obsolete] for now, but they should probably be dropped because they don't work 100% anymore.
  • This PR shows what the changes would look like for two controls: Label and CheckBox. These two are chosen because Label is in the dotnet new maui app and so we can test that the handler for that control is created correctly, and CheckBox is NOT in the sample app, so we can verify that the trimmer removes this class + the CheckBoxHandler when trim mode is set to full.

Feedback is very welcome!

/cc @jonathanpeppers @PureWeen @mattleibow @ivanpovazan @vitek-karas

@simonrozsival simonrozsival added the perf/app-size Application Size / Trimming (sub: perf) label Feb 28, 2025
Copy link
Contributor

@jsuarezruiz jsuarezruiz left a comment

Choose a reason for hiding this comment

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

UITests step failing because:
C:\a\_work\1\s\src\Controls\src\Core\Platform\GestureManager\GesturePlatformManager.Windows.cs(91,41): error CS8602: Dereference of a possibly null reference. [C:\a\_work\1\s\src\Controls\src\Core\Controls.Core.csproj::TargetFramework=net9.0-windows10.0.20348.0]

Comment on lines 8 to 19
protected override IElementHandler? GetHandler(IMauiContext context)
{
RemapForControlsIfNeeded();
return new CheckBoxHandler();
}

[return: DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)]
protected override Type? GetHandlerType()
{
RemapForControlsIfNeeded();
return typeof(CheckBoxHandler);
}
Copy link
Member

Choose a reason for hiding this comment

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

If the trimmer sees the new CheckBoxHandler() call above, is the [return:DAM] GetHandlerType() method needed at all?

Maybe I'm not following what calls GetHandlerType().

Copy link
Member Author

Choose a reason for hiding this comment

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

I hope we could get rid of the whole method later. For now, I'm keeping it to implement MauiHandlersFactory and make the trimmer happy here:

static IElementHandler? CreateTypeWithInjection(this IElement view, IMauiContext mauiContext)
{
var handlerType = mauiContext.Handlers.GetHandlerType(view);
if (handlerType == null)
return null;
#if ANDROID
if(mauiContext.Context != null)
{
return (IElementHandler)Extensions.DependencyInjection.
ActivatorUtilities.CreateInstance(mauiContext.Services, handlerType, mauiContext.Context);
}
#endif
return (IElementHandler)Extensions.DependencyInjection.
ActivatorUtilities.CreateInstance(mauiContext.Services, handlerType);
}

@simonrozsival simonrozsival force-pushed the dev/simonrozsival/trimmable-view-handlers branch from 9c90cfd to fd9d57a Compare March 3, 2025 17:43
@simonrozsival simonrozsival marked this pull request as ready for review March 6, 2025 12:55
@simonrozsival simonrozsival requested a review from a team as a code owner March 6, 2025 12:55
@simonrozsival simonrozsival marked this pull request as draft March 6, 2025 12:55
@simonrozsival simonrozsival changed the title [Trimming][PoC] Make it possible to trim controls and handlers [Trimming] Make it possible to trim controls and handlers Mar 11, 2025
@github-actions github-actions bot locked and limited conversation to collaborators Apr 12, 2025
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

perf/app-size Application Size / Trimming (sub: perf)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants