Partager via


Migrer un renderer personnalisé Xamarin.Forms vers un gestionnaire MAUI .NET

Dans Xamarin.Forms, les renderers personnalisés peuvent être utilisés pour personnaliser l’apparence et le comportement d’un contrôle et créer de nouveaux contrôles multiplateformes. Chaque renderer personnalisé a une référence au contrôle multiplateforme et s’appuie souvent sur INotifyPropertyChanged l’envoi de notifications de modification de propriété. Au lieu d’utiliser des renderers personnalisés, l’interface utilisateur d’application multiplateforme .NET (.NET MAUI) introduit un nouveau concept appelé gestionnaire.

Les gestionnaires offrent de nombreuses améliorations des performances par rapport aux renderers personnalisés. Dans Xamarin.Forms, la ViewRenderer classe crée un élément parent. Par exemple, sur Android, un ViewGroup élément est créé pour les tâches de positionnement auxiliaires. Dans .NET MAUI, la ViewHandler<TVirtualView,TPlatformView> classe ne crée pas d’élément parent, ce qui permet de réduire la taille de la hiérarchie visuelle et d’améliorer les performances de votre application. Les gestionnaires dissocient également les contrôles de plateforme de l’infrastructure. Le contrôle de plateforme doit uniquement gérer les besoins de l’infrastructure. Ce n’est pas seulement plus efficace, mais il est beaucoup plus facile d’étendre ou de remplacer si nécessaire. Les gestionnaires sont également adaptés à la réutilisation par d’autres frameworks tels que Comet et Fabulous. Pour obtenir plus d’informations sur les gestionnaires, consultez Gestionnaires.

Dans Xamarin.Forms, la OnElementChanged méthode d’un renderer personnalisé crée le contrôle de plateforme, initialise les valeurs par défaut, s’abonne aux événements et gère l’élément Xamarin.Forms auquel le renderer a été attaché (OldElement) et l’élément auquel le renderer est attaché (NewElement). En outre, une méthode unique OnElementPropertyChanged définit les opérations à appeler lorsqu’une modification de propriété se produit dans le contrôle multiplateforme. .NET MAUI simplifie cette approche, afin que chaque modification de propriété soit gérée par une méthode distincte, et que le code permettant de créer le contrôle de plateforme, d’effectuer une configuration de contrôle et d’effectuer un nettoyage de contrôle, soit séparé en méthodes distinctes.

Le processus de migration d’un contrôle personnalisé Xamarin.Forms soutenu par des renderers personnalisés sur chaque plateforme vers un contrôle personnalisé .NET MAUI soutenu par un gestionnaire sur chaque plateforme est le suivant :

  1. Créez une classe pour le contrôle multiplateforme, qui fournit l’API publique du contrôle. Pour plus d’informations, consultez Créer le contrôle multiplateforme.
  2. Créez une partial classe de gestionnaire. Pour plus d’informations, consultez Créer le gestionnaire.
  3. Dans la classe de gestionnaire, créez un PropertyMapper dictionnaire, qui définit les actions à entreprendre lorsque des modifications de propriété interplateformes se produisent. Pour plus d’informations, consultez Créer le mappeur de propriétés.
  4. Créez des partial classes de gestionnaire pour chaque plateforme qui créent les vues natives qui implémentent le contrôle multiplateforme. Pour plus d’informations, consultez Créer les contrôles de plateforme.
  5. Inscrivez le gestionnaire à l’aide des méthodes et AddHandler des ConfigureMauiHandlers méthodes dans la classe de MauiProgram votre application. Pour plus d’informations, consultez Inscrire le gestionnaire.

Ensuite, le contrôle multiplateforme peut être consommé. Pour plus d’informations, consultez Utiliser le contrôle multiplateforme.

Vous pouvez également convertir des renderers personnalisés qui personnalisent les contrôles Xamarin.Forms afin qu’ils modifient les gestionnaires MAUI .NET. Pour plus d’informations, consultez Personnaliser les contrôles avec des gestionnaires.

Créer le contrôle multiplateforme

Pour créer un contrôle multiplateforme, vous devez créer une classe qui dérive de 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); }
        }
    }
}

Le contrôle doit fournir une API publique accessible par son gestionnaire et contrôler les consommateurs. Les contrôles multiplateformes doivent dériver Viewd’un élément visuel utilisé pour placer des dispositions et des vues à l’écran.

Créer le gestionnaire

Après avoir créé votre contrôle multiplateforme, vous devez créer une partial classe pour votre gestionnaire :

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

La classe de gestionnaire est une classe partielle dont l’implémentation est terminée sur chaque plateforme avec une classe partielle supplémentaire.

Les instructions conditionnelles using définissent le PlatformView type sur chaque plateforme. L’instruction conditionnelle using finale définit PlatformView comme égal à System.Object. Cela est nécessaire pour que le PlatformView type puisse être utilisé dans le gestionnaire pour l’utilisation sur toutes les plateformes. L’alternative consisterait à définir la PlatformView propriété une fois par plateforme, à l’aide de la compilation conditionnelle.

