Building cross platform native control implementation in MAUI

Posted by elkidogz on Mon, 03 Jan 2022 03:23:45 +0100

Hello, I'm Zhou Hao, a researcher of Microsoft MVP laboratory in this issue. MAUI uses the Handler system to handle the implementation of native controls on different platforms, that is, if we want to create controls, we only need to create handlers based on different platforms. So the following mainly teaches you how to build your own controls by creating a Handler (event Handler).

Next, we will create a progress bar control case to demonstrate how to create a platform control in a MAUI project and use it.

Suppose that the control contains three basic functions: progress bar color (foregroup), progress bar current value (Value) and progress bar mode (Indeterminate).

First step

(declare control class)

First, create the MyProgressBar class and define the corresponding dependent properties.

internal class MyProgressBar : View
    {
        public static readonly BindableProperty ForegroundProperty =
            BindableProperty.Create(nameof(Foreground),
                typeof(Color),
                typeof(MyProgressBar),
                Colors.Transparent);

        public static readonly BindableProperty ValueProperty =
           BindableProperty.Create(nameof(Value),
               typeof(double),
               typeof(MyProgressBar),
               0.0);

        public static readonly BindableProperty IndeterminateProperty =
           BindableProperty.Create(nameof(Indeterminate),
               typeof(bool),
               typeof(MyProgressBar),
               false);

        public Color Foreground
        {
            get { return (Color)GetValue(ForegroundProperty); }
            set { SetValue(ForegroundProperty, value); }
        }
         
        public double Value
        {
            get { return (double)GetValue(ValueProperty); }
            set { SetValue(ValueProperty, value); }
        }

        public bool Indeterminate
        {
            get { return (bool)GetValue(IndeterminateProperty); }
            set { SetValue(IndeterminateProperty, value); }
        }
    }

Step 2

Create standard handler
With the standard property definition of the control, the next step is to define the standard Handler handler, which includes the control property mapper and constructor, as shown below:

partial class MyProgressBarHandler
    {
        public static PropertyMapper<MyProgressBar, MyProgressBarHandler> HorizontalProgressBarMapper = new
        (ViewHandler.ViewMapper)
        {
            [nameof(MyProgressBar.Value)] = MapValue,
            [nameof(MyProgressBar.Foreground)] = MapForeground, 
            [nameof(MyProgressBar.Indeterminate)]= MapIndeterminate
        };

        public MyProgressBarHandler(PropertyMapper mapper)
            : base(mapper)
        {

        }

        public MyProgressBarHandler() : base(HorizontalProgressBarMapper)
        {

        }
    }

Step 3

Create platform handler
In the attribute mapper, we can easily see the event handlers corresponding to the three attributes, but they are not defined at present, which means that you need to implement the corresponding three event handlers on different platforms. Quickly define a MyProgressBarHandler under platforms > Android > controls, as shown below:

It then inherits from ViewHandler and is associated with the native Android ProgressBar.

using Android.Widget;

partial class MyProgressBarHandler :
        ViewHandler<MyProgressBar, ProgressBar>
    { 

    }

Override createnativeview (this is the first place to create a local control).

protected override ProgressBar CreateNativeView()
        {
            return new ProgressBar(Context, null,  Android.Resource.Attribute.ProgressBarStyleHorizontal)
            {
                Indeterminate = true,
                Max = 10000,
            }; 
        }

Next, implement three event handler methods, MapValue, mapforegroup and MapIndeterminate.

static void MapValue(MyProgressBarHandler handler, MyProgressBar view)
        {
            var nativeView= handler?.NativeView;
            nativeView.Progress = (int)(view.Value * Max);
        }

        static void MapForeground(MyProgressBarHandler handler, MyProgressBar view)
        {
            UpdateForeground(handler?.NativeView, view.Foreground);

            static void UpdateForeground(ProgressBar nativeProgressBar, Color color)
            {
                if (color == null)
                {
                    (nativeProgressBar.Indeterminate ? nativeProgressBar.IndeterminateDrawable :
                        nativeProgressBar.ProgressDrawable)?.ClearColorFilter();
                }
                else
                {
                    var tintList = ColorStateList.ValueOf(color.ToNative());

                    if (nativeProgressBar.Indeterminate)
                        nativeProgressBar.IndeterminateTintList = tintList;
                    else
                        nativeProgressBar.ProgressTintList = tintList;
                }
            }
        }

        static void MapIndeterminate(MyProgressBarHandler handler, MyProgressBar view)
        {
           var nativeView= handler?.NativeView;
            nativeView.Indeterminate = view.Indeterminate;
        }

Step 4

The corresponding Handler event Handler that implements the iOS platform is the same as the previous steps, and the event processing details correspond to the logical processing of different platforms

partial class MyProgressBarHandler :
        ViewHandler<MyProgressBar, UIProgressView>
    {
        protected override UIProgressView CreateNativeView()
{
            return new UIProgressView(UIProgressViewStyle.Default);
        }
         
        static void MapValue(MyProgressBarHandler handler, MyProgressBar view)
{
            var nativeView = handler.NativeView;
            nativeView.Progress = (float)view.Value;
        }

        static void MapForeground(MyProgressBarHandler handler, MyProgressBar view)
{
            var nativeView = handler.NativeView;
            nativeView.ProgressTintColor = view.Foreground?.ToNative();
        } 

        static void MapIndeterminate(MyProgressBarHandler handler, MyProgressBar view)
{
            //...
        }
    }

Step 5

Open the MauiProgram file and add AddHandler

public static MauiApp CreateMauiApp()
        {
            var builder = MauiApp.CreateBuilder();
            builder.UseMauiApp<App>()
                .ConfigureFonts(fonts =>
                {
                    fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
                })
                .ConfigureMauiHandlers(handler =>
                {
                  handler.AddHandler(typeof(MyProgressBar), typeof(MyProgressBarHandler));
                });  
            return builder.Build();
        }

Step 6

In the interface, MAUI native controls and custom controls are declared respectively

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="MAUIRender.MainPage"
             xmlns:my="clr-namespace:MAUIRender" 
             xmlns:ctor="clr-namespace:MAUIRender.Controls" 
             BackgroundColor="{DynamicResource SecondaryColor}">
    <Grid>
        <StackLayout>
            <ProgressBar   
                Progress="30" ProgressColor="Red"/>
            
            <ctor:MyProgressBar 
                Indeterminate="True"
                Value="600"  Foreground="Green" />
        </StackLayout>
    </Grid>
</ContentPage>

Actual operation effect:

By using Handler to handle the behavior of controls on different platforms, it is decoupled from the control itself and easier to support more platforms.

Microsoft MVP

Microsoft's most valuable expert is a global award awarded by Microsoft to third-party technology professionals. Over the past 28 years, technology community leaders around the world have won this award for sharing expertise and experience in their online and offline technology communities.

MVP is a strictly selected expert team. They represent the most skilled and intelligent people. They are experts who are enthusiastic and helpful to the community. MVP is committed to helping others through lectures, forum Q & A, creating websites, writing blogs, sharing videos, open source projects, organizing meetings, and helping users in the Microsoft technology community use Microsoft technology to the greatest extent.
For more details, please visit the official website:
https://mvp.microsoft.com/zh-cn

Topics: .NET