.net maui 中的自定义控件和渲染器会怎样?

What will happen to custom controls and Renderers in .net maui?

在我当前的 Xamarin.Forms 项目中,我为不同的需求创建了许多渲染器。现在 9 月毛伊岛即将到来,我们必须将我们的项目迁移到那里。那么渲染器会发生什么?它会按原样工作还是我们不需要渲染器?

在 .NET MAUI 中,渲染器的概念将消失,取而代之的是处理程序架构。渲染器与实际控件和支持的平台过于紧密耦合,最重要的是(或者可能正因为如此)它们很慢,因为它们依赖于反射等

.NET MAUI 之所以如此令人兴奋,很大一部分原因在于整个架构将会改变。现在,将不再是渲染器,而是基本上一次处理一个 属性 的处理程序,并且在处理程序和控件之间添加了一个抽象。这很重要,因为现在可以更轻松地实施后端(想想 iOS、Android,还有 SkiaSharp 或其他)。

关于您的问题:可能有不同的迁移路径。您可以按原样重用当前现有的自定义渲染器。应该有非常少的代码更改。 Xamarin.Forms/.NET MAUI 团队一直在努力实现一个兼容层来实现这一点。但是,您从切换到 .NET MAUI 中获得的好处有限,但是它会给您一些时间来重写那些渲染器,并且仍然能够发布您的应用程序。虽然应该还有很多时间,但 Xamarin.Forms 仍然支持到 2022 年 11 月。

在不涉及太多技术细节的情况下,在您的 Startup.cs class .NET MAUI 项目中重用您当前的渲染器可能如下所示(完整代码 here):


public void Configure(IAppHostBuilder appBuilder)
{
    appBuilder
        .UseMauiApp<App>()
        .ConfigureMauiHandlers(handlers =>
              {
#if __ANDROID__
        handlers.AddCompatibilityRenderer(typeof(Label), typeof(MAUICustomRendererSample.Platforms.Android.MyLabelRenderer));
#endif
        })
        .ConfigureFonts(fonts =>
        {
            fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
        });
}

那么,当然,仍然可以重写处理程序行为。根据您的需要,有多种方法可以做到这一点。只覆盖一个 属性,现在比渲染器时代容易得多。例如,查看下面的代码,仅更改 Button 的背景颜色(完整代码 here):

public void Configure(IAppHostBuilder appBuilder)
{
    appBuilder
        .UseMauiApp<App>()
        .ConfigureFonts(fonts =>
        {
            fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
        });

    #if __ANDROID__
                Microsoft.Maui.Handlers.ButtonHandler.ButtonMapper["MyCustomization"] = (handler, view) =>
                {
                    handler.NativeView.SetBackgroundColor(Android.Graphics.Color.Green);
                };
    #endif
}

创建一个完整的自定义控件,其工作量大致相同,只是方法不同。更多从 Forms 到 .NET MAUI 的转换示例可以在 Javier 的 this example 中找到,他一直从事 Forms 和现在的 .NET MAUI。大多数其他事情(行为、效果等)只不过是替换代码中的一些名称空间,它们都应该有效。

作为顶部的樱桃有一个Upgrade Assistant that will help you with the most tasks of changing your project and renaming namespaces etc. More info on that can be found in this months Community Standup session

根据项目大小和 time/budget 你有空,我个人会首先使用 Forms 兼容层移植所有内容。这应该需要最少的努力。检查一切是否仍按预期工作,您应该能够继续进行。之后,开始用新的处理程序结构替换您的渲染器。

最后;请注意,您可能使用的所有第三方库也都需要为 .NET 6 and/or .NET MAUI 做好准备,因此请确保在开始升级之前检查这些库。

其他值得关注的好资源是 the Docs 正在处理中。

In.Net Maui 是记录引用的完整自定义处理程序。

创建界面基础IView //ICustomButton.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace CustomButtonLib.Controls
{
    public interface ICustomButton : IView
    {
        public string Text { get; }
        public string TexColor { get; }
    }
}

CustomButton BindableProperty //CustomButton.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace CustomButtonLib.Controls
{
    public class CustomButton : View, ICustomButton
    {
        public static readonly BindableProperty TextProperty =
            BindableProperty.Create(nameof(Text), typeof(string), typeof(CustomButton), string.Empty);
        public string Text
        {
            get { return (string)GetValue(TextProperty); }
            set { SetValue(TextProperty, value); }
        }
        public static readonly BindableProperty TextColorProperty =
           BindableProperty.Create(nameof(TextColor), typeof(Color), typeof(CustomButton), Colors.ForestGreen);
        public Color TextColor
        {
            get { return (Color)GetValue(TextProperty); }
            set { SetValue(TextColorProperty, value); }
        }
    }


}

创建部分 CustomButtonHandler //CustomButtonHandler.cs

using CustomButtonLib.Controls;
using Microsoft.Maui.Handlers;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace CustomButtonLib.Handlers
{
    public partial class CustomButtonHandler
    {
        public static PropertyMapper<ICustomButton, CustomButtonHandler> CustomButtonMapper = new PropertyMapper<ICustomButton, CustomButtonHandler>(ViewHandler.ViewMapper)
        {
            [nameof(ICustomButton.Text)] = MapText,
            [nameof(ICustomButton.TextColor)] = MapTextColor,
        };

        public CustomButtonHandler() : base(CustomButtonMapper)
        {

        }
        public CustomButtonHandler(PropertyMapper mapper = null) : base(mapper ?? CustomButtonMapper)
        {

        }

    }
}