Créer le mappeur de propriétés

Chaque gestionnaire fournit généralement un mappeur de propriétés, qui définit les actions à entreprendre lorsqu’une modification de propriété se produit dans le contrôle multiplateforme. Le PropertyMapper type est un Dictionary qui mappe les propriétés du contrôle multiplateforme à leurs actions associées.

Remarque

Le mappeur de propriétés est le remplacement de la OnElementPropertyChanged méthode dans un renderer personnalisé Xamarin.Forms.

PropertyMapper est défini dans la classe de ViewHandler<TVirtualView,TPlatformView> .NET MAUI et nécessite deux arguments génériques à fournir :

  • Classe pour le contrôle multiplateforme, qui dérive de View.
  • Classe du gestionnaire.

L’exemple de code suivant montre la CustomEntryHandler classe étendue avec la PropertyMapper définition :

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

Il PropertyMapper s’agit d’une Dictionary clé dont la clé est un string et dont la valeur est un générique Action. Représente string le nom de la propriété du contrôle multiplateforme et Action représente une static méthode qui requiert le gestionnaire et le contrôle multiplateforme en tant qu’arguments. Par exemple, la signature de la MapText méthode est public static void MapText(CustomEntryHandler handler, CustomEntry view).

Chaque gestionnaire de plateforme doit fournir des implémentations des actions, qui manipulent les API de vue native. Cela garantit que lorsqu’une propriété est définie sur un contrôle multiplateforme, la vue native sous-jacente est mise à jour en fonction des besoins. L’avantage de cette approche est qu’elle permet une personnalisation facile du contrôle multiplateforme, car le mappeur de propriétés peut être modifié par les consommateurs de contrôle multiplateforme sans sous-classe. Pour plus d’informations, consultez Personnaliser les contrôles avec des gestionnaires.

Créer les contrôles de plateforme

Après avoir créé les mappeurs pour votre gestionnaire, vous devez fournir des implémentations de gestionnaire sur toutes les plateformes. Pour ce faire, ajoutez des implémentations de gestionnaires de classes partielles dans les dossiers enfants du dossier Plateformes . Vous pouvez également configurer votre projet pour prendre en charge le multi-ciblage basé sur un nom de fichier ou le multi-ciblage basé sur des dossiers, ou les deux.

Le multi-ciblage basé sur un nom de fichier est configuré en ajoutant le code XML suivant au fichier projet, en tant qu’enfants du <Project> nœud :

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

Pour plus d’informations sur la configuration de multi-ciblage, consultez Configurer le multi-ciblage.

Chaque classe de gestionnaire de plateforme doit être une classe partielle et dériver de la ViewHandler<TVirtualView,TPlatformView> classe, ce qui nécessite deux arguments de type :

  • Classe pour le contrôle multiplateforme, qui dérive de View.
  • Type de vue native qui implémente le contrôle multiplateforme sur la plateforme. Cela doit être identique au type de la PlatformView propriété dans le gestionnaire.

Important

La ViewHandler<TVirtualView,TPlatformView> classe fournit et PlatformView propriétésVirtualView. La VirtualView propriété est utilisée pour accéder au contrôle multiplateforme à partir de son gestionnaire. La PlatformView propriété est utilisée pour accéder à la vue native sur chaque plateforme qui implémente le contrôle multiplateforme.

Chacune des implémentations de gestionnaire de plateforme doit remplacer les méthodes suivantes :

  • CreatePlatformView, qui doit créer et retourner la vue native qui implémente le contrôle multiplateforme.
  • ConnectHandler, qui doit effectuer une configuration de vue native, telle que l’initialisation de l’affichage natif et l’exécution d’abonnements aux événements.
  • DisconnectHandler, qui doit effectuer tout nettoyage d’affichage natif, tel que l’annulation de l’abonnement à partir d’événements et la suppression d’objets. Cette méthode n’est intentionnellement pas appelée par .NET MAUI. Au lieu de cela, vous devez l’appeler vous-même à partir d’un emplacement approprié dans le cycle de vie de votre application. Pour plus d’informations, consultez nettoyage en mode natif.
  • CreatePlatformView, qui doit créer et retourner la vue native qui implémente le contrôle multiplateforme.
  • ConnectHandler, qui doit effectuer une configuration de vue native, telle que l’initialisation de l’affichage natif et l’exécution d’abonnements aux événements.
  • DisconnectHandler, qui doit effectuer tout nettoyage d’affichage natif, tel que l’annulation de l’abonnement à partir d’événements et la suppression d’objets. Cette méthode est appelée automatiquement par .NET MAUI par défaut, bien que ce comportement puisse être modifié. Pour plus d’informations, consultez Déconnexion du gestionnaire de contrôle.

Remarque

Les CreatePlatformViewremplacements ConnectHandleret DisconnectHandler les remplacements sont les remplacements de la OnElementChanged méthode dans un renderer personnalisé Xamarin.Forms.

