Telerik blogs

When it comes to custom renderers in your Xamarin.Forms app, how do those migrate to .NET MAUI? One option is handlers.

If you are reading this, you are already on track to porting your Xamarin project to .NET MAUI and now the topic of platforms customization is what bothers you.

In Xamarin.Forms, one of the main tools you have used is custom renderers. So how do you achieve the same functionality in .NET MAUI? Let’s see:

  • What are the options?
  • Which one to choose?
  • Details for both options

What are the Options?

Currently, we can either:

  1. Option 1: Move the code – transfer our old code with some modifications but using the same renderers
  2. Option 2: Rewrite renderers to handlers – use the new API and architecture that .NET MAUI offers us, handlers and mappers

So, How to Choose?

The first approach is faster when we are talking about dev time, so choose this as an entry point if you want quick results or if you still have not used handlers.

But have in mind that keeping renderers is not a good solution in the long term as the new approach is there for a purpose and to stay. Handlers and mappers are the result of the new and better architecture of .NET MAUI which brings us performance improvements and easy support for new platforms.

However, one important thing to consider also—not all Microsoft custom renderers are ported to the new technology as a Cell or ListView. In this case, if you use them, you simply do not have a choice—your custom renderer must stay for now. However, such components are rarer and rarer—check them in the Microsoft article here.

Now, let’s review both options in detail.

Option 1: Continue to Use the Same Renderers

With some modification, the custom renderers from your Xamarin app can be reused and are still supported in .NET MAUI. However, note that this will not be the case in the long term, so this is a short-term solution and the recommended way is to scroll to the second option.

Still, here are the main steps to add the custom renderers from your Xamarin project to .NET MAUI. It is important to know that you can still use your renderers almost without change. To do that you need to:

1. Move the Files to the Right Place

Every new .NET MAUI project has a Platforms folder with a subfolder for the respective platforms. Move your renderers there. 😊

For example, if we want to customize the entry control, we will have the same custom MyEntry.cs class to inherit the Entry and custom renderer for every affected platform:

Xamarin vs .NET MAUI files

2. Modify the Code a Little Bit

If you still haven’t changed the namespaces, now it is the time. 😊

Any reference to Xamarin.Forms.* namespaces need to be removed, and then you can resolve the related types to Microsoft.Maui.*.

You should also remove any ExportRenderer attributes as they won’t be needed in .NET MAUI. For example, the following should be removed for a UWP-specific renderer:

[assembly: ExportRenderer(typeof(MyEntry), typeof(XamarinRendererProject.UWP.MyEntryRenderer))]

namespace XamarinRendererProject.UWP

{

}

3. Register the Renderers

Open MauiProgram.cs.

Add a using statement for the Microsoft.Maui.Controls.Compatibility.Hosting namespace.

Call UseMauiCompatibility on the MauiAppBuilder object in the CreateMauiApp method, and configure each renderer using conditional compilation per platform:

using Microsoft.Maui.Controls.Compatibility.Hosting;  
public static class MauiProgram  
{  
  public static MauiApp CreateMauiApp() 	 
  {  
    var builder = MauiApp.CreateBuilder();  
    builder .UseMauiApp<App>()  
    .UseMauiCompatibility()  
    .ConfigureFonts(fonts => { fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular"); fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold"); }) 
    .ConfigureMauiHandlers((handlers) => {  
      #if Windows handlers.AddHandler(typeof(MyEntry), typeof(MyMauiProject.Windows.Renderers. MyEntryRenderer));  

      #elif IOS...... 
      #endif  
    });  
    return builder.Build(); 
  }  
} 

So it is working. 😊 Then why are we talking about migration? Renderers are one of the architectural changes that bring us performance improvement in .NET MAUI compared to Xamarin.Form. Also, although renderers are currently supported, Microsoft does not claim that this will be true in the upcoming version of .NET.

Option 2: Using Handlers and Mappers

This new handler concept is one of the key aspects of evolving Xamarin to .NET MAUI and in the improved performers and ability to support more platforms.

So, What are These Handlers?

The handlers are already implemented classes in .NET MAUI that match to every type of control/view that exists in .NET MAUI via an interface.

