Consider allowing inheritance from the built-in handlers when creating custom controls
- 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
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