Personnalisation d’un ListView
Un Xamarin.Forms ListView est un affichage qui affiche une collection de données sous forme de liste verticale. Cet article montre comment créer un renderer personnalisé qui encapsule des contrôles de liste spécifiques à la plateforme et des dispositions de cellule natives afin de mieux contrôler les performances des contrôles de liste natifs.
Chaque Xamarin.Forms vue a un renderer associé pour chaque plateforme qui crée une instance d’un contrôle natif. Lorsqu’une ListView
application est rendue Xamarin.Forms , dans iOS, la ListViewRenderer
classe est instanciée, ce qui instancie à son tour un contrôle natif UITableView
. Sur la plateforme Android, la classe ListViewRenderer
instancie un contrôle ListView
natif. Sur la plateforme Windows universelle (UWP), la classe ListViewRenderer
instancie un contrôle ListView
natif. Pour plus d’informations sur les classes de renderer et de contrôle natives qui contrôlent Xamarin.Forms la correspondance, consultez Les classes de base du renderer et les contrôles natifs.
Le diagramme suivant illustre la relation entre le contrôle ListView
et les contrôles natifs correspondants qui l’implémentent :
Vous pouvez tirer profit du processus de rendu pour implémenter des personnalisations spécifiques à la plateforme en créant un renderer personnalisé pour un ListView
sur chaque plateforme. Le processus pour y parvenir est le suivant :
- Créez un Xamarin.Forms contrôle personnalisé.
- Consommez le contrôle personnalisé à partir de Xamarin.Forms.
- Créez le renderer personnalisé pour le contrôle sur chaque plateforme.
Nous allons à présent présenter chaque élément à tour de rôle pour implémenter un renderer NativeListView
qui tire parti de contrôles de liste spécifiques à la plateforme et de dispositions de cellule natives. Ce scénario est utile dans le cadre du portage d’une application native existante qui contient du code de liste et de cellule pouvant être réutilisé. Il est également possible de personnaliser en détail les fonctionnalités de contrôle de liste qui peuvent affecter les performances, notamment la virtualisation des données.
Création du contrôle ListView personnalisé
Vous pouvez créer un contrôle ListView
personnalisé en utilisant une sous-classe de la classe ListView
, comme indiqué dans l’exemple de code suivant :
public class NativeListView : ListView
{
public static readonly BindableProperty ItemsProperty =
BindableProperty.Create ("Items", typeof(IEnumerable<DataSource>), typeof(NativeListView), new List<DataSource> ());
public IEnumerable<DataSource> Items {
get { return (IEnumerable<DataSource>)GetValue (ItemsProperty); }
set { SetValue (ItemsProperty, value); }
}
public event EventHandler<SelectedItemChangedEventArgs> ItemSelected;
public void NotifyItemSelected (object item)
{
if (ItemSelected != null) {
ItemSelected (this, new SelectedItemChangedEventArgs (item));
}
}
}
NativeListView
est créée dans le projet de bibliothèque .NET Standard et définit l’API pour le contrôle personnalisé. Ce contrôle expose une propriété Items
qui est utilisée pour remplir ListView
de données et qui peut faire l’objet d’une liaison de données à des fins d’affichage. Il expose également un événement ItemSelected
qui est déclenché chaque fois qu’un élément est sélectionné dans un contrôle de liste natif spécifique à la plateforme. Pour plus d’informations sur la liaison de données, consultez Notions de base de la liaison de données.
Consommation du contrôle personnalisé
Vous pouvez référencer le contrôle personnalisé NativeListView
en XAML dans le projet de bibliothèque .NET Standard en déclarant un espace de noms pour son emplacement et en utilisant le préfixe d’espace de noms sur le contrôle. L’exemple de code suivant montre comment le contrôle personnalisé NativeListView
peut être consommé par une page XAML :
<ContentPage ...
xmlns:local="clr-namespace:CustomRenderer;assembly=CustomRenderer"
...>
...
<ContentPage.Content>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Label Text="{x:Static local:App.Description}" HorizontalTextAlignment="Center" />
<local:NativeListView Grid.Row="1" x:Name="nativeListView" ItemSelected="OnItemSelected" VerticalOptions="FillAndExpand" />
</Grid>
</ContentPage.Content>
</ContentPage>
Le préfixe d’espace de noms local
peut porter n’importe quel nom. Toutefois, les valeurs clr-namespace
et assembly
doivent correspondre aux détails du contrôle personnalisé. Une fois l’espace de noms déclaré, le préfixe est utilisé pour référencer le contrôle personnalisé.
L’exemple de code suivant montre comment le contrôle personnalisé NativeListView
peut être consommé par une page C# :
public class MainPageCS : ContentPage
{
NativeListView nativeListView;
public MainPageCS()
{
nativeListView = new NativeListView
{
Items = DataSource.GetList(),
VerticalOptions = LayoutOptions.FillAndExpand
};
switch (Device.RuntimePlatform)
{
case Device.iOS:
Padding = new Thickness(0, 20, 0, 0);
break;
case Device.Android:
case Device.UWP:
Padding = new Thickness(0);
break;
}
Content = new Grid
{
RowDefinitions = {
new RowDefinition { Height = GridLength.Auto },
new RowDefinition { Height = new GridLength (1, GridUnitType.Star) }
},
Children = {
new Label { Text = App.Description, HorizontalTextAlignment = TextAlignment.Center },
nativeListView
}
};
nativeListView.ItemSelected += OnItemSelected;
}
...
}
Le contrôle personnalisé NativeListView
utilise des renderers personnalisés spécifiques à la plateforme pour afficher une liste de données, laquelle est remplie par le biais de la propriété Items
. Chaque ligne dans la liste contient trois éléments de données : un nom, une catégorie et un nom de fichier image. La disposition de chaque ligne dans la liste est définie par le renderer personnalisé spécifique à la plateforme.
Remarque
Étant donné que le contrôle personnalisé NativeListView
est restitué à l’aide de contrôles de liste spécifiques à la plateforme qui prennent en charge le défilement, le contrôle personnalisé ne doit pas être hébergé dans des contrôles de disposition avec défilement comme ScrollView
.
Un renderer personnalisé peut maintenant être ajouté à chaque projet d’application pour créer des contrôles de liste spécifiques à la plateforme et des dispositions de cellule natives.
Création du renderer personnalisé sur chaque plateforme
Le processus de création de la classe de renderer personnalisé est le suivant :
- Créez une sous-classe de la classe
ListViewRenderer
qui restitue le contrôle personnalisé. - Remplacez la méthode
OnElementChanged
qui restitue le contrôle personnalisé et écrivez la logique pour le personnaliser. Cette méthode est appelée lorsque le correspondant Xamarin.FormsListView
est créé. - Ajoutez un
ExportRenderer
attribut à la classe de renderer personnalisée pour spécifier qu’il sera utilisé pour afficher le Xamarin.Forms contrôle personnalisé. Cet attribut est utilisé pour inscrire le renderer personnalisé avec Xamarin.Forms.
Remarque
Il est facultatif de fournir un renderer personnalisé dans chaque projet de plateforme. Si un renderer personnalisé n’est pas inscrit, le renderer par défaut de la classe de base de la cellule est utilisé.
Le diagramme suivant illustre les responsabilités de chaque projet dans l’exemple d’application ainsi que les relations qu’ils entretiennent les uns avec les autres :
Le contrôle personnalisé NativeListView
est restitué par des classes de renderer spécifiques à la plateforme qui dérivent toutes de la classe ListViewRenderer
pour chaque plateforme. Chaque contrôle personnalisé NativeListView
est donc restitué avec des contrôles de liste spécifiques à la plateforme et des dispositions de cellule natives, comme le montrent les captures d’écran suivantes :
La ListViewRenderer
classe expose la OnElementChanged
méthode, appelée lorsque le Xamarin.Forms contrôle personnalisé est créé pour afficher le contrôle natif correspondant. Cette méthode prend un paramètre ElementChangedEventArgs
qui contient les propriétés OldElement
et NewElement
. Ces propriétés représentent l’élément Xamarin.Forms auquel le renderer a été attaché, et l’élément Xamarin.Forms auquel le renderer est attaché, respectivement. Dans l’exemple d’application, la propriété OldElement
sera null
et la propriété NewElement
contiendra une référence à l’instance NativeListView
.
Une version substituée de la méthode OnElementChanged
, dans chaque classe de renderer spécifique à la plateforme, est l’emplacement où effectuer la personnalisation du contrôle natif. Une référence typée au contrôle natif en cours d’utilisation sur la plateforme est accessible par le biais de la propriété Control
. En outre, une référence au Xamarin.Forms contrôle en cours de rendu peut être obtenue via la Element
propriété.
Faites preuve de vigilance quand vous vous abonnez à des gestionnaires d’événements dans la méthode OnElementChanged
, comme le montre l’exemple de code suivant :
protected override void OnElementChanged (ElementChangedEventArgs<Xamarin.Forms.ListView> e)
{
base.OnElementChanged (e);
if (e.OldElement != null) {
// Unsubscribe from event handlers and cleanup any resources
}
if (e.NewElement != null) {
// Configure the native control and subscribe to event handlers
}
}
Le contrôle natif doit uniquement être configuré et 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é qui n’est pas affecté par des fuites de mémoire.
Une version substituée de la méthode, dans chaque classe de renderer spécifique à la OnElementPropertyChanged
plateforme, est l’endroit où répondre aux modifications de propriété pouvant être liées sur le Xamarin.Forms contrôle personnalisé. Une vérification de la propriété changée doit toujours être effectuée, car cette substitution peut être appelée plusieurs fois.
Chaque classe de renderer personnalisée est décorée avec un ExportRenderer
attribut qui inscrit le renderer avec Xamarin.Forms. L’attribut prend deux paramètres : le nom de type du Xamarin.Forms contrôle personnalisé en cours de rendu et le nom de type du renderer personnalisé. Le préfixe assembly
de l’attribut spécifie que l’attribut s’applique à la totalité de l’assembly.
Les sections suivantes décrivent l’implémentation de chaque classe de renderer personnalisé spécifique à la plateforme.
Création du renderer personnalisé sur iOS
L’exemple de code suivant illustre le renderer personnalisé pour la plateforme iOS :
[assembly: ExportRenderer (typeof(NativeListView), typeof(NativeiOSListViewRenderer))]
namespace CustomRenderer.iOS
{
public class NativeiOSListViewRenderer : ListViewRenderer
{
protected override void OnElementChanged (ElementChangedEventArgs<Xamarin.Forms.ListView> e)
{
base.OnElementChanged (e);
if (e.OldElement != null) {
// Unsubscribe
}
if (e.NewElement != null) {
Control.Source = new NativeiOSListViewSource (e.NewElement as NativeListView);
}
}
}
}
Le UITableView
contrôle est configuré en créant une instance de la NativeiOSListViewSource
classe, à condition que le renderer personnalisé soit attaché à un nouvel Xamarin.Forms élément. Cette classe fournit des données au contrôle UITableView
en substituant les méthodes RowsInSection
et GetCell
de la classe UITableViewSource
et en exposant une propriété Items
qui contient la liste des données à afficher. La classe fournit également une substitution de méthode RowSelected
qui appelle l’événement ItemSelected
fourni par le contrôle personnalisé NativeListView
. Pour plus d’informations sur les substitutions de méthode, consultez Utilisation d’une sous-classe de UITableViewSource. La méthode GetCell
retourne un UITableCellView
dont chaque ligne dans la liste est remplie de données, comme illustré dans l’exemple de code suivant :
public override UITableViewCell GetCell (UITableView tableView, NSIndexPath indexPath)
{
// request a recycled cell to save memory
NativeiOSListViewCell cell = tableView.DequeueReusableCell (cellIdentifier) as NativeiOSListViewCell;
// if there are no cells to reuse, create a new one
if (cell == null) {
cell = new NativeiOSListViewCell (cellIdentifier);
}
if (String.IsNullOrWhiteSpace (tableItems [indexPath.Row].ImageFilename)) {
cell.UpdateCell (tableItems [indexPath.Row].Name
, tableItems [indexPath.Row].Category
, null);
} else {
cell.UpdateCell (tableItems [indexPath.Row].Name
, tableItems [indexPath.Row].Category
, UIImage.FromFile ("Images/" + tableItems [indexPath.Row].ImageFilename + ".jpg"));
}
return cell;
}
Cette méthode crée une instance NativeiOSListViewCell
pour chaque ligne de données affichée à l’écran. L’instance NativeiOSCell
définit la disposition de chaque cellule et les données de la cellule. Quand une cellule disparaît de l’écran pour cause de défilement, elle est disponible pour réutilisation. Le fait d’avoir uniquement des instances NativeiOSCell
pour les données affichées à l’écran, et non pour toutes les données dans la liste, évite le gaspillage de mémoire. Pour plus d’informations sur la réutilisation de cellules, consultez Réutilisation de cellules. La méthode GetCell
lit également la propriété ImageFilename
de chaque ligne de données, à condition qu’elle existe, puis lit l’image et la stocke en tant qu’instance UIImage
avant de mettre à jour l’instance NativeiOSListViewCell
avec les données (nom, catégorie et image) pour la ligne.
La classe NativeiOSListViewCell
définit la disposition pour chaque cellule et est illustrée dans l’exemple de code suivant :
public class NativeiOSListViewCell : UITableViewCell
{
UILabel headingLabel, subheadingLabel;
UIImageView imageView;
public NativeiOSListViewCell (NSString cellId) : base (UITableViewCellStyle.Default, cellId)
{
SelectionStyle = UITableViewCellSelectionStyle.Gray;
ContentView.BackgroundColor = UIColor.FromRGB (218, 255, 127);
imageView = new UIImageView ();
headingLabel = new UILabel () {
Font = UIFont.FromName ("Cochin-BoldItalic", 22f),
TextColor = UIColor.FromRGB (127, 51, 0),
BackgroundColor = UIColor.Clear
};
subheadingLabel = new UILabel () {
Font = UIFont.FromName ("AmericanTypewriter", 12f),
TextColor = UIColor.FromRGB (38, 127, 0),
TextAlignment = UITextAlignment.Center,
BackgroundColor = UIColor.Clear
};
ContentView.Add (headingLabel);
ContentView.Add (subheadingLabel);
ContentView.Add (imageView);
}
public void UpdateCell (string caption, string subtitle, UIImage image)
{
headingLabel.Text = caption;
subheadingLabel.Text = subtitle;
imageView.Image = image;
}
public override void LayoutSubviews ()
{
base.LayoutSubviews ();
headingLabel.Frame = new CoreGraphics.CGRect (5, 4, ContentView.Bounds.Width - 63, 25);
subheadingLabel.Frame = new CoreGraphics.CGRect (100, 18, 100, 20);
imageView.Frame = new CoreGraphics.CGRect (ContentView.Bounds.Width - 63, 5, 33, 33);
}
}
Cette classe définit les contrôles permettant de restituer le contenu de la cellule et sa disposition. Le constructeur NativeiOSListViewCell
crée des instances des contrôles UILabel
et UIImageView
, et initialise leur apparence. Ces contrôles sont utilisés pour afficher les données de chaque ligne, la méthode UpdateCell
étant utilisée pour définir ces données sur les instances UILabel
et UIImageView
. L’emplacement de ces instances est défini par la méthode substituée LayoutSubviews
en spécifiant leurs coordonnées au sein de la cellule.
Réponse à un changement de propriété sur le contrôle personnalisé
Si la propriété NativeListView.Items
change en raison de l’ajout d’éléments à la liste ou de leur suppression, le renderer personnalisé doit répondre en affichant les changements. Cet objectif peut être atteint par la substitution de la méthode OnElementPropertyChanged
, ce qu’illustre l’exemple de code suivant :
protected override void OnElementPropertyChanged (object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged (sender, e);
if (e.PropertyName == NativeListView.ItemsProperty.PropertyName) {
Control.Source = new NativeiOSListViewSource (Element as NativeListView);
}
}
La méthode crée une instance de la classe NativeiOSListViewSource
qui fournit des données au contrôle UITableView
, à condition que la propriété pouvant être liée NativeListView.Items
ait changé.
Création du renderer personnalisé sur Android
L’exemple de code suivant illustre le renderer personnalisé pour la plateforme Android :
[assembly: ExportRenderer(typeof(NativeListView), typeof(NativeAndroidListViewRenderer))]
namespace CustomRenderer.Droid
{
public class NativeAndroidListViewRenderer : ListViewRenderer
{
Context _context;
public NativeAndroidListViewRenderer(Context context) : base(context)
{
_context = context;
}
protected override void OnElementChanged(ElementChangedEventArgs<Xamarin.Forms.ListView> e)
{
base.OnElementChanged(e);
if (e.OldElement != null)
{
// unsubscribe
Control.ItemClick -= OnItemClick;
}
if (e.NewElement != null)
{
// subscribe
Control.Adapter = new NativeAndroidListViewAdapter(_context as Android.App.Activity, e.NewElement as NativeListView);
Control.ItemClick += OnItemClick;
}
}
...
void OnItemClick(object sender, Android.Widget.AdapterView.ItemClickEventArgs e)
{
((NativeListView)Element).NotifyItemSelected(((NativeListView)Element).Items.ToList()[e.Position - 1]);
}
}
}
Le contrôle natif ListView
est configuré à condition que le renderer personnalisé soit attaché à un nouvel Xamarin.Forms élément. Cette configuration implique la création d’une instance de la classe NativeAndroidListViewAdapter
qui fournit des données au contrôle natif ListView
et l’inscription d’un gestionnaire d’événements pour traiter l’événement ItemClick
. Ce gestionnaire appelle à son tour l’événement ItemSelected
fourni par le contrôle personnalisé NativeListView
. L’événement ItemClick
est annulé si l’élément auquel le Xamarin.Forms renderer est attaché aux modifications.
Le NativeAndroidListViewAdapter
dérive de la classe BaseAdapter
. Il expose une propriété Items
qui contient la liste des données à afficher et substitue les méthodes Count
, GetView
, GetItemId
et this[int]
. Pour plus d’informations sur ces substitutions de méthode, consultez Implémentation de ListAdapter. La méthode GetView
retourne une vue pour chaque ligne, remplie de données, comme illustré dans l’exemple de code suivant :
public override View GetView (int position, View convertView, ViewGroup parent)
{
var item = tableItems [position];
var view = convertView;
if (view == null) {
// no view to re-use, create new
view = context.LayoutInflater.Inflate (Resource.Layout.NativeAndroidListViewCell, null);
}
view.FindViewById<TextView> (Resource.Id.Text1).Text = item.Name;
view.FindViewById<TextView> (Resource.Id.Text2).Text = item.Category;
// grab the old image and dispose of it
if (view.FindViewById<ImageView> (Resource.Id.Image).Drawable != null) {
using (var image = view.FindViewById<ImageView> (Resource.Id.Image).Drawable as BitmapDrawable) {
if (image != null) {
if (image.Bitmap != null) {
//image.Bitmap.Recycle ();
image.Bitmap.Dispose ();
}
}
}
}
// If a new image is required, display it
if (!String.IsNullOrWhiteSpace (item.ImageFilename)) {
context.Resources.GetBitmapAsync (item.ImageFilename).ContinueWith ((t) => {
var bitmap = t.Result;
if (bitmap != null) {
view.FindViewById<ImageView> (Resource.Id.Image).SetImageBitmap (bitmap);
bitmap.Dispose ();
}
}, TaskScheduler.FromCurrentSynchronizationContext ());
} else {
// clear the image
view.FindViewById<ImageView> (Resource.Id.Image).SetImageBitmap (null);
}
return view;
}
La méthode GetView
est appelée pour retourner la cellule à restituer, en tant que View
, pour chaque ligne de données dans la liste. Une instance View
est créée pour chaque ligne de données affichée à l’écran, l’apparence de l’instance View
étant définie dans un fichier de disposition. Quand une cellule disparaît de l’écran pour cause de défilement, elle est disponible pour réutilisation. Le fait d’avoir uniquement des instances View
pour les données affichées à l’écran, et non pour toutes les données dans la liste, évite le gaspillage de mémoire. Pour plus d’informations sur la réutilisation de vues, consultez Réutilisation de vues de ligne.
La méthode GetView
remplit également l’instance View
. Elle lit notamment les données d’image à partir du fichier dont le nom est spécifié dans la propriété ImageFilename
.
La disposition de chaque cellule affichée par le ListView
natif est définie dans le fichier de disposition NativeAndroidListViewCell.axml
, lequel est augmenté par la méthode LayoutInflater.Inflate
. L’exemple de code suivant illustre la définition de disposition :
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:padding="8dp"
android:background="@drawable/CustomSelector">
<LinearLayout
android:id="@+id/Text"
android:orientation="vertical"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingLeft="10dip">
<TextView
android:id="@+id/Text1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#FF7F3300"
android:textSize="20dip"
android:textStyle="italic" />
<TextView
android:id="@+id/Text2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="14dip"
android:textColor="#FF267F00"
android:paddingLeft="100dip" />
</LinearLayout>
<ImageView
android:id="@+id/Image"
android:layout_width="48dp"
android:layout_height="48dp"
android:padding="5dp"
android:src="@drawable/icon"
android:layout_alignParentRight="true" />
</RelativeLayout>
Cette disposition spécifie que deux contrôles TextView
et un contrôle ImageView
sont utilisés pour afficher le contenu de la cellule. Les deux contrôles TextView
sont orientés verticalement dans un contrôle LinearLayout
, tandis que tous les contrôles sont contenus dans une RelativeLayout
.
Réponse à un changement de propriété sur le contrôle personnalisé
Si la propriété NativeListView.Items
change en raison de l’ajout d’éléments à la liste ou de leur suppression, le renderer personnalisé doit répondre en affichant les changements. Cet objectif peut être atteint par la substitution de la méthode OnElementPropertyChanged
, ce qu’illustre l’exemple de code suivant :
protected override void OnElementPropertyChanged (object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged (sender, e);
if (e.PropertyName == NativeListView.ItemsProperty.PropertyName) {
Control.Adapter = new NativeAndroidListViewAdapter (_context as Android.App.Activity, Element as NativeListView);
}
}
La méthode crée une instance de la classe NativeAndroidListViewAdapter
qui fournit des données au contrôle natif ListView
, à condition que la propriété pouvant être liée NativeListView.Items
ait changé.
Création du renderer personnalisé sur UWP
L’exemple de code suivant illustre le renderer personnalisé pour UWP :
[assembly: ExportRenderer(typeof(NativeListView), typeof(NativeUWPListViewRenderer))]
namespace CustomRenderer.UWP
{
public class NativeUWPListViewRenderer : ListViewRenderer
{
ListView listView;
protected override void OnElementChanged(ElementChangedEventArgs<Xamarin.Forms.ListView> e)
{
base.OnElementChanged(e);
listView = Control as ListView;
if (e.OldElement != null)
{
// Unsubscribe
listView.SelectionChanged -= OnSelectedItemChanged;
}
if (e.NewElement != null)
{
listView.SelectionMode = ListViewSelectionMode.Single;
listView.IsItemClickEnabled = false;
listView.ItemsSource = ((NativeListView)e.NewElement).Items;
listView.ItemTemplate = App.Current.Resources["ListViewItemTemplate"] as Windows.UI.Xaml.DataTemplate;
// Subscribe
listView.SelectionChanged += OnSelectedItemChanged;
}
}
void OnSelectedItemChanged(object sender, SelectionChangedEventArgs e)
{
((NativeListView)Element).NotifyItemSelected(listView.SelectedItem);
}
}
}
Le contrôle natif ListView
est configuré à condition que le renderer personnalisé soit attaché à un nouvel Xamarin.Forms élément. Cette configuration nécessite la définition du mode de réponse du contrôle natif ListView
aux éléments sélectionnés, le remplissage des données affichées par le contrôle, la définition de l’apparence et du contenu de chaque cellule, et l’inscription d’un gestionnaire d’événements pour traiter l’événement SelectionChanged
. Ce gestionnaire appelle à son tour l’événement ItemSelected
fourni par le contrôle personnalisé NativeListView
. L’événement SelectionChanged
est annulé si l’élément auquel le Xamarin.Forms renderer est attaché aux modifications.
L’apparence et le contenu de chaque cellule native ListView
sont définis par un DataTemplate
nommé ListViewItemTemplate
. DataTemplate
est stocké dans le dictionnaire de ressources de niveau application et est illustré dans l’exemple de code suivant :
<DataTemplate x:Key="ListViewItemTemplate">
<Grid Background="#DAFF7F">
<Grid.Resources>
<local:ConcatImageExtensionConverter x:Name="ConcatImageExtensionConverter" />
</Grid.Resources>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="0.40*" />
<ColumnDefinition Width="0.40*"/>
<ColumnDefinition Width="0.20*" />
</Grid.ColumnDefinitions>
<TextBlock Grid.ColumnSpan="2" Foreground="#7F3300" FontStyle="Italic" FontSize="22" VerticalAlignment="Top" Text="{Binding Name}" />
<TextBlock Grid.RowSpan="2" Grid.Column="1" Foreground="#267F00" FontWeight="Bold" FontSize="12" VerticalAlignment="Bottom" Text="{Binding Category}" />
<Image Grid.RowSpan="2" Grid.Column="2" HorizontalAlignment="Left" VerticalAlignment="Center" Source="{Binding ImageFilename, Converter={StaticResource ConcatImageExtensionConverter}}" Width="50" Height="50" />
<Line Grid.Row="1" Grid.ColumnSpan="3" X1="0" X2="1" Margin="30,20,0,0" StrokeThickness="1" Stroke="LightGray" Stretch="Fill" VerticalAlignment="Bottom" />
</Grid>
</DataTemplate>
DataTemplate
spécifie les contrôles permettant d’afficher le contenu de la cellule ainsi que leurs disposition et apparence. Deux contrôles TextBlock
et un contrôle Image
sont utilisés pour afficher le contenu de la cellule par le biais de la liaison de données. En outre, une instance de ConcatImageExtensionConverter
est utilisée pour concaténer l’extension de fichier .jpg
à chaque nom de fichier image. Ainsi, le contrôle Image
peut charger et restituer l’image quand sa propriété Source
est définie.
Réponse à un changement de propriété sur le contrôle personnalisé
Si la propriété NativeListView.Items
change en raison de l’ajout d’éléments à la liste ou de leur suppression, le renderer personnalisé doit répondre en affichant les changements. Cet objectif peut être atteint par la substitution de la méthode OnElementPropertyChanged
, ce qu’illustre l’exemple de code suivant :
protected override void OnElementPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
if (e.PropertyName == NativeListView.ItemsProperty.PropertyName)
{
listView.ItemsSource = ((NativeListView)Element).Items;
}
}
La méthode remplit à nouveau le contrôle natif ListView
des données ayant changé, à condition que la propriété pouvant être liée NativeListView.Items
ait changé.
Résumé
Dans cet article, nous avons vu comment créer un renderer personnalisé qui encapsule des contrôles de liste spécifiques à la plateforme et des dispositions de cellule natives afin de mieux contrôler les performances des contrôles de liste natifs.