Sdílet prostřednictvím


Migrace vlastního rendereru Xamarin.Forms do obslužné rutiny .NET MAUI

V Xamarin.Forms se vlastní renderery dají použít k přizpůsobení vzhledu a chování ovládacího prvku a vytvoření nových ovládacích prvků pro různé platformy. Každý vlastní renderer má odkaz na řízení napříč platformami a často spoléhá na INotifyPropertyChanged odesílání oznámení o změnách vlastností. Místo použití vlastních rendererů představuje uživatelské rozhraní .NET Multi-Platform App UI (.NET MAUI) nový koncept označovaný jako obslužná rutina.

Obslužné rutiny nabízejí mnoho vylepšení výkonu u vlastních rendererů. V Xamarin.Forms třída ViewRenderer vytvoří nadřazený prvek. Například v Androidu se vytvoří objekt ViewGroup , který se používá pro pomocné úlohy umístění. V rozhraní .NET MAUI ViewHandler třída nevytvoří nadřazený prvek, což pomáhá snížit velikost hierarchie vizuálů a zlepšit výkon vaší aplikace. Obslužné rutiny také oddělí ovládací prvky platformy od architektury. Řízení platformy musí zpracovávat pouze potřeby architektury. To je nejen efektivnější, ale v případě potřeby je mnohem jednodušší rozšířit nebo přepsat. Obslužné rutiny jsou také vhodné pro opakované použití jinými architekturami, jako jsou kometa a Báječná. Další informace o obslužných rutinách naleznete v tématu Obslužné rutiny.

V Xamarin.Forms OnElementChanged metoda ve vlastním rendereru vytvoří ovládací prvek platformy, inicializuje výchozí hodnoty, přihlásí se k odběru událostí a zpracuje prvek Xamarin.Forms, ke kterému byl renderer připojen (OldElement) a element, ke kterému je renderer připojený (NewElement). Kromě toho jedna OnElementPropertyChanged metoda definuje operace, které se mají vyvolat, když dojde ke změně vlastnosti v ovládacím prvku pro různé platformy. .NET MAUI tento přístup zjednodušuje, takže každá změna vlastností je zpracována samostatnou metodou, a proto je kód pro vytvoření ovládacího prvku platformy, provedení nastavení ovládacího prvku a vyčištění ovládacího prvku oddělen do odlišných metod.

Proces migrace vlastního ovládacího prvku Xamarin.Forms, který je založen na vlastních rendererech na každé platformě na vlastní ovládací prvek .NET MAUI, který je založen na obslužné rutině na každé platformě, je následující:

  1. Vytvořte třídu pro multiplatformní řízení, která poskytuje veřejné rozhraní API ovládacího prvku. Další informace najdete v tématu Vytvoření víceplatformového ovládacího prvku.
  2. Vytvořte partial třídu obslužné rutiny. Další informace naleznete v tématu Vytvoření obslužné rutiny.
  3. Ve třídě obslužné rutiny vytvořte PropertyMapper slovník, který definuje akce, které se mají provést při změně vlastností pro různé platformy. Další informace naleznete v tématu Vytvoření mapovače vlastností.
  4. Vytvořte partial třídy obslužné rutiny pro každou platformu, která vytváří nativní zobrazení, která implementují řízení napříč platformami. Další informace najdete v tématu Vytvoření ovládacích prvků platformy.
  5. Zaregistrujte obslužnou rutinu pomocí ConfigureMauiHandlers metod a AddHandler metod ve třídě vaší aplikace MauiProgram . Další informace naleznete v tématu Registrace obslužné rutiny.

Pak je možné využívat řízení napříč platformami. Další informace najdete v tématu Využití řízení napříč platformami.

Alternativně lze vlastní renderery, které přizpůsobí ovládací prvky Xamarin.Forms, převést tak, aby upravovaly obslužné rutiny .NET MAUI. Další informace naleznete v tématu Přizpůsobení ovládacích prvků pomocí obslužných rutin.

Vytvoření ovládacího prvku pro různé platformy

Pokud chcete vytvořit ovládací prvek pro různé platformy, měli byste vytvořit třídu odvozenou z View:

namespace MyMauiControl.Controls
{
    public class CustomEntry : View
    {
        public static readonly BindableProperty TextProperty =
            BindableProperty.Create(nameof(Text), typeof(string), typeof(CustomEntry), null);

