Diseños enlazables en Xamarin.Forms
Los diseños enlazables permiten que cualquier clase de diseño que derive de la clase Layout<T>
genere su contenido mediante el enlace a una colección de elementos, con la opción de establecer la apariencia de cada elemento con un DataTemplate
. La clase proporciona BindableLayout
diseños enlazables y expone las siguientes propiedades adjuntas:
ItemsSource
, que especifica la colección de elementosIEnumerable
que va a mostrar el diseño.ItemTemplate
, que especifica elDataTemplate
que se aplicará a cada elemento de la colección de elementos que mostrará el diseño.ItemTemplateSelector
, que espeficia elDataTemplateSelector
que se usará para elegir unDataTemplate
para un elemento en tiempo de ejecución.
Nota:
Si estableces las propiedades ItemTemplate
y ItemTemplateSelector
, la prioridad la tiene la propiedad ItemTemplate
.
Además, la clase BindableLayout
expone las siguientes propiedades enlazables:
EmptyView
, que especificastring
o la vista o que se mostrará cuando la propiedadItemsSource
seanull
o cuando la colección especificada por la propiedadItemsSource
seanull
o esté vacía. El valor predeterminado esnull
.EmptyViewTemplate
, que especifica queDataTemplate
se mostrará cuando la propiedadItemsSource
seanull
o cuando la colección especificada por la propiedadItemsSource
seanull
o esté vacía. El valor predeterminado esnull
.
Nota:
Si estableces las propiedades EmptyView
y EmptyViewTemplate
, la prioridad la tiene la propiedad EmptyViewTemplate
.
Todas estas propiedades se pueden adjuntar a las clases AbsoluteLayout
, FlexLayout
, Grid
, RelativeLayout
y StackLayout
, que derivan de la clase Layout<T>
.
La clase Layout<T>
expone una colección Children
a la que se agregan los elementos secundarios de un diseño. Cuando la propiedad BindableLayout.ItemsSource
se establece en una colección de elementos y se adjunta a una clase derivada de Layout<T>
, cada elemento de la colección se agrega a la colección Layout<T>.Children
para su visualización en el diseño. Luego, la clase derivada de Layout<T>
actualizará sus vistas secundarias cuando cambie la colección subyacente. Para obtener más información sobre el ciclo de diseño Xamarin.Forms, consulte Crear de un diseño personalizado.
Los diseños enlazables solo deben usarse cuando la colección de elementos que se va a mostrar es pequeña y no es necesario desplazarse ni seleccionar. Aunque el desplazamiento se puede proporcionar ajustando un diseño enlazable en ScrollView
, no se recomienda porque los diseños enlazables carecen de virtualización de interfaz de usuario. Cuando se requiere el desplazamiento, se debe usar una vista desplazable que incluya virtualización de interfaz de usuario, como ListView
o CollectionView
. El incumplimiento de esta recomendación puede provocar problemas de rendimiento.
Importante
Aunque técnicamente es posible adjuntar un diseño enlazable a cualquier clase de diseño que derive de la clase Layout<T>
, no siempre resulta práctico hacerlo, especialmente en el caso de las clases AbsoluteLayout
, Grid
y RelativeLayout
. Por ejemplo, piensa en un escenario en el que se quiere mostrar una colección de datos en un Grid
mediante un diseño enlazable, donde cada elemento de la colección es un objeto que contiene varias propiedades. Cada fila de Grid
debe mostrar un objeto de la colección, y cada columna de Grid
muestra una de las propiedades del objeto. Dado que el DataTemplate
para el diseño enlazable solo puede contener un objeto, es necesario que ese objeto sea una clase de diseño que contenga varias vistas y que cada vista muestre una de las propiedades del objeto en una columna Grid
específica. Aunque este escenario se puede cumplir con diseños enlazables, da como resultado un Grid
primario que contiene un Grid
secundario para cada elemento de la colección enlazada, lo que supone un uso altamente ineficaz y cuestionable del diseño Grid
.
Rellenar un diseño enlazable con datos
Un diseño enlazable se rellena con datos estableciendo su propiedad ItemsSource
en cualquier colección que implemente IEnumerable
y adjuntándola a una clase derivada de Layout<T>
:
<Grid BindableLayout.ItemsSource="{Binding Items}" />
El código de C# equivalente es el siguiente:
IEnumerable<string> items = ...;
var grid = new Grid();
BindableLayout.SetItemsSource(grid, items);
Cuando se establece la propiedad adjunta BindableLayout.ItemsSource
en un diseño, pero no se establece la propiedad adjunta BindableLayout.ItemTemplate
, cada elemento de la colección IEnumerable
se mostrará mediante un objeto Label
creado por la clase BindableLayout
.
Definición de la apariencia de elementos
La apariencia de cada elemento del diseño enlazable se puede definir estableciendo la propiedad adjunta BindableLayout.ItemTemplate
en un objeto DataTemplate
:
<StackLayout BindableLayout.ItemsSource="{Binding User.TopFollowers}"
Orientation="Horizontal"
...>
<BindableLayout.ItemTemplate>
<DataTemplate>
<controls:CircleImage Source="{Binding}"
Aspect="AspectFill"
WidthRequest="44"
HeightRequest="44"
... />
</DataTemplate>
</BindableLayout.ItemTemplate>
</StackLayout>
El código de C# equivalente es el siguiente:
DataTemplate circleImageTemplate = ...;
var stackLayout = new StackLayout();
BindableLayout.SetItemsSource(stackLayout, viewModel.User.TopFollowers);
BindableLayout.SetItemTemplate(stackLayout, circleImageTemplate);
En este ejemplo, cada elemento de la colección TopFollowers
se mostrará mediante una vista CircleImage
definida en el DataTemplate
:
Para obtener más información sobre las plantillas de datos, consulte Plantillas de datos de Xamarin.Forms.
Elección de la apariencia del elemento en tiempo de ejecución
La apariencia de cada elemento del diseño enlazable se puede elegir en tiempo de ejecución, en función del valor del elemento, estableciendo la propiedad BindableLayout.ItemTemplateSelector
adjunta en un objeto DataTemplateSelector
:
<FlexLayout BindableLayout.ItemsSource="{Binding User.FavoriteTech}"
BindableLayout.ItemTemplateSelector="{StaticResource TechItemTemplateSelector}"
... />
El código de C# equivalente es el siguiente:
DataTemplateSelector dataTemplateSelector = new TechItemTemplateSelector { ... };
var flexLayout = new FlexLayout();
BindableLayout.SetItemsSource(flexLayout, viewModel.User.FavoriteTech);
BindableLayout.SetItemTemplateSelector(flexLayout, dataTemplateSelector);
El DataTemplateSelector
usado en la aplicación de ejemplo se muestra en el siguiente ejemplo:
public class TechItemTemplateSelector : DataTemplateSelector
{
public DataTemplate DefaultTemplate { get; set; }
public DataTemplate XamarinFormsTemplate { get; set; }
protected override DataTemplate OnSelectTemplate(object item, BindableObject container)
{
return (string)item == "Xamarin.Forms" ? XamarinFormsTemplate : DefaultTemplate;
}
}
La clase TechItemTemplateSelector
define propiedades DataTemplate
DefaultTemplate
y XamarinFormsTemplate
que se establecen en diferentes plantillas de datos. El método OnSelectTemplate
devuelve el XamarinFormsTemplate
, que muestra un elemento en rojo oscuro con un corazón junto a él, cuando el elemento es igual a "Xamarin.Forms". Cuando el elemento no es igual a "Xamarin.Forms", el método OnSelectTemplate
devuelve el DefaultTemplate
, que muestra un elemento con el color predeterminado de un Label
:
Para más información sobre los selectores de plantillas de datos, consulte Creación de una instancia de Xamarin.Forms DataTemplateSelector.
Mostrar una cadena cuando los datos no están disponibles
La propiedad EmptyView
se puede establecer en una cadena, que se mostrará mediante Label
cuando la propiedad ItemsSource
sea null
o cuando la colección especificada por la propiedad ItemsSource
sea null
o esté vacía. En el siguiente archivo XAML se muestra un ejemplo de este escenario:
<StackLayout BindableLayout.ItemsSource="{Binding UserWithoutAchievements.Achievements}"
BindableLayout.EmptyView="No achievements">
...
</StackLayout>
El resultado es que, cuando la colección enlazada a datos es null
, se muestra el conjunto de cadenas como valor de propiedad EmptyView
:
Muestra de vistas cuando los datos no están disponibles
La propiedad EmptyView
se puede establecer en una vista, que se mostrará cuando la propiedad ItemsSource
sea null
, o cuando la colección especificada por la propiedad ItemsSource
sea null
o esté vacía. Puede ser una sola vista o una vista que contenga varias vistas secundarias. En el ejemplo de XAML siguiente se muestra la propiedad EmptyView
establecida en una vista que contiene varias vistas secundarias:
<StackLayout BindableLayout.ItemsSource="{Binding UserWithoutAchievements.Achievements}">
<BindableLayout.EmptyView>
<StackLayout>
<Label Text="None."
FontAttributes="Italic"
FontSize="{StaticResource smallTextSize}" />
<Label Text="Try harder and return later?"
FontAttributes="Italic"
FontSize="{StaticResource smallTextSize}" />
</StackLayout>
</BindableLayout.EmptyView>
...
</StackLayout>
El resultado es que, cuando la colección enlazada a datos es null
, se muestran StackLayout
y sus vistas secundarias.
Del mismo modo, EmptyViewTemplate
se puede establecer en DataTemplate
, que se mostrará cuando la propiedad ItemsSource
sea null
o cuando la colección especificada por la propiedad ItemsSource
sea null
o esté vacía. DataTemplate
puede contener una sola vista o una vista que contenga varias vistas secundarias. Además, BindingContext
de EmptyViewTemplate
se heredará de BindingContext
de BindableLayout
. En el ejemplo XAML siguiente se muestra la propiedad EmptyViewTemplate
establecida en DataTemplate
que contiene una sola vista:
<StackLayout BindableLayout.ItemsSource="{Binding UserWithoutAchievements.Achievements}">
<BindableLayout.EmptyViewTemplate>
<DataTemplate>
<Label Text="{Binding Source={x:Reference usernameLabel}, Path=Text, StringFormat='{0} has no achievements.'}" />
</DataTemplate>
</BindableLayout.EmptyViewTemplate>
...
</StackLayout>
El resultado es que cuando la colección enlazada a datos es null
, se muestra Label
en DataTemplate
:
Nota:
La propiedad EmptyViewTemplate
no se puede establecer mediante DataTemplateSelector
.
Elección de emptyView en tiempo de ejecución
Las vistas que se mostrarán como EmptyView
cuando los datos no estén disponibles, se pueden definir como objetos ContentView
en un ResourceDictionary
. Luego, la propiedad EmptyView
se puede establecer en un elemento ContentView
específico, en función de alguna lógica de negocios, en tiempo de ejecución. En el siguiente archivo XAML se muestra un ejemplo de este escenario:
<ContentPage ...>
<ContentPage.Resources>
...
<ContentView x:Key="BasicEmptyView">
<StackLayout>
<Label Text="No achievements."
FontSize="14" />
</StackLayout>
</ContentView>
<ContentView x:Key="AdvancedEmptyView">
<StackLayout>
<Label Text="None."
FontAttributes="Italic"
FontSize="14" />
<Label Text="Try harder and return later?"
FontAttributes="Italic"
FontSize="14" />
</StackLayout>
</ContentView>
</ContentPage.Resources>
<StackLayout>
...
<Switch Toggled="OnEmptyViewSwitchToggled" />
<StackLayout x:Name="stackLayout"
BindableLayout.ItemsSource="{Binding UserWithoutAchievements.Achievements}">
...
</StackLayout>
</StackLayout>
</ContentPage>
El XAML define dos objetos ContentView
en el nivel de página ResourceDictionary
, con el objeto Switch
que controla qué objeto ContentView
se establecerá como valor de propiedad EmptyView
. Switch
Cuando está activado, el controlador de eventos OnEmptyViewSwitchToggled
ejecuta el método ToggleEmptyView
:
void ToggleEmptyView(bool isToggled)
{
object view = isToggled ? Resources["BasicEmptyView"] : Resources["AdvancedEmptyView"];
BindableLayout.SetEmptyView(stackLayout, view);
}
El método ToggleEmptyView
establece la propiedad EmptyView
del objeto stackLayout
en uno de los dos objetos ContentView
almacenados en ResourceDictionary
, en función del valor de la propiedad Switch.IsToggled
. A continuación, cuando la colección enlazada a datos es null
, se muestra el objeto ContentView
establecido como la propiedad EmptyView
: