MAUI中構建跨平臺原生控制元件實現

微軟技術棧發表於2022-01-07

大家好,我是本期的微軟MVP實驗室研究員——周豪。MAUI中使用Handler體系來處理不同平臺的原生控制元件實現,即對應的, 如果我們想要建立控制元件,只需要建立基於不同平臺的Handler即可。那麼下面主要教大家如何通過建立Handler(事件處理程式)來構建自己的控制元件。

image.png

下面,將通過建立一個進度條控制元件案例,來演示如何在MAUI專案中建立平臺控制元件並且使用它。

假設控制元件包含基礎的三項功能,進度條顏色(Foreground)、進度條當前值(Value)、進度條模式(Indeterminate)。

第一步

(宣告控制元件類)

首先,建立MyProgressBar類,定義對應的依賴屬性。

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); }
        }
    }

第二步

建立標準處理程式
有了控制元件的標準屬性定義之後,接下來就是定義標準的Handler處理程式,其中包含控制元件屬性對映器及建構函式,如下所示:

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)
        {

        }
    }

第三步

建立平臺處理程式
在屬性對映器中,我們可以很輕鬆看見對應了三個屬性的事件處理程式,但是目前並沒有定義它,這意味著你需要在不同平臺下分別實現對應的三個事件處理程式,趕緊在Platforms > Android > Controls 下定義了一個MyProgressBarHandler,如下所示:

98932604b2fb3f3dd16fdb8aead0d651.png

接著繼承於ViewHandler並且與原生安卓ProgressBar關聯。


using Android.Widget;

partial class MyProgressBarHandler :
        ViewHandler<MyProgressBar, ProgressBar>
    { 

    }

重寫CreateNativeView(這是建立本地控制元件最開始的地方)。

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

緊接著, 實現三個事件處理程式方法, MapValue、MapForeground、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;
        }

第四步

對應的實現iOS平臺的Handler事件處理程式, 與上步驟相同, 對於事件的處理細節則對應不同平臺的邏輯處理

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)
{
            //...
        }
    }

第五步

開啟MauiProgram檔案, 新增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();
        }

第六步

介面中,分別宣告MAUI原生控制元件與自定義控制元件

<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>

執行實際效果:

0b68e277a741cda08c7b0ce10106426e.jpg

通過利用Handler來處理不同平臺控制元件的行為,與控制元件本身解耦並且更加容易支援更多的平臺。

微軟最有價值專家(MVP)

bc93fde364ea9dd3d9106b58e805b770.png

微軟最有價值專家是微軟公司授予第三方技術專業人士的一個全球獎項。28年來,世界各地的技術社群領導者,因其線上上和線下的技術社群中分享專業知識和經驗而獲得此獎項。

MVP是經過嚴格挑選的專家團隊,他們代表著技術最精湛且最具智慧的人,是對社群投入極大的熱情並樂於助人的專家。MVP致力於通過演講、論壇問答、建立網站、撰寫部落格、分享視訊、開源專案、組織會議等方式來幫助他人,並最大程度地幫助微軟技術社群使用者使用Microsoft技術。
更多詳情請登入官方網站:
https://mvp.microsoft.com/zh-cn

相關文章