        public static readonly BindableProperty TextColorProperty =
            BindableProperty.Create(nameof(TextColor), typeof(Color), typeof(CustomEntry), null);

        public string Text
        {
            get { return (string)GetValue(TextProperty); }
            set { SetValue(TextProperty, value); }
        }

        public Color TextColor
        {
            get { return (Color)GetValue(TextColorProperty); }
            set { SetValue(TextColorProperty, value); }
        }
    }
}

Ovládací prvek by měl poskytovat veřejné rozhraní API, ke kterému bude přistupovat jeho obslužná rutina, a řídit uživatele. Ovládací prvky napříč platformami by měly být odvozeny od View, který představuje vizuální prvek, který se používá k umístění rozložení a zobrazení na obrazovce.

Vytvoření obslužné rutiny

Po vytvoření řízení napříč platformami byste měli vytvořit třídu pro obslužnou rutinu partial :

#if IOS || MACCATALYST
using PlatformView = Microsoft.Maui.Platform.MauiTextField;
#elif ANDROID
using PlatformView = AndroidX.AppCompat.Widget.AppCompatEditText;
#elif WINDOWS
using PlatformView = Microsoft.UI.Xaml.Controls.TextBox;
#elif (NETSTANDARD || !PLATFORM) || (NET6_0_OR_GREATER && !IOS && !ANDROID)
using PlatformView = System.Object;
#endif
using MyMauiControl.Controls;
using Microsoft.Maui.Handlers;

namespace MyMauiControl.Handlers
{
    public partial class CustomEntryHandler
    {
    }
}

Třída obslužné rutiny je částečná třída, jejíž implementace bude dokončena na každé platformě s další částečnou třídou.

Podmíněné using příkazy definují PlatformView typ na každé platformě. Konečný podmíněný using příkaz definuje PlatformView , že se System.Objectrovná . To je nezbytné, aby se PlatformView typ mohl použít v rámci obslužné rutiny pro použití na všech platformách. Alternativou by bylo definovat vlastnost jednou pro každou platformu PlatformView pomocí podmíněné kompilace.

Vytvoření mapovače vlastností

Každá obslužná rutina obvykle poskytuje mapovač vlastností, který definuje, jaké akce se mají provést, když dojde ke změně vlastnosti v ovládacím prvku pro různé platformy. Typ PropertyMapper je typ Dictionary , který mapuje vlastnosti ovládacího prvku pro různé platformy na přidružené akce.

Poznámka:

Mapovač vlastností je náhradou za metodu OnElementPropertyChanged ve vlastním rendereru Xamarin.Forms.

PropertyMapper je definován v obecné ViewHandler třídě .NET MAUI a vyžaduje, aby byly zadány dva obecné argumenty:

  • Třída pro řízení napříč platformami, která je odvozena od View.
  • Třída obslužné rutiny.

Následující příklad kódu ukazuje CustomEntryHandler třídu rozšířenou o definici PropertyMapper :

public partial class CustomEntryHandler
{
    public static PropertyMapper<CustomEntry, CustomEntryHandler> PropertyMapper = new PropertyMapper<CustomEntry, CustomEntryHandler>(ViewHandler.ViewMapper)
    {
        [nameof(CustomEntry.Text)] = MapText,
        [nameof(CustomEntry.TextColor)] = MapTextColor
    };

    public CustomEntryHandler() : base(PropertyMapper)
    {
    }
}

Jedná PropertyMapper se o Dictionary klíč, jehož klíč je string a jehož hodnota je obecný Action. Představuje string název vlastnosti ovládacího prvku pro různé platformy a Action představuje metodu static , která vyžaduje obslužnou rutinu a řízení mezi platformami jako argumenty. Například podpis MapText metody je public static void MapText(CustomEntryHandler handler, CustomEntry view).

Každá obslužná rutina platformy musí poskytovat implementace akcí, které manipulují s rozhraními API nativního zobrazení. Tím se zajistí, že když je vlastnost nastavena v ovládacím prvku pro různé platformy, podkladové nativní zobrazení se podle potřeby aktualizuje. Výhodou tohoto přístupu je, že umožňuje snadné přizpůsobení řízení napříč platformami, protože mapovač vlastností může být upraven spotřebiteli řízení napříč platformami bez podtřídy. Další informace naleznete v tématu Přizpůsobení ovládacích prvků pomocí obslužných rutin.

