dotnet / dotnet/maui

Consider allowing inheritance from the built-in handlers when creating custom controls

Open
#4,810 6 comments 14 reactions 0 assignees View on GitHub
area-architecture legacy-area-controls partner proposal/open
Dominant language
C#
Stars
23.3k
Forks
2k
Avg merge
1d 15h
Merged PRs (30d)
290

Description

### Description

When creating a custom control, there are two general approaches:
1. Create an entirely new control from scratch, with its own platform-specific implementations and handlers.
2. Extend an existing MAUI control to add extra functionality, inheriting from the existing handlers of the control.

Here, I am talking about the second approach, namely inheriting from an existing MAUI control and its handlers. While this is supposed to work in theory, often the handlers are not implemented with inheritance in mind. For instance, the consider following code in the ButtonHandler for iOS:

```csharp
protected override UIButton CreatePlatformView()
{
var button = new UIButton(UIButtonType.System);
SetControlPropertiesFromProxy(button);
return button;
}
```

https://github.com/dotnet/maui/blob/cf6896e276aa02023db22c59a25f1d3b20024e6d/src/Core/src/Handlers/Button/ButtonHandler.iOS.cs#L28

The `CreatePlatformView` method is `virtual`, so in theory it is possible to override it to return an instance of a different class, as long as it inherits for `UIButton`. That is not possible in practice however, because the base `CreatePlatformView` implementation calls a private `SetControlPropertiesFromProxy` method, which is not accessible to the inheritors.

The above is just an example. There are many more places, where we have some kind of private logic *"buried"* in the base handlers, which is not accessible to the inheritors. Often, overriding certain methods lead to subtle bugs, which can be resolved only by reading the source code of the handler and (unfortunately) duplicating the missing logic, which is inaccessible.

I am pretty sure the handlers can be designed in such a way, to allow inheriting form them, without exposing all the internals. For example, the above problem could be mitigated by using the [template method pattern](https://en.wikipedia.org/wiki/Template_method_pattern), e.g.:

```csharp
protected TPlatformView CreatePlatformView()
{
var platformView = this.CreatePlatformViewOverride();
// Do other required stuff with the platformView
return platformView;
}

protected abstract TPlatformView CreatePlatformViewOverride();
```

As we are approaching the release candidate timeframe, it is about a time to do such clean up of the implementation. Otherwise, we would be stuck forever with such flawed approaches and questionable practices, unwilling to change them to avoid introducing breaking changes to production code.

Contributor guide

Open the contributing guide

Research direction

Start with src/Core/src/Handlers/Button/ButtonHandler.iOS.cs and inspect CreatePlatformView plus the referenced private SetControlPropertiesFromProxy logic. Then compare other built-in handler implementations for similar inheritance barriers and look for relevant handler tests. Done would require an agreed, compatible design that lets custom controls reuse built-in handler behavior without duplicating inaccessible logic.

Written by the indexing model from the issue text.

Assessment

Tech stack
csharp
Domain
frontend, mobile-dev
Issue type
Refactor
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
25/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.