What is maui Handler and can we achieve what we could with the Xamarin Renderers?

Emil Alipiev 271 Reputation points
2023-06-24T15:13:55.24+00:00

I think i had read and learnt enough even tried much with Maui Handlers but there is something missing or misleading in MS documentation and in all those tutorials.
It was presented as Renderers replaced with Handlers. But if handlers cannot achieve what Renderes were exactly doing how can this be said?
So with Custom Renderers, I was able to change a custom control on a platform level. Let me give you an example,

I have a custom entry inherited from Entry, see below. I have 2 new bindable properties and i define some default values like IsNumeric, Keyboard etc.

 public class CustomEntry : Entry
    {
        public bool IsNumeric { get; set; }
        public bool IsDouble { get; set; } = true;
        public int MaxDecimalDigits { get; set; }
        #region Constructors
        public CustomEntry()
            : base()
        {
            Handler = new CustomEntryHandler();
            MaxDecimalDigits = 2;
            IsNumeric = true;
            IsDouble = true;
            Keyboard = Keyboard.Numeric;
            // Set the events.
            base.TextChanged += OnTextChanged;
        }
        #endregion
        #region Properties
        public static readonly BindableProperty FormatStringValueProperty = BindableProperty.Create("FormatString", typeof(string), typeof(CustomEntry),
             defaultValue: string.Empty, defaultBindingMode: BindingMode.TwoWay);
        public string FormatString
        {
            get => (string)GetValue(FormatStringValueProperty);
            set => SetValue(FormatStringValueProperty, value);
        }
        public static readonly BindableProperty MaxValueProperty = BindableProperty.Create("MaxValue", typeof(double?), typeof(CustomEntry), defaultValue: null, defaultBindingMode: BindingMode.TwoWay);
        public double? MaxValue
        {
            get => (double?)GetValue(MaxValueProperty);
            set => SetValue(MaxValueProperty, value);
        }

then i decide I want to customize this for Ios using a custom renderer by updating borders, padding etc. only for Ios in a custom renderer. it will look like as below

public class CustomEntryRenderer : EntryRenderer
    {
        #region Parent override
        protected override void OnElementChanged(ElementChangedEventArgs<Entry> e)
        {
            base.OnElementChanged(e);
            if (e.OldElement != null || Element == null)
                return;
            Control.BorderStyle = UITextBorderStyle.None;
            UpdateBorderWidth();
            UpdateBorderColor();
            UpdateBorderRadius();
            UpdateLeftPadding();
            UpdateRightPadding();
        }
        protected override void OnElementPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
        {
            base.OnElementPropertyChanged(sender, e);
            if (this.Element == null)
                return;
            if (e.PropertyName == CustomEntry.BorderWidthProperty.PropertyName)
            {
                UpdateBorderWidth();
            }
            else if (e.PropertyName == CustomEntry.BorderColorProperty.PropertyName)
            {
                UpdateBorderColor();
            }
            else if (e.PropertyName == CustomEntry.BorderRadiusProperty.PropertyName)
            {
                UpdateBorderRadius();
            }
            else if (e.PropertyName == CustomEntry.LeftPaddingProperty.PropertyName)
            {
                UpdateLeftPadding();
            }
            else if (e.PropertyName == CustomEntry.RightPaddingProperty.PropertyName)
            {
                UpdateRightPadding();
            }
        }  

Now i want to update this from renderer to handler in Maui. How do i achieve it? when I use a handler with mappings like below

it creates completely new Entry from native handler. it doesn't extend or update. Because i don't see my default values defined in my Cross platform level within the constructor. So how do we achieve this task? when we use handlers is it no longer crossplatform control?

public partial class CustomEntryHandler : EntryHandler

{

    #region Parent override

    // protected override MauiTextField CreatePlatformView() => new(VirtualView.Frame);

    protected override void ConnectHandler(MauiTextField platformView)

    {

        platformView.KeyboardType = UIKeyboardType.NumberPad;

        platformView.BorderStyle = UITextBorderStyle.None;

        base.ConnectHandler(platformView);

    }

    #endregion

    #region Utility methods

    public static void UpdateBorderWidth(CustomEntryHandler handler, CustomEntry customEntry)

    {

        handler.PlatformView.UpdateBorder(customEntry);

    }

    public static void UpdateBorderColor(CustomEntryHandler handler, CustomEntry customEntry)

    {

        handler.PlatformView.UpdateBorder(customEntry);

    }

public partial class CustomEntryHandler

{

    public static PropertyMapper<CustomEntry, CustomEntryHandler> PropertyMapper = new PropertyMapper<CustomEntry, CustomEntryHandler>(EntryHandler.ViewMapper)

    {

 

#if IOS || MACCATALYST

        [nameof(CustomEntry.BorderWidth)] = UpdateBorderWidth,

        [nameof(CustomEntry.BorderColor)] = UpdateBorderColor,

        [nameof(CustomEntry.BorderRadius)] = UpdateBorderRadius,

        [nameof(CustomEntry.LeftPadding)] = UpdateLeftPadding,

        [nameof(CustomEntry.RightPadding)] = UpdateRightPadding,

#endif

    };

    public CustomEntryHandler() : base(PropertyMapper)

    {

    }


it creates completely new Entry from native handler. it doesn't extend or update. Because i don't see my default values defined in my Cross platform level within the constructor. So how do we achieve this task? when we use handlers is it no longer crossplatform control?

PS, i am using partial class approach and i want to use it because I want to keep in Maui class library creating nuget package and i dont have any MauiBuilder within a class library.

UPDATE:

I have created a test repo with demo maui project and maui class library.

https://github.com/EmilAlipiev/TestMauiHandlers

under the class library you find the Old renderers under each platform and new upgraded Handlers under handlers folders.

  1. So first run the Demo project in IOS without handlers. you can see the default value for text "this is default text" on the ui.
  2. enable ConfigureMauiHandler in MauiProgram.cs in HandlersDemoApp

3.You see that default text is empty

Reason for that is Handlers are creating a complete new Entry instead of extending the Entry defined in the shared part i believe.

If i use renderers they will be just extended with the default cross platform definitions.

last question; I have this CustomEntry in the Class library because i want to created a nuget package. How do i register handlers within the class library?

.NET MAUI
.NET MAUI
A Microsoft open-source framework for building native device applications spanning mobile, tablet, and desktop.
2,960 questions
{count} votes

1 answer

Sort by: Most helpful
  1. Wenyan Zhang (Shanghai Wicresoft Co,.Ltd.) 26,946 Reputation points Microsoft Vendor
    2023-06-27T08:54:57.6966667+00:00

    Hello,

    In the basic class EntryHandler, there is a default property mapper that maps the Entry's properties to their associated Actions.

    When you customize the EntryHandler, you create a new static PropertyMapper, so you could add the mapper (property: CustomEntry.Text action:MapText ) in your custom method. Then the default text won't be empty.

    public static PropertyMapper<CustomEntry, CustomEntryHandler> PropertyMapper = new PropertyMapper<CustomEntry, CustomEntryHandler>(EntryHandler.ViewMapper)
        {
            ...
            [nameof(CustomEntry.Text)] = MapText,
        };
    

    Please refer to the example that shows the CustomEntryHandler class extended with the PropertyMapper definition in the doc-Create the property mapper.

    And see the source code of EntryHandler.

    Best Regards,

    Wenyan Zhang


    If the answer is the right solution, please click "Accept Answer" and kindly upvote it. If you have extra questions about this answer, please click "Comment".

    Note: Please follow the steps in our documentation to enable e-mail notifications if you want to receive the related email notification for this thread.