Vytvoření ovládacích prvků platformy

Po vytvoření mapovačů pro obslužnou rutinu musíte poskytnout implementace obslužné rutiny na všech platformách. Toho lze dosáhnout přidáním dílčích implementací obslužné rutiny tříd do podřízených složek složky Platformy . Případně můžete projekt nakonfigurovat tak, aby podporoval cílení na více názvů souborů nebo více cílení na složky nebo obojí.

Cílení na více názvů souborů je nakonfigurováno přidáním následujícího KÓDU XML do souboru projektu jako podřízených položek <Project> uzlu:

<!-- Android -->
<ItemGroup Condition="$(TargetFramework.StartsWith('net8.0-android')) != true">
  <Compile Remove="**\*.Android.cs" />
  <None Include="**\*.Android.cs" Exclude="$(DefaultItemExcludes);$(DefaultExcludesInProjectFolder)" />
</ItemGroup>

<!-- iOS and Mac Catalyst -->
<ItemGroup Condition="$(TargetFramework.StartsWith('net8.0-ios')) != true AND $(TargetFramework.StartsWith('net8.0-maccatalyst')) != true">
  <Compile Remove="**\*.MaciOS.cs" />
  <None Include="**\*.MaciOS.cs" Exclude="$(DefaultItemExcludes);$(DefaultExcludesInProjectFolder)" />
</ItemGroup>

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

Další informace o konfiguraci cílení na více verzí najdete v tématu Konfigurace cílení na více verzí.

Každá třída obslužné rutiny platformy by měla být částečnou třídou a odvozena od obecné ViewHandler třídy, která vyžaduje dva argumenty typu:

  • Třída pro řízení napříč platformami, která je odvozena od View.
  • Typ nativního zobrazení, který implementuje multiplatformní řízení na platformě. To by mělo být stejné jako typ PlatformView vlastnosti v obslužné rutině.

Důležité

Třída ViewHandler poskytuje VirtualView a PlatformView vlastnosti. Vlastnost VirtualView se používá pro přístup k ovládacímu prvku pro různé platformy z jeho obslužné rutiny. Tato PlatformView vlastnost se používá pro přístup k nativnímu zobrazení na každé platformě, která implementuje řízení napříč platformami.

Každá implementace obslužné rutiny platformy by měla přepsat následující metody:

  • CreatePlatformView, který by měl vytvořit a vrátit nativní zobrazení, které implementuje řízení napříč platformami.
  • ConnectHandler, který by měl provést jakékoli nastavení nativního zobrazení, jako je inicializace nativního zobrazení a provádění odběrů událostí.
  • DisconnectHandler, který by měl provést jakékoli nativní vyčištění zobrazení, jako je například zrušení odběru událostí a odstraňování objektů. Tato metoda není záměrně vyvolána rozhraním .NET MAUI. Místo toho ho musíte vyvolat sami z vhodného umístění v životním cyklu vaší aplikace. Další informace naleznete v tématu Nativní vyčištění zobrazení.

Poznámka:

, CreatePlatformViewConnectHandlera DisconnectHandler přepsání jsou nahrazení metody OnElementChanged ve vlastním rendereru Xamarin.Forms.

Každá obslužná rutina platformy by také měla implementovat akce definované ve slovníkech mapperu. Kromě toho by každá obslužná rutina platformy měla také poskytnout kód, jak je potřeba, aby implementovaly funkce řízení napříč platformami na platformě. Pro složitější ovládací prvky, které lze poskytnout dalším typem.

Následující příklad ukazuje implementaci v Androidu CustomEntryHandler :

#nullable enable
using AndroidX.AppCompat.Widget;
using Microsoft.Maui.Handlers;
using Microsoft.Maui.Platform;
using MyMauiControl.Controls;

namespace MyMauiControl.Handlers
{
    public partial class CustomEntryHandler : ViewHandler<CustomEntry, AppCompatEditText>
    {
        protected override AppCompatEditText CreatePlatformView() => new AppCompatEditText(Context);

        protected override void ConnectHandler(AppCompatEditText platformView)
        {
            base.ConnectHandler(platformView);

            // Perform any control setup here
        }

        protected override void DisconnectHandler(AppCompatEditText platformView)
        {
            // Perform any native view cleanup here
            platformView.Dispose();
            base.DisconnectHandler(platformView);
        }

