Note
Access to this page requires authorization. You can try signing in or changing directories.
Access to this page requires authorization. You can try changing directories.
While there are many benefits to using .NET Multi-platform App UI (.NET MAUI) handlers to customize and create controls, it's still possible to use Xamarin.Forms custom renderers in .NET MAUI apps. For more information about custom renderers, see Xamarin.Forms custom renderers.
Important
The optional Microsoft.Maui.Controls.Compatibility NuGet package is no longer built or shipped in .NET 11 and later. This article applies to shimmed renderer base classes that are still included with Microsoft.Maui.Controls. If your custom renderer depends on APIs or Xamarin.Forms compatibility renderers that were only available from the opt-in compatibility package, remove the package reference and migrate those usages to .NET MAUI handlers before upgrading.
Shimmed renderers
.NET MAUI provides shimmed renderers that enable easy re-use of Xamarin.Forms custom renderers, provided that the renderer derives from FrameRenderer, ListViewRenderer, ShellRenderer on iOS and Android, TableViewRenderer, and VisualElementRenderer.
The process for migrating a Xamarin.Forms custom renderer that derives from FrameRenderer, ListViewRenderer, ShellRenderer, TableViewRenderer, and VisualElementRenderer to a .NET MAUI shimmed renderer is to:
- Add the custom renderer code into the appropriate location in your .NET MAUI project(s). For more information, see Add the code.
- Modify the
usingdirectives and removeExportRendererattributes. For more information, see Modify using directives and other code. - Register the renderers. For more information, see Register renderers.
- Consume the renderers. For more information, see Consume the custom renderers.
To demonstrate using custom renderers in .NET MAUI, consider a Xamarin.Forms control named PressableView. This control exposes Pressed and Released events based on platform-specific gestures. The custom renderer implementation is composed of 3 files:
PressableView.cs- the cross-platform class that extendsContentView.PressableViewRenderer.cs- the Android implementation, which derives fromVisualElementRenderer.PressableViewRenderer.cs- the iOS implementation, which derives fromVisualElementRenderer.
Note
An alternative to using a Xamarin.Forms custom renderer in .NET MAUI is to migrate the custom renderer to a .NET MAUI handler. For more information, see Migrate a Xamarin.Forms custom renderer to a .NET MAUI handler.
Add the code
If you're using a .NET MAUI multi-targeted project, the cross-platform file can be moved to anywhere outside the Platforms folder, and the platform-specific implementation files should be moved to the corresponding Platform folder:
If your solution has separate projects per-platform, then you should move the platform-specific implementation files into the corresponding projects.
Modify using directives and other code
Any reference to the Xamarin.Forms.* namespaces need to be removed, and then you can resolve the related types to Microsoft.Maui.*. This needs to occur in all files you've added to the .NET MAUI project(s).
You should also remove any ExportRenderer attributes as they won't be needed in .NET MAUI. For example, the following should be removed:
[assembly: ExportRenderer(typeof(PressableView), typeof(PressableViewRenderer))]
Register renderers
The cross-platform control and its renderers must be registered with an app before it can be consumed. This should occur in the CreateMauiApp method in the MauiProgram class in your app project, which is the cross-platform entry point for the app:
public static class MauiProgram
{
public static MauiApp CreateMauiApp()
{
var builder = MauiApp.CreateBuilder();
builder
.UseMauiApp<App>()
.ConfigureFonts(fonts =>
{
fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold");
})
.ConfigureMauiHandlers((handlers) =>
{
#if ANDROID
handlers.AddHandler(typeof(PressableView), typeof(XamarinCustomRenderer.Droid.Renderers.PressableViewRenderer));
#elif IOS
handlers.AddHandler(typeof(PressableView), typeof(XamarinCustomRenderer.iOS.Renderers.PressableViewRenderer));
#endif
});
return builder.Build();
}
}
The renderers are registered with the ConfigureMauiHandlers and AddHandler method. This first argument to the AddHandler method is the cross-platform control type, with the second argument being its renderer type.
Important
Only renderers that derive from FrameRenderer, ListViewRenderer, NavigationRenderer on iOS, ShellRenderer on iOS and Android, TabbedRenderer on iOS, TableViewRenderer, and VisualElementRenderer can be registered with the AddHandler method.
Migrate Android Shell renderers
Starting in .NET MAUI 11, Android Shell uses handlers by default. Apps with custom Android Shell renderer subclasses should migrate those customizations to the Android Shell handler types where possible:
| Android renderer customization | Handler customization |
|---|---|
ShellRenderer subclass |
ShellHandler subclass |
ShellItemRenderer subclass |
ShellItemHandler subclass |
ShellSectionRenderer subclass |
ShellSectionHandler subclass |
ShellItemRenderer.OnTabReselected() |
ShellItemHandler.OnTabReselected() |
Shell More overflow menu or bottom sheet customization |
ShellItemHandler.CreateMoreBottomSheet() |
Navigation animation SetupAnimation(...) |
ShellSectionHandler.OnCreateNavigationAnimation(Context, bool isPopping, bool enter) |
If you need the legacy Android Shell renderer while migrating, register it explicitly with ConfigureMauiHandlers:
builder.ConfigureMauiHandlers(handlers =>
{
#if ANDROID
handlers.AddHandler<Shell, Microsoft.Maui.Controls.Handlers.Compatibility.ShellRenderer>();
#endif
});
Migrate iOS TabbedPage renderers
Starting in .NET MAUI 11, TabbedPage uses the Microsoft.Maui.Handlers.TabbedViewHandler handler by default on iOS and Mac Catalyst, instead of the TabbedRenderer compatibility renderer. Apps with a custom TabbedRenderer subclass should migrate those customizations to the handler's property mapper. For more information, see Customize controls with handlers.
If you need the compatibility renderer while migrating, register it explicitly with ConfigureMauiHandlers:
builder.ConfigureMauiHandlers(handlers =>
{
#if IOS || MACCATALYST
handlers.AddHandler<TabbedPage, Microsoft.Maui.Controls.Handlers.Compatibility.TabbedRenderer>();
#endif
});
If you've derived a custom renderer from TabbedRenderer, register your own type instead. For more information, see TabbedPage on iOS and Mac Catalyst.
Migrate iOS NavigationPage renderers
Starting in .NET MAUI 11, NavigationPage uses the Microsoft.Maui.Handlers.NavigationViewHandler handler by default on iOS and Mac Catalyst, instead of the NavigationRenderer compatibility renderer. Apps with a custom NavigationRenderer subclass should migrate those customizations to the handler's property mapper. For more information, see Customize controls with handlers.
If you need the compatibility renderer while migrating, register it explicitly with ConfigureMauiHandlers:
builder.ConfigureMauiHandlers(handlers =>
{
#if IOS || MACCATALYST
handlers.AddHandler<NavigationPage, Microsoft.Maui.Controls.Handlers.Compatibility.NavigationRenderer>();
#endif
});
If you've derived a custom renderer from NavigationRenderer, register your own type instead. For more information, see NavigationPage on iOS and Mac Catalyst.
Consume the custom renderers
The custom renderer can be consumed in a .NET MAUI app as a custom control:
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:controls="clr-namespace:XamarinCustomRenderer.Controls"
x:Class="MauiCustomRenderer.MainPage">
<Grid BackgroundColor="#f1f1f1">
<controls:PressableView Pressed="Handle_Pressed"
Released="Handle_Released"
HorizontalOptions="Center"
VerticalOptions="Center">
<Grid BackgroundColor="#202020"
HorizontalOptions="Center"
VerticalOptions="Center">
<Label Text="Press Me"
FontSize="16"
TextColor="White"
Margin="24,20"
HorizontalTextAlignment="Center" />
</Grid>
</controls:PressableView>
</Grid>
</ContentPage>
Browse the sample