Note that their scope is global. This means that if we use and modify one handler for a control—let’s say Button—this customization will affect all buttons.

.NET MAUI handlers are accessed through their control-specific interface, such as IButton for a Button. This avoids the cross-platform control having to reference its handler, and the handler having to reference the cross-platform control. Clean architecture, right? 

Some important properties that we need to know to understand the concept:

  • PlatformView – Each handler class exposes the native view for the cross-platform control via its PlatformView property. This property can be accessed to set native view properties, invoke native view methods, and subscribe to native view events.
  • VirtualView – The cross-platform control implemented by the handler is exposed via its VirtualView property.

What are Mappers?

Handlers can be used and accessed, but when we want to modify how a specific property or behavior to be changed, we actually use mappers.

There are two types of mappers:

  • A property mapper defines what Actions to take when a property change occurs in the cross-platform control. It’s a Dictionary that maps the cross-platform control’s properties to their associated Actions. Each platform handler then provides implementations of the Actions, which manipulate the native view API. This ensures that when a property is set on a cross-platform control, the underlying native view is updated as required.
  • A command mapper defines what Actions to take when the cross-platform control sends commands to native views.

So, handlers and mappers are great, but how do we use them? For example, how can we port our code and modify the color of Entry component? Here are three ways.

Scenario 1: Access the Handler When You Need to Modify It

This approach is not very difficult, and it is just one of the major scenarios to use handlers. To use this approach, simply add the “Handler” keyword to the component you need to modify (for example, in the constructor of our content page) and you will be good to use it.

When you want to customize the entry, you will need EntryHandler—for a button, ButtonHandler, etc.

Ex: Microsoft.Maui.Handlers.EntryHandler

Then set the right mapping and use the PlatformView to access the native control. (MyCustomization is a sample key name that we put in our mapping.)

void  ModifyEntry()
{
Microsoft.Maui.Handlers.EntryHandler.Mapper.AppendToMapping("**MyCustomization**", (handler, view) =>
{
#if WINDOWS
				if (view is MyEntry) //here we check and customize only MyEntry instances and not all entries, without this we can easily customize all entries if we needed it
				{
							handler.PlatformView.Background = Brush.Cyan;
				}
#endif
			});
}

Scenario 2: Use Handler Lifecycle Events to Access It and Modify Properties of the Components

Handlers have their lifecycles and set of events that we can listen to. For example, we can subscribe to the Entry instance and listen for HandlerChanged and HandlerChanging and change the properties that we need there.

A complete example can be found in MS docs .NET MAUI control customization with handlers - .NET MAUI | Microsoft Learn and in Telerik docs showing this approach with RadListView.

Scenario 3: Create a Custom Component and Create a Whole New Handler

The process for this approach is based on six steps:

  1. Create an interface, implementing IView.
  2. Create a subclass of the ViewHandler
  3. In the ViewHandler subclass, override the CreatePlatformView method that renders the platform control.
  4. Create the PropertyMapper dictionary, which handles what actions to take when property changes occur.
  5. Create a custom control by subclassing the View class and implementing the control interface.
  6. Register the handler using the AddHandler method in the MauiProgram class.

For the full detailed example—please go to the MS docs here.

Conclusion

Handlers are so flexible, they bring performance improvement and their API is more extensible. However, it needs time to get used to the concept, but it is worth it and is one of the major reasons to make .NET MAUI better than Xamarin.Forms. So, go ahead and play with this. You can also consider reading Xamarin to MAUI migration tips on Telerik and Microsoft for more scenarios.

Telerik UI for .NET MAUI is also here with all the components needed and documentation to ease the journey of apps including Telerik controls. Try it for free. 

Rossitza-Fakalieva
About the Author

Rossitza Fakalieva

Rossitza Fakalieva is a Technical Manager, Microsoft MVP in Developer Technologies and a Director of the Bulgarian chapter of the global Women Who Code organization. She previously worked on the Telerik engineering team and defines herself as .NET enthusiast. She loves to empower others to grow in their career and in the tech field—by teaching, by delivering courses and presentations, and as part of her daily job.

Related Posts

Comments

Comments are disabled in preview mode.