        public static void MapText(CustomEntryHandler handler, CustomEntry view)
        {
            handler.PlatformView.Text = view.Text;
            handler.PlatformView?.SetSelection(handler.PlatformView?.Text?.Length ?? 0);
        }

        public static void MapTextColor(CustomEntryHandler handler, CustomEntry view)
        {
            handler.PlatformView?.SetTextColor(view.TextColor.ToPlatform());
        }
    }
}

CustomEntryHandler odvozuje z ViewHandler třídy, s obecným CustomEntry argumentem určujícím typ ovládacího prvku pro různé platformy a AppCompatEditText argument určující typ nativního ovládacího prvku.

Přepsání CreatePlatformView vytvoří a vrátí AppCompatEditText objekt. Přepsání ConnectHandler je umístění pro provedení požadovaného nastavení nativního zobrazení. Přepsání DisconnectHandler je umístění pro provedení nativního čištění zobrazení, a proto volá metodu Dispose v AppCompatEditText instanci.

Obslužná rutina také implementuje Akce definované ve slovníku mapper vlastnosti. Každá akce se provádí v reakci na vlastnost, která se mění v ovládacím prvku pro různé platformy, a je metoda, která vyžaduje obslužnou static rutinu a instance řízení napříč platformami jako argumenty. V každém případě akce volá metody definované v nativním ovládacím prvku.

Registrace obslužné rutiny

Před použitím vlastního ovládacího prvku a jeho obslužné rutiny musí být registrovány v aplikaci. K tomu by mělo dojít v CreateMauiApp metodě ve MauiProgram třídě v projektu aplikace, což je vstupní bod aplikace pro různé platformy:

using Microsoft.Extensions.Logging;
using MyMauiControl.Controls;
using MyMauiControl.Handlers;

namespace MyMauiControl;

public static class MauiProgram
{
  public static MauiApp CreateMauiApp()
  {
    var builder = MauiApp.CreateBuilder();
    builder
      .UseMauiApp<App>()
      .ConfigureFonts(fonts =>
      {
        fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
        fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold");
      })
      .ConfigureMauiHandlers(handlers =>
      {
        handlers.AddHandler(typeof(CustomEntry), typeof(CustomEntryHandler));
      });

#if DEBUG
    builder.Logging.AddDebug();
#endif

    return builder.Build();
  }
}

Obslužná rutina je zaregistrována pomocí ConfigureMauiHandlers metody a AddHandler metody. Prvním argumentem AddHandler metody je typ ovládacího prvku pro různé platformy, přičemž druhým argumentem je jeho typ obslužné rutiny.

Poznámka:

Tento přístup registrace zabraňuje skenování sestavení Xamarin.Forms, což je pomalé a nákladné.

Využívání multiplatformních ovládacích prvků

Po registraci obslužné rutiny ve vaší aplikaci se pak dá využívat řízení mezi platformami:

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:controls="clr-namespace:MyMauiControl.Controls"
             x:Class="MyMauiControl.MainPage">
    <Grid>
        <controls:CustomEntry Text="Hello world"
                              TextColor="Blue" />
    </Grid>
</ContentPage>

Vyčištění nativního zobrazení

Implementace obslužné rutiny DisconnectHandler každé platformy přepíše implementaci, která se používá k provádění nativního čištění zobrazení, jako je zrušení odběru událostí a odstraňování objektů. Toto přepsání však záměrně nevyvolává rozhraní .NET MAUI. Místo toho ho musíte vyvolat sami z vhodného umístění v životním cyklu vaší aplikace. To může být v případě, že stránka obsahující ovládací prvek přejde pryč, což způsobí vyvolání události stránky Unloaded .

Obslužnou rutinu události pro událost stránky Unloaded je možné zaregistrovat v XAML:

<ContentPage ...
             xmlns:controls="clr-namespace:MyMauiControl.Controls"
             Unloaded="ContentPage_Unloaded">
    <Grid>
        <controls:CustomEntry x:Name="customEntry"
                              ... />
    </Grid>
</ContentPage>

Obslužná rutina události události Unloaded pak může vyvolat metodu DisconnectHandler ve své Handler instanci:

void ContentPage_Unloaded(object sender, EventArgs e)
{
    customEntry.Handler?.DisconnectHandler();
}

Viz také