.NetStandard 部分 class //CustomButtonHandler.Standard.cs

using CustomButtonLib.Controls;
using Microsoft.Maui.Handlers;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace CustomButtonLib.Handlers
{
    public partial class CustomButtonHandler : ViewHandler<ICustomButton, object>
    {
        protected override object CreatePlatformView()
        {
            throw new NotImplementedException();
        }
        public static void MapText(CustomButtonHandler handler, ICustomButton iCustomButton) { }

        public static void MapTextColor(CustomButtonHandler handler, ICustomButton iCustomButton) { }
    }
}

在平台文件夹中创建特定的处理程序(例如 Windows)//CustomButtonHandler.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;


using Microsoft.Maui.Handlers;
using Microsoft.UI.Xaml;

namespace CustomButtonLib.Handlers
{
    public partial class CustomButtonHandler : ViewHandler<ICustomButton, FrameworkElement>
    {
        protected override FrameworkElement CreatePlatformView()
        {
            return new Microsoft.UI.Xaml.Controls.Button();
        }
        protected override void ConnectHandler(Microsoft.UI.Xaml.Controls.Button nativeView)
        {
            //Subscribe events

            base.ConnectHandler(nativeView);
        }

        protected override void DisconnectHandler(Microsoft.UI.Xaml.Controls.ButtonnativeView)
        {
            //Unsubcribe events
            base.DisconnectHandler(nativeView);
        }

        public static void MapText(CustomButtonHandler handler, ICustomButton iCustomButton)
        {
             public static void MapText(CustomButtonHandler handler, ICustomButton iCustomButton)
        { 
            if (handler != null && handler.PlatformView!=null && handler.PlatformView is Microsoft.UI.Xaml.Controls.Button bttn) 
                bttn.Content = iCustomButton.Text;
        }
        }

        [MissingMapper] //Add this to indicate if not available on this platform.
        public static void MapTextColor(CustomButtonHandler handler, ICustomButton iCustomButton) { }

    }
}

在其他平台上继续构建成功。

可选 => 添加项目 sdk 项目组,因此无需添加平台指令。 (例如#if Windows)。

<ItemGroup Condition="$(TargetFramework.StartsWith('net6.0-ios')) != true ">
        <Compile Remove="Platforms\iOS\*.cs" />
        <None Include="Platforms\iOS\*.cs" />
    </ItemGroup>

    <ItemGroup Condition="$(TargetFramework.StartsWith('net6.0-maccatalyst')) != true ">
        <Compile Remove="Platforms\MacCatalyst\*.cs" />
        <None Include="Platforms\MacCatalyst\*.cs" />
    </ItemGroup>

    <ItemGroup Condition="$(TargetFramework.StartsWith('net6.0-android')) != true ">
        <Compile Remove="Platforms\Android\*.cs" />
        <None Include="Platforms\Android\*.cs" />
    </ItemGroup>

    <ItemGroup Condition="$(TargetFramework.Contains('-windows')) != true ">
        <Compile Remove="Platforms\Windows\*.cs" />
        <None Include="Platforms\Windows\*.cs" />
    </ItemGroup>

    <ItemGroup Condition="$(TargetFramework.StartsWith('net6.0-ios')) == true OR $(TargetFramework.StartsWith('net6.0-maccatalyst')) == true OR $(TargetFramework.StartsWith('net6.0-android')) == true OR $(TargetFramework.Contains('-windows')) == true">
        <Compile Remove="**\*.Standard.cs" />
        <None Include="**\*.Standard.cs" />
        <Compile Remove="**\Standard\**\*.cs" />
        <None Include="**\Standard\**\*.cs" />
    </ItemGroup>



    <!-- ANDROID -->
    <PropertyGroup Condition="$(TargetFramework.StartsWith('net6.0-android'))">
        <DefineConstants>$(DefineConstants);MONOANDROID</DefineConstants>
    </PropertyGroup>

    <!-- IOS -->
    <PropertyGroup Condition=" '$(TargetFramework)' == 'net6.0-ios' ">
        <DefineConstants>$(DefineConstants);IOS</DefineConstants>
    </PropertyGroup>

    <!-- MACCATALYST -->
    <PropertyGroup Condition=" '$(TargetFramework)' == 'net6.0-maccatalyst' ">
        <DefineConstants>$(DefineConstants);MACCATALYST;IOS</DefineConstants>
    </PropertyGroup>

    <!-- WINDOWS -->
    <PropertyGroup Condition="$(TargetFramework.Contains('-windows')) == true ">
        <TargetPlatformMinVersion>10.0.17763.0</TargetPlatformMinVersion>
        <RuntimeIdentifiers>win10-x86;win10-x64;win10-arm64</RuntimeIdentifiers>
        <DefineConstants>WINDOWS;$(DefineConstants)</DefineConstants>
    </PropertyGroup>

上次在构建器服务上注册自定义按钮 => 扩展

 public static MauiAppBuilder ConfigureLibrary(this MauiAppBuilder builder)
        {
            builder
                .ConfigureMauiHandlers(handlers =>
                {
                    handlers.AddLibraryHandlers();
                });
            return builder;
        }

        

        public static IMauiHandlersCollection AddLibraryHandlers(this IMauiHandlersCollection handlers)
        {
            handlers.AddTransient(typeof(CustomButton), h => new CustomButtonHandler());
            
            return handlers;
        }
// Usage .ConfigureLibrary()