.net maui 中的自定义控件和渲染器会发生什么?

Div*_*_08 2 c# xamarin.ios xamarin xamarin.forms maui

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

Ger*_*uis 6

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

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

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

在不涉及太多技术细节的情况下,Startup.cs在 .NET MAUI 项目的类中重用当前的渲染器可能看起来像这样(完整代码在这里):


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");
        });
}
Run Code Online (Sandbox Code Playgroud)

然后,当然,仍然有可能覆盖处理程序行为。根据您的需要,有多种方法可以做到这一点。只覆盖一个属性,现在比渲染器时代要容易得多。例如,看看下面的代码来改变按钮的背景颜色(完整代码在这里):

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
}
Run Code Online (Sandbox Code Playgroud)

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

最重要的是,有一个升级助手可以帮助您完成更改项目和重命名命名空间等的大部分任务。更多信息可以在本月的社区站立会议中找到

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

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

其他值得关注的好资源是正在处理的文档


小智 5

在.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; }
    }
}
Run Code Online (Sandbox Code Playgroud)

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

Run Code Online (Sandbox Code Playgroud)

创建部分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)
        {
        }
    }
}

Run Code Online (Sandbox Code Playgroud)

.NetStandard 部分类 //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) 
        {
        }
    }
}

Run Code Online (Sandbox Code Playgroud)

在平台文件夹(例如 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 to events
            base.ConnectHandler(nativeView);
        }

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

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

    }
}
Run Code Online (Sandbox Code Playgroud)

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

可选 => 添加项目 sdk ItemGroups,因此无需添加平台指令。(例如#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>
Run Code Online (Sandbox Code Playgroud)

构建器服务上的最后注册自定义按钮 => 扩展

        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()
Run Code Online (Sandbox Code Playgroud)