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 :
- 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.
- Créez une
partial
classe de gestionnaire. Pour plus d’informations, consultez Créer le gestionnaire. - 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.
- 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. - 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é jointeHandlerProperties.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.