Chaque gestionnaire de plateforme doit également implémenter les actions définies dans les dictionnaires du mappeur. En outre, chaque gestionnaire de plateforme doit également fournir du code, selon les besoins, pour implémenter les fonctionnalités du contrôle multiplateforme sur la plateforme. Vous pouvez également fournir des contrôles plus complexes par un type supplémentaire.

L’exemple suivant montre l’implémentation CustomEntryHandler sur Android :

#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 dérive de la ViewHandler<TVirtualView,TPlatformView> classe, avec l’argument générique CustomEntry spécifiant le type de contrôle multiplateforme et l’argument AppCompatEditText spécifiant le type de contrôle natif.

Le CreatePlatformView remplacement crée et retourne un AppCompatEditText objet. Le ConnectHandler remplacement est l’emplacement pour effectuer toute configuration d’affichage native requise. Le DisconnectHandler remplacement est l’emplacement pour effectuer un nettoyage d’affichage natif, et appelle donc la Dispose méthode sur l’instance AppCompatEditText .

Le gestionnaire implémente également les actions définies dans le dictionnaire du mappeur de propriétés. Chaque action est exécutée en réponse à une modification de propriété sur le contrôle multiplateforme et est une static méthode qui nécessite des instances de gestionnaire et de contrôle multiplateforme en tant qu’arguments. Dans chaque cas, l’action appelle des méthodes définies sur le contrôle natif.

Inscrire le gestionnaire

Un contrôle personnalisé et son gestionnaire doivent être inscrits auprès d’une application, avant de pouvoir être consommés. Cela doit se produire dans la méthode CreateMauiApp de la classe MauiProgram dans votre projet d’application, qui est le point d’entrée multiplateforme de l’application :

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

Le gestionnaire est inscrit avec la méthode et AddHandler la ConfigureMauiHandlers méthode. Le premier argument de la AddHandler méthode est le type de contrôle multiplateforme, le deuxième argument étant son type de gestionnaire.

Remarque

Cette approche d’inscription évite l’analyse d’assembly de Xamarin.Forms, qui est lente et coûteuse.

Utiliser le contrôle multiplateforme

Après avoir inscrit le gestionnaire auprès de votre application, le contrôle multiplateforme peut ensuite être consommé :

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

Nettoyage de la vue native

L’implémentation du gestionnaire de chaque plateforme remplace l’implémentation DisconnectHandler , qui est utilisée pour effectuer un nettoyage d’affichage natif, comme l’annulation de l’envoi d’événements et l’élimination d’objets. Toutefois, cette substitution n’est intentionnellement pas appelée par .NET MAUI. Au lieu de cela, vous devez l’appeler vous-même à partir d’un emplacement approprié dans le cycle de vie de votre application. Cela peut être lorsque la page contenant le contrôle est éloignée, ce qui entraîne le levée de l’événement de la Unloaded page.

Un gestionnaire d’événements pour l’événement de Unloaded la page peut être inscrit en XAML :

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

Le gestionnaire d’événements de l’événement Unloaded peut ensuite appeler la DisconnectHandler méthode sur son Handler instance :

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

Déconnexion du gestionnaire de contrôle

L’implémentation du gestionnaire de chaque plateforme remplace l’implémentation DisconnectHandler , qui est utilisée pour effectuer un nettoyage d’affichage natif, comme l’annulation de l’envoi d’événements et l’élimination d’objets. Par défaut, les gestionnaires se déconnectent automatiquement de leurs contrôles, par exemple lors de la navigation vers l’arrière dans une application.

Dans certains scénarios, vous pouvez contrôler quand un gestionnaire se déconnecte de son contrôle, ce qui peut être obtenu avec la HandlerProperties.DisconnectPolicy propriété jointe. Cette propriété nécessite un HandlerDisconnectPolicy argument, avec l’énumération définissant les valeurs suivantes :

  • Automatic, qui indique que le gestionnaire sera déconnecté automatiquement. Ceci est la valeur par défaut de la propriété jointe HandlerProperties.DisconnectPolicy.
  • Manual, qui indique que le gestionnaire doit être déconnecté manuellement en appelant l’implémentation DisconnectHandler() .

L’exemple suivant montre comment définir la propriété jointe HandlerProperties.DisconnectPolicy :

<controls:CustomEntry x:Name="customEntry"
                      Text="Hello world"
                      TextColor="Blue"
                      HandlerProperties.DisconnectPolicy="Manual" />             

Lorsque vous définissez la propriété jointe sur Manual vous-même, vous devez appeler l’implémentation HandlerProperties.DisconnectPolicy du DisconnectHandler gestionnaire à partir d’un emplacement approprié dans le cycle de vie de votre application. Cela peut être obtenu en appelant customEntry.Handler?.DisconnectHandler();.

En outre, il existe une méthode d’extension DisconnectHandlers qui déconnecte les gestionnaires d’un élément IView donné :

video.DisconnectHandlers();

Lors d’une déconnexion, la méthode DisconnectHandlers se propage dans l’arborescence de contrôle jusqu’à ce qu’elle se termine ou qu’elle atteigne un contrôle qui a défini une stratégie manuelle.

Voir aussi