Améliorer les Xamarin.Forms performances des applications
Évolution 2016 : Optimisation des performances des applications avec Xamarin.Forms
Pour une application, la dégradation des performances peut se traduire de différentes façons. L’application semble ne pas répondre, le défilement de l’affichage est ralenti et la durée de vie de la batterie de l’appareil réduite. Toutefois, l’optimisation des performances implique davantage de choses que l’implémentation d’un code efficace. L’expérience utilisateur liée au niveau de performance de l’application doit également être prise en compte. Par exemple, pour contribuer à améliorer l’expérience utilisateur, vous devez vérifier que les opérations s’exécutent sans empêcher l’utilisateur d’effectuer d’autres activités.
Il existe de nombreuses techniques pour augmenter les performances et les performances perçues des Xamarin.Forms applications. Ensemble, ces techniques peuvent considérablement réduire la charge de travail d’un processeur, de même que la quantité de mémoire consommée par une application.
Remarque
Avant de lire cet article, lisez d’abord Niveau de performance multiplateforme, qui décrit les techniques indépendantes des plateformes permettant améliorer l’utilisation de la mémoire et les performances des applications générées à l’aide de la plateforme Xamarin.
Activer le compilateur XAML
XAML peut être éventuellement compilé directement en langage intermédiaire (IL) avec le compilateur XAML (XAMLC). XAMLC offre un certain nombre d’avantages :
- Il effectue une vérification au moment de la compilation du code XAML et informe l’utilisateur des erreurs rencontrées.
- Il supprime une partie du temps de chargement et d’instanciation pour les éléments XAML.
- Il permet de réduire la taille de fichier de l’assembly final en n’incluant plus les fichiers .xaml.
XAMLC est activé par défaut dans les nouvelles Xamarin.Forms solutions. Il peut toutefois être nécessaire de l’activer dans des solutions plus anciennes. Pour plus d’informations, consultez Compilation XAML.
Utiliser des liaisons compilées
Les liaisons compilées améliorent les performances de liaison de données dans les applications en Xamarin.Forms résolvant les expressions de liaison au moment de la compilation, plutôt qu’au moment de l’exécution avec réflexion. La compilation d’une expression de liaison génère du code compilé qui résout généralement une liaison 8 à 20 fois plus rapidement qu’avec une liaison classique. Pour plus d’informations, consultez Liaisons compilées.
Réduire les liaisons inutiles
N’utilisez pas de liaisons pour le contenu qui peut être aisément défini de manière statique. Il n’existe aucun avantage à lier des données qui n’ont pas besoin de l’être, car les liaisons ne sont pas rentables. Par exemple, le paramètre Button.Text = "Accept"
a moins de surcharge que la liaison Button.Text
à une propriété string
viewmodel avec la valeur « Accepter ».
Utiliser des renderers rapides
Les convertisseurs rapides réduisent l’inflation et les coûts de rendu des Xamarin.Forms contrôles sur Android en aplatissant la hiérarchie de contrôle native résultante. Cette technique améliore davantage les performances en créant moins d’objets, ce qui aboutit à une arborescence d’éléments visuels moins complexe et à une utilisation moindre de la mémoire.
À partir de Xamarin.Forms la version 4.0, toutes les applications ciblant l’utilisation FormsAppCompatActivity
de renderers rapides par défaut. Pour plus d’informations, consultez Renderers rapides.
Activer le suivi de démarrage sur Android
La compilation Ahead Of Time (AOT) sur Android réduit la charge de démarrage de l’application juste-à-temps (JIT) et l’utilisation de la mémoire, au détriment de la création d’un APK de plus grande taille. Une alternative consiste à utiliser le suivi de démarrage, qui fournit un compromis entre taille d’APK Android et temps de démarrage par rapport à la compilation AOT conventionnelle.
Au lieu de compiler autant d’application que possible pour le code non managé, le suivi de démarrage compile uniquement l’ensemble de méthodes managées qui représentent les parties les plus coûteuses du démarrage de l’application dans une application vide Xamarin.Forms . Cette approche entraîne une réduction de la taille de l’APK par rapport à la compilation AOT conventionnelle, tout en fournissant des améliorations similaires au démarrage.
Activer la compression des dispositions
La compression des dispositions supprime les dispositions spécifiées de l’arborescence d’éléments visuels dans le but d’améliorer les performances de rendu des pages. Les avantages en matière de performances de cette technique varient selon la complexité d’une page, la version du système d’exploitation utilisé et l’appareil sur lequel l’application est en cours d’exécution. Toutefois, les gains en termes de performances les plus importants seront visibles sur les appareils les plus anciens. Pour plus d’informations, consultez Compression des dispositions.
Choisir la disposition correcte
Une disposition qui est capable d’afficher plusieurs enfants, mais qui n’en a qu’un seul, est inutile. Par exemple, l’exemple de code suivant montre un élément StackLayout
avec un seul enfant :
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="DisplayImage.HomePage">
<StackLayout>
<Image Source="waterfront.jpg" />
</StackLayout>
</ContentPage>
Cela est inutile et l’élément StackLayout
doit être supprimé, comme indiqué dans l’exemple de code suivant :
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="DisplayImage.HomePage">
<Image Source="waterfront.jpg" />
</ContentPage>
En outre, n’essayez pas de reproduire l’apparence d’une disposition spécifique à l’aide de combinaisons d’autres dispositions, car cela aboutirait à des calculs de dispositions inutiles. Par exemple, n’essayez pas de reproduire une disposition Grid
à l’aide d’une combinaison d’instances de StackLayout
. L’exemple de code ci-dessous illustre cette mauvaise pratique :
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Details.HomePage"
Padding="0,20,0,0">
<StackLayout>
<StackLayout Orientation="Horizontal">
<Label Text="Name:" />
<Entry Placeholder="Enter your name" />
</StackLayout>
<StackLayout Orientation="Horizontal">
<Label Text="Age:" />
<Entry Placeholder="Enter your age" />
</StackLayout>
<StackLayout Orientation="Horizontal">
<Label Text="Occupation:" />
<Entry Placeholder="Enter your occupation" />
</StackLayout>
<StackLayout Orientation="Horizontal">
<Label Text="Address:" />
<Entry Placeholder="Enter your address" />
</StackLayout>
</StackLayout>
</ContentPage>
Cela ne sert à rien, car des calculs de dispositions inutiles sont effectués. Au lieu de cela, vous pouvez obtenir la disposition souhaitée à l’aide d’un élément Grid
, comme illustré dans l’exemple de code suivant :
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Details.HomePage"
Padding="0,20,0,0">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="30" />
<RowDefinition Height="30" />
<RowDefinition Height="30" />
<RowDefinition Height="30" />
</Grid.RowDefinitions>
<Label Text="Name:" />
<Entry Grid.Column="1" Placeholder="Enter your name" />
<Label Grid.Row="1" Text="Age:" />
<Entry Grid.Row="1" Grid.Column="1" Placeholder="Enter your age" />
<Label Grid.Row="2" Text="Occupation:" />
<Entry Grid.Row="2" Grid.Column="1" Placeholder="Enter your occupation" />
<Label Grid.Row="3" Text="Address:" />
<Entry Grid.Row="3" Grid.Column="1" Placeholder="Enter your address" />
</Grid>
</ContentPage>
Optimiser les performances de disposition
Pour obtenir les meilleures performances de disposition possibles, suivez ces instructions :
- Réduisez la profondeur des hiérarchies de dispositions en spécifiant les valeurs de propriété
Margin
, ce qui permet de créer des dispositions avec moins de retours à la ligne. Pour plus d’informations, consultez Marges et remplissage. - Quand vous utilisez un élément
Grid
, essayez de vous assurer que le moins de lignes et de colonnes possible sont définies sur une tailleAuto
. Pour chaque ligne ou colonne dimensionnée automatiquement, le moteur de disposition effectue des calculs de dispositions supplémentaires. Utilisez plutôt des lignes et colonnes de taille fixe si possible. Vous pouvez également définir des lignes et des colonnes pour occuper un montant proportionnel d’espace avec la valeur d’énumérationGridUnitType.Star
, à condition que l’arborescence parente respecte ces instructions de disposition. - Ne définissez pas les propriétés
VerticalOptions
etHorizontalOptions
d’une disposition, sauf si cela est obligatoire. Les valeurs par défaut deLayoutOptions.Fill
etLayoutOptions.FillAndExpand
permettent la meilleure optimisation de la disposition. La modification de ces propriétés a un coût et consomme de la mémoire, même en leur affectant les valeurs par défaut. - Évitez d’utiliser un élément
RelativeLayout
autant que possible. Le processeur aurait considérablement plus de travail à effectuer. - Quand vous utilisez un élément
AbsoluteLayout
, évitez d’utiliser la propriétéAbsoluteLayout.AutoSize
autant que possible. - Quand vous utilisez un élément
StackLayout
, vérifiez qu’un seul enfant est défini surLayoutOptions.Expands
. Cette propriété permet de garantir que l’enfant spécifié occupe le plus grand espace que l’élémentStackLayout
peut lui donner, et il est inutile d’effectuer ces calculs plusieurs fois. - Évitez d’appeler une des méthodes de la classe
Layout
, car elles entraînent l’exécution de calculs de dispositions coûteux. Au lieu de cela, il est probable que vous puissiez obtenir le comportement de disposition souhaité en définissant les propriétésTranslationX
etTranslationY
. Vous pouvez également sous-classer la classeLayout<View>
pour obtenir le comportement de disposition souhaité. - Ne mettez à jour aucune instance de
Label
plus fréquemment que nécessaire, car la modification de la taille de l’étiquette peut entraîner le recalcul de l’intégralité de la disposition de l’écran. - Ne définissez pas la propriété
Label.VerticalTextAlignment
, sauf si cela est obligatoire. - Affectez à l’élément
LineBreakMode
des instances deLabel
la valeurNoWrap
autant que possible.
Utiliser la programmation asynchrone
La réactivité globale de votre application peut être améliorée et les goulots d’étranglement des performances sont souvent évités à l’aide de la programmation asynchrone. Dans .NET, le Modèle asynchrone basé sur les tâches (TAP) est le modèle de conception recommandé pour les opérations asynchrones. Toutefois, une utilisation incorrecte du TAP peut entraîner des applications non performantes. Par conséquent, les instructions suivantes doivent être suivies lors de l’utilisation du TAP.
Notions de base
Comprendre le cycle de vie des tâches, représenté par l’énumération
TaskStatus
. Pour plus d’informations, consultez La signification de TaskStatus et État de la tâche.Utilisez la méthode
Task.WhenAll
pour attendre de façon asynchrone que plusieurs opérations asynchrones se terminent, plutôt queawait
individuellement une série d’opérations asynchrones. Pour en savoir plus, consultez Task.WhenAll.Utilisez la méthode
Task.WhenAny
pour attendre de façon asynchrone la fin de l’une des opérations asynchrones. Pour en savoir plus, consultez Task.WhenAny.Utilisez la méthode
Task.Delay
pour produire un objetTask
qui se termine après l’heure spécifiée. Cela est utile pour les scénarios tels que l’interrogation des données et le retard de gestion des entrées utilisateur pendant un temps prédéterminé. Pour en savoir plus, consultez Task.Delay.Exécutez des opérations intensives de processeur synchrone sur le pool de threads avec la méthode
Task.Run
. Cette méthode est un raccourci pour la méthodeTaskFactory.StartNew
, avec les arguments les plus optimaux définis. Pour en savoir plus, consultez Task.Run.Évitez d’essayer de créer des constructeurs asynchrones. Utilisez plutôt des événements de cycle de vie ou une logique d’initialisation distincte pour
await
correctement toute initialisation. Pour plus d’informations, consultez Constructeurs asynchrones sur blog.stephencleary.com.Utilisez le modèle de tâche différé pour éviter d’attendre que les opérations asynchrones se terminent au démarrage de l’application. Pour plus d'informations, consultez AsyncLazy.
Créez un enveloppeur de tâches pour les opérations asynchrones existantes, qui n’utilisent pas le TAP, en créant des objets
TaskCompletionSource<T>
. Ces objets bénéficient des avantages de la programmabilitéTask
et vous permettent de contrôler la durée de vie et l’achèvement duTask
associé. Pour plus d’informations, consultez La nature de TaskCompletionSource.Retourne un objet
Task
, au lieu de renvoyer un objetTask
attendu, lorsqu’il n’est pas nécessaire de traiter le résultat d’une opération asynchrone. Cela est plus performant en raison d’un basculement de contexte inférieur en cours d’exécution.Utilisez la bibliothèque de flux de données TPL (Task Parallel Library) dans des scénarios tels que le traitement des données à mesure qu’elles sont disponibles ou lorsque vous avez plusieurs opérations qui doivent communiquer entre elles de manière asynchrone. Pour plus d’informations, consultez Flux de données (bibliothèque parallèle de tâches).
UI
Appelez une version asynchrone d’une API, si elle est disponible. Ainsi, le thread d’interface utilisateur reste non bloqué, ce qui améliore l’expérience utilisateur.
Mettez à jour les éléments de l’interface utilisateur avec des données provenant d’opérations asynchrones sur le thread d’interface utilisateur, afin d’éviter les exceptions levées. Toutefois, les mises à jour de la propriété
ListView.ItemsSource
sont automatiquement marshalées vers le thread d’interface utilisateur. Pour plus d’informations sur la détermination du code en cours d’exécution sur le thread d’interface utilisateur, consultez Xamarin.Essentials: MainThread.Important
Toutes les propriétés de contrôle mises à jour via la liaison de données sont automatiquement marshalées sur le thread d’interface utilisateur.
Gestion des erreurs
- En savoir plus sur la gestion des exceptions asynchrones. Les exceptions non gérées levées par le code qui s’exécutent de manière asynchrone sont propagées vers le thread appelant, sauf dans certains scénarios. Pour plus d’informations, consultez Gestion des exceptions (bibliothèque parallèle de tâches).
- Évitez de créer des méthodes
async void
et créez plutôt des méthodesasync Task
. Celles-ci facilitent la gestion des erreurs, la composabilité et la testabilité. L’exception à cette directive est des gestionnaires d’événements asynchrones, qui doivent retournervoid
. Pour plus d’informations, consultez Éviter Async Void. - Ne mélangez pas le blocage et le code asynchrone en appelant les méthodes
Task.Wait
,Task.Result
ouGetAwaiter().GetResult
, car elles peuvent entraîner un blocage. Toutefois, si cette directive doit être violée, l’approche recommandée consiste à appeler la méthodeGetAwaiter().GetResult
, car elle conserve les exceptions de tâche. Pour plus d’informations, consultez Async All the Way et Gestion des exceptions de tâche dans .NET 4.5. - Utilisez la méthode dans la mesure du possible pour créer du code
ConfigureAwait
sans contexte. Le code sans contexte offre de meilleures performances pour les applications mobiles et est une technique utile pour éviter l’interblocage lors de l’utilisation d’une base de code partiellement asynchrone. Pour plus d’informations, consultez Configurer le contexte. - Utilisez des tâches de continuation pour des fonctionnalités telles que la gestion des exceptions levées par l’opération asynchrone précédente et l’annulation d’une continuation avant son démarrage ou pendant son exécution. Pour plus d’informations, voir Chaînage des tâches à l’aide de tâches continues.
- Utilisez une implémentation
ICommand
asynchrone lorsque des opérations asynchrones sont appelées à partir duICommand
. Cela garantit que toutes les exceptions de la logique de commande asynchrone peuvent être gérées. Pour plus d’informations, consultez Programmation asynchrone : Modèles pour les applications MVVM asynchrones : Commandes.
Choisir un conteneur d’injection de dépendances avec précaution
Les conteneurs d’injection de dépendances introduisent des contraintes de performances supplémentaires dans les applications mobiles. L’inscription et la résolution des types avec un conteneur ont un coût en termes de performances en raison de l’utilisation par le conteneur de la réflexion pour créer chaque type, et plus particulièrement si les dépendances sont reconstruites pour chaque navigation entre les pages de l’application. S’il existe de nombreuses dépendances ou des dépendances profondes, le coût de création peut augmenter de manière significative. En outre, l’inscription de type, qui se produit généralement au démarrage de l’application, peut avoir un impact notable sur le temps de démarrage selon le conteneur utilisé.
En guise d’alternative, l’injection de dépendances peut être rendue plus performante en l’implémentant manuellement à l’aide de fabriques.
Créer des applications d’interpréteur de commandes
Xamarin.Forms Les applications Shell offrent une expérience de navigation avisée basée sur les menus volants et les onglets. Si l’expérience utilisateur de votre application peut être implémentée avec l’interpréteur de commandes, il est préférable de le faire. Les applications d’interpréteur de commandes permettent d’éviter une mauvaise expérience de démarrage, car les pages sont créées à la demande en réponse à la navigation plutôt qu’au démarrage de l’application, ce qui est le cas avec les applications qui utilisent un « TabbedPage ». Pour plus d’informations, consultez Xamarin.Forms Shell.
Utiliser CollectionView au lieu de ListView
CollectionView
est une vue de présentation de listes de données à l’aide de différentes spécifications de disposition. Il offre une alternative plus flexible et plus performante à ListView
. Pour plus d’informations, consultez Xamarin.Forms CollectionView.
Optimiser les performances de ListView
Lorsque vous utilisez ListView
, un certain nombre d’expériences utilisateur doit être optimisé :
- Initialisation : intervalle de temps qui commence quand le contrôle est créé et se termine quand les éléments sont affichés à l’écran.
- Défilement : possibilité de faire défiler la liste et de vérifier que l’interface utilisateur est synchrone avec les entrées tactiles.
- Interaction pour l’ajout, la suppression et la sélection d’éléments.
Le contrôle ListView
requiert une application pour fournir des modèles de données et de cellules. Cette opération aura un impact important sur les performances du contrôle. Pour plus d’informations, consultez Performances de ListView.
Optimiser les ressources d’images
L’affichage de ressources d’images peut augmenter considérablement l’encombrement mémoire d’une application. Par conséquent, elles ne doivent être créées que si elles sont nécessaires, et doivent être libérées dès que l’application n’en a plus besoin. Par exemple, si une application affiche une image en lisant les données à partir d’un flux, vérifiez que ce flux est créé uniquement si nécessaire et qu’il est libéré quand il ne l’est plus. Pour cela, créez le flux quand la page est créée ou que l’événement Page.Appearing
se déclenche, puis supprimez-le quand l’événement Page.Disappearing
se déclenche.
Quand vous téléchargez une image à afficher avec la méthode ImageSource.FromUri
, mettez en cache l’image téléchargée en vérifiant que la propriété UriImageSource.CachingEnabled
a la valeur true
. Pour plus d’informations, consultez Utilisation des images.
Pour plus d’informations, consultez Optimiser les ressources d’images.
Réduire la taille de l’arborescence d’éléments visuels
La réduction du nombre d’éléments dans une page accélère l’affichage de la page. Pour ce faire, il existe deux techniques principales. La première consiste à masquer les éléments qui ne sont pas visibles. La propriété IsVisible
de chaque élément détermine si l’élément doit faire partie de l’arborescence d’éléments visuels ou pas. Par conséquent, si un élément n’est pas visible parce qu’il est masqué par d’autres éléments, supprimez l’élément ou affectez à sa propriété IsVisible
la valeur false
.
La seconde technique consiste à supprimer les éléments inutiles. Par exemple, l’exemple de code suivant illustre une mise en page contenant plusieurs objets Label
:
<StackLayout>
<StackLayout Padding="20,20,0,0">
<Label Text="Hello" />
</StackLayout>
<StackLayout Padding="20,20,0,0">
<Label Text="Welcome to the App!" />
</StackLayout>
<StackLayout Padding="20,20,0,0">
<Label Text="Downloading Data..." />
</StackLayout>
</StackLayout>
La même mise en page peut être maintenue avec un nombre réduit d’éléments, comme indiqué dans l’exemple de code suivant :
<StackLayout Padding="20,35,20,20" Spacing="25">
<Label Text="Hello" />
<Label Text="Welcome to the App!" />
<Label Text="Downloading Data..." />
</StackLayout>
Réduire la taille du dictionnaire de ressources de l’application
Toutes les ressources qui sont utilisées dans l’application doivent être stockées dans le dictionnaire de ressources de l’application pour éviter la duplication. Cela permet de réduire la quantité de code XAML qui doit être analysé dans toute l’application. L’exemple de code suivant illustre la ressource HeadingLabelStyle
, qui est utilisée au niveau de l’application et par conséquent définie dans le dictionnaire de ressources de l’application :
<Application xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Resources.App">
<Application.Resources>
<ResourceDictionary>
<Style x:Key="HeadingLabelStyle" TargetType="Label">
<Setter Property="HorizontalOptions" Value="Center" />
<Setter Property="FontSize" Value="Large" />
<Setter Property="TextColor" Value="Red" />
</Style>
</ResourceDictionary>
</Application.Resources>
</Application>
Toutefois, le code XAML qui est spécifique à une page ne doit pas être inclus dans le dictionnaire de ressources de l’application, car les ressources sont alors analysées au démarrage de l’application et non lorsqu’une page le demande. Si une ressource est utilisée par une page qui n’est pas la page de démarrage, elle doit être placée dans le dictionnaire de ressources pour cette page, ce qui contribue à réduire le code XAML qui est analysé quand l’application démarre. L’exemple de code suivant illustre la ressource HeadingLabelStyle
, qui figure uniquement dans une seule page et est par conséquent définie dans le dictionnaire de ressources de la page :
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Test.HomePage"
Padding="0,20,0,0">
<ContentPage.Resources>
<ResourceDictionary>
<Style x:Key="HeadingLabelStyle" TargetType="Label">
<Setter Property="HorizontalOptions" Value="Center" />
<Setter Property="FontSize" Value="Large" />
<Setter Property="TextColor" Value="Red" />
</Style>
</ResourceDictionary>
</ContentPage.Resources>
...
</ContentPage>
Pour plus d’informations sur les ressources d’application, consultez Styles XAML.
Utiliser le modèle de renderer personnalisé
La plupart des Xamarin.Forms classes de renderer exposent la OnElementChanged
méthode, appelée lorsqu’un Xamarin.Forms contrôle personnalisé est créé pour afficher le contrôle natif correspondant. Les classes de renderers personnalisés dans chaque projet de plateforme remplacent ensuite cette méthode pour instancier et personnaliser le contrôle natif. La méthode SetNativeControl
est utilisée pour instancier le contrôle natif, et cette méthode attribue également la référence de contrôle à la propriété Control
.
Toutefois, dans certains cas, la méthode OnElementChanged
peut être appelée plusieurs fois. Par conséquent, pour éviter les fuites de mémoire qui peuvent avoir un impact sur les performances, vous devez être vigilant lors de l’instanciation d’un nouveau contrôle natif. L’approche à utiliser lors de l’instanciation d’un nouveau contrôle natif dans un renderer personnalisé est indiquée dans l’exemple de code suivant :
protected override void OnElementChanged (ElementChangedEventArgs<NativeListView> e)
{
base.OnElementChanged (e);
if (e.OldElement != null)
{
// Unsubscribe from event handlers and cleanup any resources
}
if (e.NewElement != null)
{
if (Control == null)
{
// Instantiate the native control with the SetNativeControl method
}
// Configure the control and subscribe to event handlers
}
}
Un nouveau contrôle natif doit uniquement être instancié une seule fois, quand la propriété Control
a la valeur null
. En outre, le contrôle ne doit être créé, configuré et que les gestionnaires d’événements sont abonnés lorsque le renderer personnalisé est attaché à un nouvel Xamarin.Forms élément. De même, vous devez vous désabonner des gestionnaires d’événements auxquels vous vous êtes abonné uniquement quand l’élément auquel le renderer est attaché change. L’adoption de cette approche permet de créer un renderer personnalisé efficace qui ne connaît pas les fuites de mémoire.
Important
La méthode SetNativeControl
ne doit être appelée que si la propriété e.NewElement
n’est pas null
, et si la propriété Control
est null
.
Pour plus d’informations sur les renderers personnalisés, consultez Personnalisation des contrôles sur chaque plateforme.