Personalización de la clase ViewCell
ViewCell de Xamarin.Forms es una celda que se puede agregar a ListView o TableView y que contiene una vista definida por el desarrollador. En este artículo se muestra cómo crear un representador personalizado para un ViewCell que se hospeda dentro de un control ListView de Xamarin.Forms. Esto impide que se llame varias veces a los cálculos de diseño de Xamarin.Forms durante el desplazamiento de ListView.
Todas las celdas de Xamarin.Forms tienen un representador que las acompaña para cada plataforma y que crea una instancia de un control nativo. Cuando una aplicación de Xamarin.Forms representa un ViewCell
, se crea en iOS la instancia de la clase ViewCellRenderer
, que a su vez crea una instancia del control UITableViewCell
nativo. En la plataforma de Android, la clase ViewCellRenderer
crea una instancia de un control View
nativo. En la Plataforma universal de Windows (UWP), la clase ViewCellRenderer
crea una instancia de un elemento DataTemplate
nativo. Para obtener más información sobre el representador y las clases de control nativo a las que se asignan los controles de Xamarin.Forms, vea Clases base y controles nativos del representador.
El siguiente diagrama muestra la relación entre la clase ViewCell
y los controles nativos correspondientes que la implementan:
El proceso de representación puede aprovecharse para implementar las personalizaciones específicas de la plataforma creando un representador personalizado para una ViewCell
en cada plataforma. Para ello, siga este procedimiento:
- Cree una celda personalizada de Xamarin.Forms.
- Consuma la celda personalizada de Xamarin.Forms.
- Cree el representador personalizado para la celda en cada plataforma.
Cada elemento ahora se explicará a su vez, con el objetivo de implementar un representador NativeCell
que aproveche las ventajas de un diseño específico de la plataforma para cada celda que se hospeda dentro de un control Xamarin.FormsListView
. Esto impide que se llame varias veces a los cálculos de diseño de Xamarin.Forms durante el desplazamiento de ListView
.
Creación de la celda personalizada
Se puede crear un control de celda personalizado mediante la creación de subclases de la clase ViewCell
, como se muestra en el siguiente ejemplo de código:
public class NativeCell : ViewCell
{
public static readonly BindableProperty NameProperty =
BindableProperty.Create ("Name", typeof(string), typeof(NativeCell), "");
public string Name {
get { return (string)GetValue (NameProperty); }
set { SetValue (NameProperty, value); }
}
public static readonly BindableProperty CategoryProperty =
BindableProperty.Create ("Category", typeof(string), typeof(NativeCell), "");
public string Category {
get { return (string)GetValue (CategoryProperty); }
set { SetValue (CategoryProperty, value); }
}
public static readonly BindableProperty ImageFilenameProperty =
BindableProperty.Create ("ImageFilename", typeof(string), typeof(NativeCell), "");
public string ImageFilename {
get { return (string)GetValue (ImageFilenameProperty); }
set { SetValue (ImageFilenameProperty, value); }
}
}
La clase NativeCell
se crea en el proyecto de biblioteca de .NET Standard y define la API para la celda personalizada. La celda personalizada expone las propiedades Name
, Category
y ImageFilename
que se pueden mostrar mediante el enlace de datos. Para más información sobre el enlace de datos, consulte Data Binding Basics (Aspectos básicos del enlace de datos).
Consumo de la celda personalizada
En XAML puede hacerse referencia a la celda personalizada NativeCell
en el proyecto de biblioteca de .NET Standard. Para ello, se declara un espacio de nombres para su ubicación y se usa el prefijo del espacio de nombres en el elemento de celda personalizado. El siguiente ejemplo de código muestra cómo la celda personalizada NativeCell
puede utilizarse en una página XAML:
<ContentPage ...
xmlns:local="clr-namespace:CustomRenderer;assembly=CustomRenderer"
...>
...
<ContentPage.Content>
<StackLayout>
<Label Text="Xamarin.Forms native cell" HorizontalTextAlignment="Center" />
<ListView x:Name="listView" CachingStrategy="RecycleElement" ItemSelected="OnItemSelected">
<ListView.ItemTemplate>
<DataTemplate>
<local:NativeCell Name="{Binding Name}" Category="{Binding Category}" ImageFilename="{Binding ImageFilename}" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
</ContentPage.Content>
</ContentPage>
El prefijo de espacio de nombres local
puede tener cualquier nombre. Empero, los valores clr-namespace
y assembly
deben coincidir con los detalles del control personalizado. Una vez que se declare el espacio de nombres, el prefijo se utiliza para hacer referencia a la celda personalizada.
El siguiente ejemplo de código muestra cómo una página de C# puede consumir la celda personalizada NativeCell
:
public class NativeCellPageCS : ContentPage
{
ListView listView;
public NativeCellPageCS()
{
listView = new ListView(ListViewCachingStrategy.RecycleElement)
{
ItemsSource = DataSource.GetList(),
ItemTemplate = new DataTemplate(() =>
{
var nativeCell = new NativeCell();
nativeCell.SetBinding(NativeCell.NameProperty, "Name");
nativeCell.SetBinding(NativeCell.CategoryProperty, "Category");
nativeCell.SetBinding(NativeCell.ImageFilenameProperty, "ImageFilename");
return nativeCell;
})
};
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 StackLayout
{
Children = {
new Label { Text = "Xamarin.Forms native cell", HorizontalTextAlignment = TextAlignment.Center },
listView
}
};
listView.ItemSelected += OnItemSelected;
}
...
}
Un control ListView
de Xamarin.Forms se usa para mostrar una lista de los datos, que se rellena mediante la propiedad ItemSource
. La estrategia de almacenamiento en caché RecycleElement
intenta minimizar la velocidad de ejecución y el consumo de memoria de ListView
mediante el reciclaje de las celdas de la lista. Para obtener más información, vea Estrategia de almacenamiento en caché.
Cada fila de la lista contiene tres elementos de datos: un nombre, una categoría y un nombre de archivo de imagen. El diseño de cada fila de la lista está definido por el DataTemplate
al que se hace referencia mediante la propiedad enlazable ListView.ItemTemplate
. DataTemplate
define que cada fila de datos en la lista será una NativeCell
que muestra sus propiedades Name
, Category
y ImageFilename
mediante el enlace de datos. Para obtener más información sobre el control ListView
, vea ListView de Xamarin.Forms.
Ahora se puede agregar un representador personalizado a cada proyecto de aplicación para personalizar el diseño específico de la plataforma para cada celda.
Creación del representador personalizado en cada plataforma
El proceso de creación de la clase de representador personalizada es el siguiente:
- Cree una subclase de la clase
ViewCellRenderer
que represente la celda personalizada. - Invalide el método específico de la plataforma que representa la celda personalizada y escriba una lógica para personalizarla.
- Agregue un atributo
ExportRenderer
a la clase de representador personalizada para especificar que se usará para representar la celda personalizada de Xamarin.Forms. Este atributo se usa para registrar al representador personalizado con Xamarin.Forms.
Nota:
Para la mayoría de los elementos de Xamarin.Forms, proporcionar un representador personalizado en cada proyecto de la plataforma es un paso opcional. Si no se registra un representador personalizado, se usará el representador predeterminado de la clase base del control. Con todo, los representadores personalizados son necesarios en cada proyecto de la plataforma al representar un elemento ViewCell.
El siguiente diagrama muestra las responsabilidades de cada proyecto de la aplicación de ejemplo, junto con las relaciones entre ellos:
Las clases del representador específico de la plataforma, que se derivan de la clase ViewCellRenderer
para cada plataforma, representan la celda personalizada NativeCell
. Esto da como resultado que cada celda personalizada NativeCell
se represente con diseño específico de la plataforma, como se muestra en las siguientes capturas de pantalla:
La clase ViewCellRenderer
expone métodos específicos de la plataforma para representar la celda personalizada. Estos son el método GetCell
en la plataforma iOS, el método GetCellCore
en la plataforma Android y el método GetTemplate
en UWP.
Cada clase de representador personalizado se decora con un atributo ExportRenderer
que registra el representador con Xamarin.Forms. El atributo toma dos parámetros: el nombre de tipo de la celda de Xamarin.Forms que se representa y el nombre de tipo del representador personalizado. El prefijo assembly
del atributo especifica que el atributo se aplica a todo el ensamblado.
En las secciones siguientes se describe la implementación de cada clase de representador personalizado específico de plataforma.
Crear un representador personalizado en iOS
El ejemplo de código siguiente muestra el representador personalizado para la plataforma iOS:
[assembly: ExportRenderer(typeof(NativeCell), typeof(NativeiOSCellRenderer))]
namespace CustomRenderer.iOS
{
public class NativeiOSCellRenderer : ViewCellRenderer
{
NativeiOSCell cell;
public override UITableViewCell GetCell(Cell item, UITableViewCell reusableCell, UITableView tv)
{
var nativeCell = (NativeCell)item;
cell = reusableCell as NativeiOSCell;
if (cell == null)
cell = new NativeiOSCell(item.GetType().FullName, nativeCell);
else
cell.NativeCell.PropertyChanged -= OnNativeCellPropertyChanged;
nativeCell.PropertyChanged += OnNativeCellPropertyChanged;
cell.UpdateCell(nativeCell);
return cell;
}
...
}
}
Se llama al método GetCell
para crear cada celda que se mostrará. Cada celda es una instancia de NativeiOSCell
que define el diseño de la celda y sus datos. La operación del método GetCell
depende de la estrategia de almacenamiento en caché ListView
:
Cuando la estrategia de almacenamiento en caché
ListView
seaRetainElement
, se invocará el métodoGetCell
para cada celda. Se creará una instancia deNativeiOSCell
para cada instancia deNativeCell
que se muestre inicialmente en la pantalla. Cuando el usuario se desplace a través de laListView
, se volverán a usar las instancias deNativeiOSCell
. Para obtener más información sobre la reutilización de celdas de iOS, vea Reutilización de celda.Nota:
Este código de representador personalizado llevará a cabo cierta reutilización de celda cuando la
ListView
se establezca para conservar las celdas.El método
UpdateCell
actualizará los datos mostrados por cada instancia deNativeiOSCell
, ya sean recién creados o se vuelvan a utilizar, con los datos de cada instancia deNativeCell
.Nota:
El método
OnNativeCellPropertyChanged
nunca se invocará cuando la estrategia de almacenamiento en cachéListView
se establezca para conservar las celdas.Cuando la estrategia de almacenamiento en caché
ListView
seaRecycleElement
, se invocará el métodoGetCell
para cada celda que se muestra inicialmente en la pantalla. Se creará una instancia deNativeiOSCell
para cada instancia deNativeCell
que se muestre inicialmente en la pantalla. El métodoUpdateCell
actualizará los datos mostrados por cada instancia deNativeiOSCell
con los datos de cada instancia deNativeCell
. Empero, el métodoGetCell
no se invoca cuando el usuario se desplaza por laListView
. En su lugar, se reutilizarán las instancias deNativeiOSCell
. Los eventos dePropertyChanged
se generarán en la instancia deNativeCell
cuando cambien sus datos y el controlador de eventosOnNativeCellPropertyChanged
actualizará los datos en cada instancia deNativeiOSCell
reutilizada.
El siguiente ejemplo de código muestra el método OnNativeCellPropertyChanged
que se invoca cuando se provoca un evento PropertyChanged
:
namespace CustomRenderer.iOS
{
public class NativeiOSCellRenderer : ViewCellRenderer
{
...
void OnNativeCellPropertyChanged(object sender, PropertyChangedEventArgs e)
{
var nativeCell = (NativeCell)sender;
if (e.PropertyName == NativeCell.NameProperty.PropertyName)
{
cell.HeadingLabel.Text = nativeCell.Name;
}
else if (e.PropertyName == NativeCell.CategoryProperty.PropertyName)
{
cell.SubheadingLabel.Text = nativeCell.Category;
}
else if (e.PropertyName == NativeCell.ImageFilenameProperty.PropertyName)
{
cell.CellImageView.Image = cell.GetImage(nativeCell.ImageFilename);
}
}
}
}
Este método actualiza los datos que muestran las instancias de NativeiOSCell
reutilizadas. Se realiza una comprobación para la propiedad que se ha modificado, ya que el método puede llamarse varias veces.
La clase NativeiOSCell
define el diseño de cada celda y se muestra en el siguiente ejemplo de código:
internal class NativeiOSCell : UITableViewCell, INativeElementView
{
public UILabel HeadingLabel { get; set; }
public UILabel SubheadingLabel { get; set; }
public UIImageView CellImageView { get; set; }
public NativeCell NativeCell { get; private set; }
public Element Element => NativeCell;
public NativeiOSCell(string cellId, NativeCell cell) : base(UITableViewCellStyle.Default, cellId)
{
NativeCell = cell;
SelectionStyle = UITableViewCellSelectionStyle.Gray;
ContentView.BackgroundColor = UIColor.FromRGB(255, 255, 224);
CellImageView = 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(CellImageView);
}
public void UpdateCell(NativeCell cell)
{
HeadingLabel.Text = cell.Name;
SubheadingLabel.Text = cell.Category;
CellImageView.Image = GetImage(cell.ImageFilename);
}
public UIImage GetImage(string filename)
{
return (!string.IsNullOrWhiteSpace(filename)) ? UIImage.FromFile("Images/" + filename + ".jpg") : null;
}
public override void LayoutSubviews()
{
base.LayoutSubviews();
HeadingLabel.Frame = new CGRect(5, 4, ContentView.Bounds.Width - 63, 25);
SubheadingLabel.Frame = new CGRect(100, 18, 100, 20);
CellImageView.Frame = new CGRect(ContentView.Bounds.Width - 63, 5, 33, 33);
}
}
Esta clase define los controles utilizados para representar el contenido de la celda y su diseño. La clase implementa la interfaz INativeElementView
, que es necesaria cuando ListView
usa estrategia de almacenamiento en caché RecycleElement
. Esta interfaz especifica que la clase debe implementar la propiedad Element
, que debe devolver los datos de celda personalizada para las celdas recicladas.
El constructor NativeiOSCell
inicializa la apariencia de las propiedades HeadingLabel
, SubheadingLabel
y CellImageView
. Estas propiedades se utilizan para mostrar los datos almacenados en la instancia NativeCell
, siendo llamado el método UpdateCell
para establecer el valor de cada propiedad. Además, cuando ListView
usa la estrategia de almacenamiento en caché RecycleElement
, el método OnNativeCellPropertyChanged
puede actualizar los datos mostrados por las propiedades HeadingLabel
, SubheadingLabel
y CellImageView
en el representador personalizado.
El diseño de la celda se realiza mediante la invalidación de LayoutSubviews
, que establece las coordenadas de HeadingLabel
, SubheadingLabel
y CellImageView
dentro de la celda.
Crear un representador personalizado en Android
El siguiente ejemplo de código muestra el representador personalizado para la plataforma de Android:
[assembly: ExportRenderer(typeof(NativeCell), typeof(NativeAndroidCellRenderer))]
namespace CustomRenderer.Droid
{
public class NativeAndroidCellRenderer : ViewCellRenderer
{
NativeAndroidCell cell;
protected override Android.Views.View GetCellCore(Cell item, Android.Views.View convertView, ViewGroup parent, Context context)
{
var nativeCell = (NativeCell)item;
Console.WriteLine("\t\t" + nativeCell.Name);
cell = convertView as NativeAndroidCell;
if (cell == null)
{
cell = new NativeAndroidCell(context, nativeCell);
}
else
{
cell.NativeCell.PropertyChanged -= OnNativeCellPropertyChanged;
}
nativeCell.PropertyChanged += OnNativeCellPropertyChanged;
cell.UpdateCell(nativeCell);
return cell;
}
...
}
}
Se llama al método GetCellCore
para crear cada celda que se mostrará. Cada celda es una instancia de NativeAndroidCell
que define el diseño de la celda y sus datos. La operación del método GetCellCore
depende de la estrategia de almacenamiento en caché ListView
:
Cuando la estrategia de almacenamiento en caché
ListView
seaRetainElement
, se invocará el métodoGetCellCore
para cada celda. Se creará unaNativeAndroidCell
para cada instancia deNativeCell
que se muestre inicialmente en la pantalla. Cuando el usuario se desplace a través de laListView
, se volverán a usar las instancias deNativeAndroidCell
. Para obtener más información sobre la reutilización de celdas en Android, vea Reutilización de vista fila.Nota:
Tenga en cuenta que este código de representador personalizado llevará a cabo cierta reutilización de celda cuando la
ListView
se establezca para conservar las celdas.El método
UpdateCell
actualizará los datos mostrados por cada instancia deNativeAndroidCell
, ya sean recién creados o se vuelvan a utilizar, con los datos de cada instancia deNativeCell
.Nota:
Tenga en cuenta que el método
OnNativeCellPropertyChanged
se invocará cuandoListView
se configure para conservar las celdas, pero no se actualizarán los valores de propiedad deNativeAndroidCell
.Cuando la estrategia de almacenamiento en caché
ListView
seaRecycleElement
, se invocará el métodoGetCellCore
para cada celda que se muestra inicialmente en la pantalla. Se creará una instancia deNativeAndroidCell
para cada instancia deNativeCell
que se muestre inicialmente en la pantalla. El métodoUpdateCell
actualizará los datos mostrados por cada instancia deNativeAndroidCell
con los datos de cada instancia deNativeCell
. Empero, el métodoGetCellCore
no se invoca cuando el usuario se desplaza por laListView
. En su lugar, se reutilizarán las instancias deNativeAndroidCell
. Los eventos dePropertyChanged
se generarán en la instancia deNativeCell
cuando cambien sus datos y el controlador de eventosOnNativeCellPropertyChanged
actualizará los datos en cada instancia deNativeAndroidCell
reutilizada.
El siguiente ejemplo de código muestra el método OnNativeCellPropertyChanged
que se invoca cuando se provoca un evento PropertyChanged
:
namespace CustomRenderer.Droid
{
public class NativeAndroidCellRenderer : ViewCellRenderer
{
...
void OnNativeCellPropertyChanged(object sender, PropertyChangedEventArgs e)
{
var nativeCell = (NativeCell)sender;
if (e.PropertyName == NativeCell.NameProperty.PropertyName)
{
cell.HeadingTextView.Text = nativeCell.Name;
}
else if (e.PropertyName == NativeCell.CategoryProperty.PropertyName)
{
cell.SubheadingTextView.Text = nativeCell.Category;
}
else if (e.PropertyName == NativeCell.ImageFilenameProperty.PropertyName)
{
cell.SetImage(nativeCell.ImageFilename);
}
}
}
}
Este método actualiza los datos que muestran las instancias de NativeAndroidCell
reutilizadas. Se realiza una comprobación para la propiedad que se ha modificado, ya que el método puede llamarse varias veces.
La clase NativeAndroidCell
define el diseño de cada celda y se muestra en el siguiente ejemplo de código:
internal class NativeAndroidCell : LinearLayout, INativeElementView
{
public TextView HeadingTextView { get; set; }
public TextView SubheadingTextView { get; set; }
public ImageView ImageView { get; set; }
public NativeCell NativeCell { get; private set; }
public Element Element => NativeCell;
public NativeAndroidCell(Context context, NativeCell cell) : base(context)
{
NativeCell = cell;
var view = (context as Activity).LayoutInflater.Inflate(Resource.Layout.NativeAndroidCell, null);
HeadingTextView = view.FindViewById<TextView>(Resource.Id.HeadingText);
SubheadingTextView = view.FindViewById<TextView>(Resource.Id.SubheadingText);
ImageView = view.FindViewById<ImageView>(Resource.Id.Image);
AddView(view);
}
public void UpdateCell(NativeCell cell)
{
HeadingTextView.Text = cell.Name;
SubheadingTextView.Text = cell.Category;
// Dispose of the old image
if (ImageView.Drawable != null)
{
using (var image = ImageView.Drawable as BitmapDrawable)
{
if (image != null)
{
if (image.Bitmap != null)
{
image.Bitmap.Dispose();
}
}
}
}
SetImage(cell.ImageFilename);
}
public void SetImage(string filename)
{
if (!string.IsNullOrWhiteSpace(filename))
{
// Display new image
Context.Resources.GetBitmapAsync(filename).ContinueWith((t) =>
{
var bitmap = t.Result;
if (bitmap != null)
{
ImageView.SetImageBitmap(bitmap);
bitmap.Dispose();
}
}, TaskScheduler.FromCurrentSynchronizationContext());
}
else
{
// Clear the image
ImageView.SetImageBitmap(null);
}
}
}
Esta clase define los controles utilizados para representar el contenido de la celda y su diseño. La clase implementa la interfaz INativeElementView
, que es necesaria cuando ListView
usa estrategia de almacenamiento en caché RecycleElement
. Esta interfaz especifica que la clase debe implementar la propiedad Element
, que debe devolver los datos de celda personalizada para las celdas recicladas.
El constructor NativeAndroidCell
aumenta el diseño de NativeAndroidCell
e inicializa las propiedades HeadingTextView
, SubheadingTextView
y ImageView
a los controles en el diseño aumentado. Estas propiedades se utilizan para mostrar los datos almacenados en la instancia NativeCell
, siendo llamado el método UpdateCell
para establecer el valor de cada propiedad. Además, cuando ListView
usa la estrategia de almacenamiento en caché RecycleElement
, el método OnNativeCellPropertyChanged
puede actualizar los datos mostrados por las propiedades HeadingTextView
, SubheadingTextView
y ImageView
en el representador personalizado.
El siguiente ejemplo de código muestra la definición de diseño para el archivo de diseño de NativeAndroidCell.axml
:
<?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/HeadingText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#FF7F3300"
android:textSize="20dip"
android:textStyle="italic" />
<TextView
android:id="@+id/SubheadingText"
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>
Este diseño especifica que dos controles de TextView
y un control de ImageView
se usan para mostrar el contenido de la celda. Los dos controles de TextView
están orientados verticalmente dentro de un control de LinearLayout
, con todos los controles contenidos en un RelativeLayout
.
Creación del representador personalizado en UWP
En el siguiente ejemplo de código se muestra el representador personalizado para UWP:
[assembly: ExportRenderer(typeof(NativeCell), typeof(NativeUWPCellRenderer))]
namespace CustomRenderer.UWP
{
public class NativeUWPCellRenderer : ViewCellRenderer
{
public override Windows.UI.Xaml.DataTemplate GetTemplate(Cell cell)
{
return App.Current.Resources["ListViewItemTemplate"] as Windows.UI.Xaml.DataTemplate;
}
}
}
Se llama al método GetTemplate
para devolver la celda que se va a representar para cada fila de datos en la lista. Crea un DataTemplate
para cada instancia de NativeCell
que se mostrará en la pantalla, con el DataTemplate
definiendo la apariencia y el contenido de la celda.
DataTemplate
se almacena en el diccionario de recursos de nivel de aplicación y se muestra en el siguiente ejemplo de código:
<DataTemplate x:Key="ListViewItemTemplate">
<Grid Background="LightYellow">
<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>
El DataTemplate
especifica los controles utilizados para mostrar el contenido de la celda y su diseño y apariencia. Dos controles de TextBlock
y un control de Image
se usan para mostrar el contenido de la celda mediante el enlace de datos. Además, una instancia de ConcatImageExtensionConverter
se utiliza para concatenar la extensión de archivo .jpg
para cada nombre de archivo de imagen. Esto garantiza que el control Image
puede cargar y representar la imagen cuando se establece su propiedad Source
.
Resumen
En este artículo, se ha mostrado cómo crear un representador personalizado para un ViewCell
que se hospeda dentro de un control ListView
de Xamarin.Forms. Esto impide que se llame varias veces a los cálculos de diseño de Xamarin.Forms durante el desplazamiento de ListView
.