Xamarin.Forms EmptyView de CollectionView

Download SampleDescargar el ejemplo

CollectionView define las siguientes propiedades que se pueden usar para proporcionar comentarios de los usuarios cuando no hay datos que mostrar:

  • EmptyView, de tipo object, la cadena, el enlace o la vista que se mostrarán cuando la propiedad ItemsSource sea null o cuando la colección especificada por la propiedad ItemsSource sea null o esté vacía. El valor predeterminado es null.
  • EmptyViewTemplate, de tipo DataTemplate, la plantilla utilizada para dar formato específico a EmptyView. El valor predeterminado es null.

Todas estas propiedades están respaldadas por objetos BindableProperty, lo que significa que las propiedades pueden ser destinos de los enlaces de datos.

Los principales escenarios de uso para establecer la propiedad EmptyView muestran comentarios de usuario cuando una operación de filtrado de un CollectionView no produce datos y muestra los comentarios de usuario mientras se recuperan datos de un servicio web.

Nota:

La propiedad EmptyView se puede establecer en una vista que incluya contenido interactivo si es necesario.

Para obtener más información sobre las plantillas de datos, consulte Plantillas de datos de Xamarin.Forms.

Mostrar una cadena cuando los datos no están disponibles

La propiedad EmptyView se puede establecer en una cadena, 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. En el siguiente XAML se muestra un ejemplo de este caso:

<CollectionView ItemsSource="{Binding EmptyMonkeys}"
                EmptyView="No items to display" />

El código de C# equivalente es el siguiente:

CollectionView collectionView = new CollectionView
{
    EmptyView = "No items to display"
};
collectionView.SetBinding(ItemsView.ItemsSourceProperty, "EmptyMonkeys");

El resultado es que, dado que la colección enlazada a datos es null, se muestra el conjunto de cadenas como el valor de propiedad EmptyView:

Screenshot of a CollectionView vertical list with a text empty view, on iOS and Android

Visualizar 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 XAML siguiente se muestra la propiedad EmptyView establecida en una vista que contiene varias vistas secundarias:

<StackLayout Margin="20">
    <SearchBar x:Name="searchBar"
               SearchCommand="{Binding FilterCommand}"
               SearchCommandParameter="{Binding Source={x:Reference searchBar}, Path=Text}"
               Placeholder="Filter" />
    <CollectionView ItemsSource="{Binding Monkeys}">
        <CollectionView.ItemTemplate>
            <DataTemplate>
                ...
            </DataTemplate>
        </CollectionView.ItemTemplate>
        <CollectionView.EmptyView>
            <ContentView>
                <StackLayout HorizontalOptions="CenterAndExpand"
                             VerticalOptions="CenterAndExpand">
                    <Label Text="No results matched your filter."
                           Margin="10,25,10,10"
                           FontAttributes="Bold"
                           FontSize="18"
                           HorizontalOptions="Fill"
                           HorizontalTextAlignment="Center" />
                    <Label Text="Try a broader filter?"
                           FontAttributes="Italic"
                           FontSize="12"
                           HorizontalOptions="Fill"
                           HorizontalTextAlignment="Center" />
                </StackLayout>
            </ContentView>
        </CollectionView.EmptyView>
    </CollectionView>
</StackLayout>

En este ejemplo, lo que parece un ContentView redundante se ha agregado como el elemento raíz del EmptyView. Esto se debe a que internamente, el EmptyView se agrega a un contenedor nativo que no proporciona ningún contexto para el diseño Xamarin.Forms. Por lo tanto, para colocar las vistas que componen EmptyView, debes agregar un diseño raíz, cuyo elemento secundario es un diseño que puede colocarse dentro del diseño raíz.

El código de C# equivalente es el siguiente:

SearchBar searchBar = new SearchBar { ... };
CollectionView collectionView = new CollectionView
{
    EmptyView = new ContentView
    {
        Content = new StackLayout
        {
            Children =
            {
                new Label { Text = "No results matched your filter.", ... },
                new Label { Text = "Try a broader filter?", ... }
            }
        }
    }
};
collectionView.SetBinding(ItemsView.ItemsSourceProperty, "Monkeys");

Cuando SearchBar ejecuta FilterCommand, la colección mostrada por CollectionView se filtra para el término de búsqueda almacenado en la propiedad SearchBar.Text. Si la operación de filtrado no produce ningún dato, se muestra el conjunto StackLayout establecido como valor de propiedad EmptyView:

Screenshot of a CollectionView vertical list with custom empty view, on iOS and Android

Visualizar un tipo personalizado con plantilla cuando los datos no están disponibles

La propiedad EmptyView se puede establecer en un tipo personalizado, cuya plantilla se muestra cuando la propiedad ItemsSource es null o cuando la colección especificada por la propiedad ItemsSource es null o está vacía. La propiedad EmptyViewTemplate se puede establecer en un DataTemplate que define la apariencia de EmptyView. En el siguiente XAML se muestra un ejemplo de este caso:

<StackLayout Margin="20">
    <SearchBar x:Name="searchBar"
               SearchCommand="{Binding FilterCommand}"
               SearchCommandParameter="{Binding Source={x:Reference searchBar}, Path=Text}"
               Placeholder="Filter" />
    <CollectionView ItemsSource="{Binding Monkeys}">
        <CollectionView.ItemTemplate>
            <DataTemplate>
                ...
            </DataTemplate>
        </CollectionView.ItemTemplate>
        <CollectionView.EmptyView>
            <views:FilterData Filter="{Binding Source={x:Reference searchBar}, Path=Text}" />
        </CollectionView.EmptyView>
        <CollectionView.EmptyViewTemplate>
            <DataTemplate>
                <Label Text="{Binding Filter, StringFormat='Your filter term of {0} did not match any records.'}"
                       Margin="10,25,10,10"
                       FontAttributes="Bold"
                       FontSize="18"
                       HorizontalOptions="Fill"
                       HorizontalTextAlignment="Center" />
            </DataTemplate>
        </CollectionView.EmptyViewTemplate>
    </CollectionView>
</StackLayout>

El código de C# equivalente es el siguiente:

SearchBar searchBar = new SearchBar { ... };
CollectionView collectionView = new CollectionView
{
    EmptyView = new FilterData { Filter = searchBar.Text },
    EmptyViewTemplate = new DataTemplate(() =>
    {
        return new Label { ... };
    })
};

El tipo FilterData define una propiedad Filter y una BindableProperty correspondiente:

public class FilterData : BindableObject
{
    public static readonly BindableProperty FilterProperty = BindableProperty.Create(nameof(Filter), typeof(string), typeof(FilterData), null);

    public string Filter
    {
        get { return (string)GetValue(FilterProperty); }
        set { SetValue(FilterProperty, value); }
    }
}

La propiedad EmptyView se establece en un objeto FilterData y los datos de propiedad Filter se enlazan a la propiedad SearchBar.Text. Cuando SearchBar ejecuta FilterCommand, la colección mostrada por CollectionView se filtra para el término de búsqueda almacenado en la propiedad Filter. Si la operación de filtrado no produce ningún dato, se muestra la clase Label definida en DataTemplate que se establece como valor de propiedad EmptyViewTemplate:

Screenshot of a CollectionView vertical list with an empty view template, on iOS and Android

Nota:

Al mostrar un tipo personalizado con plantilla cuando los datos no están disponibles, la propiedad EmptyViewTemplate se puede establecer en una vista que contenga varias vistas secundarias.

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 xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="CollectionViewDemos.Views.EmptyViewSwapPage"
             Title="EmptyView (swap)">
    <ContentPage.Resources>
        <ContentView x:Key="BasicEmptyView">
            <StackLayout>
                <Label Text="No items to display."
                       Margin="10,25,10,10"
                       FontAttributes="Bold"
                       FontSize="18"
                       HorizontalOptions="Fill"
                       HorizontalTextAlignment="Center" />
            </StackLayout>
        </ContentView>
        <ContentView x:Key="AdvancedEmptyView">
            <StackLayout>
                <Label Text="No results matched your filter."
                       Margin="10,25,10,10"
                       FontAttributes="Bold"
                       FontSize="18"
                       HorizontalOptions="Fill"
                       HorizontalTextAlignment="Center" />
                <Label Text="Try a broader filter?"
                       FontAttributes="Italic"
                       FontSize="12"
                       HorizontalOptions="Fill"
                       HorizontalTextAlignment="Center" />
            </StackLayout>
        </ContentView>
    </ContentPage.Resources>

    <StackLayout Margin="20">
        <SearchBar x:Name="searchBar"
                   SearchCommand="{Binding FilterCommand}"
                   SearchCommandParameter="{Binding Source={x:Reference searchBar}, Path=Text}"
                   Placeholder="Filter" />
        <StackLayout Orientation="Horizontal">
            <Label Text="Toggle EmptyViews" />
            <Switch Toggled="OnEmptyViewSwitchToggled" />
        </StackLayout>
        <CollectionView x:Name="collectionView"
                        ItemsSource="{Binding Monkeys}">
            <CollectionView.ItemTemplate>
                <DataTemplate>
                    ...
                </DataTemplate>
            </CollectionView.ItemTemplate>
        </CollectionView>
    </StackLayout>
</ContentPage>

Este 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. Cuando Switch está activado, el controlador de eventos OnEmptyViewSwitchToggled ejecuta el método ToggleEmptyView:

void ToggleEmptyView(bool isToggled)
{
    collectionView.EmptyView = isToggled ? Resources["BasicEmptyView"] : Resources["AdvancedEmptyView"];
}

El método ToggleEmptyView establece la propiedad EmptyView del objeto collectionView en uno de los dos objetos ContentView almacenados en ResourceDictionary, en función del valor de la propiedad Switch.IsToggled. Cuando SearchBar ejecuta FilterCommand, la colección mostrada por CollectionView se filtra para el término de búsqueda almacenado en la propiedad SearchBar.Text. Si la operación de filtrado no produce datos, se muestra el objeto ContentView establecido como propiedad EmptyView:

Screenshot of a CollectionView vertical list with swapped empty views, on iOS and Android

Para más información sobre los diccionarios de recursos, consulte Diccionarios de recursos Xamarin.Forms.

Elección de EmptyViewTemplate en tiempo de ejecución

La apariencia de EmptyView se puede elegir en tiempo de ejecución, en función de su valor, estableciendo la propiedad CollectionView.EmptyViewTemplate en un objeto DataTemplateSelector:

<ContentPage ...
             xmlns:controls="clr-namespace:CollectionViewDemos.Controls">
    <ContentPage.Resources>
        <DataTemplate x:Key="AdvancedTemplate">
            ...
        </DataTemplate>

        <DataTemplate x:Key="BasicTemplate">
            ...
        </DataTemplate>

        <controls:SearchTermDataTemplateSelector x:Key="SearchSelector"
                                                 DefaultTemplate="{StaticResource AdvancedTemplate}"
                                                 OtherTemplate="{StaticResource BasicTemplate}" />
    </ContentPage.Resources>

    <StackLayout Margin="20">
        <SearchBar x:Name="searchBar"
                   SearchCommand="{Binding FilterCommand}"
                   SearchCommandParameter="{Binding Source={x:Reference searchBar}, Path=Text}"
                   Placeholder="Filter" />
        <CollectionView ItemsSource="{Binding Monkeys}"
                        EmptyView="{Binding Source={x:Reference searchBar}, Path=Text}"
                        EmptyViewTemplate="{StaticResource SearchSelector}" />
    </StackLayout>
</ContentPage>

El código de C# equivalente es el siguiente:

SearchBar searchBar = new SearchBar { ... };
CollectionView collectionView = new CollectionView
{
    EmptyView = searchBar.Text,
    EmptyViewTemplate = new SearchTermDataTemplateSelector { ... }
};
collectionView.SetBinding(ItemsView.ItemsSourceProperty, "Monkeys");

La propiedad EmptyView se define en la propiedad SearchBar.Text y la propiedad EmptyViewTemplate, en un objeto SearchTermDataTemplateSelector.

Cuando SearchBar ejecuta FilterCommand, la colección mostrada por CollectionView se filtra para el término de búsqueda almacenado en la propiedad SearchBar.Text. Si la operación de filtrado no produce ningún dato, la DataTemplate elegida por el objeto SearchTermDataTemplateSelector se fija como la propiedad EmptyViewTemplate y se muestra.

En el ejemplo siguiente se muestra la clase SearchTermDataTemplateSelector:

public class SearchTermDataTemplateSelector : DataTemplateSelector
{
    public DataTemplate DefaultTemplate { get; set; }
    public DataTemplate OtherTemplate { get; set; }

    protected override DataTemplate OnSelectTemplate(object item, BindableObject container)
    {
        string query = (string)item;
        return query.ToLower().Equals("xamarin") ? OtherTemplate : DefaultTemplate;
    }
}

La clase SearchTermTemplateSelector define las propiedades DefaultTemplate y OtherTemplateDataTemplate que se establecen en plantillas de datos diferentes. La invalidación OnSelectTemplate devuelve DefaultTemplate, que muestra un mensaje al usuario, cuando la consulta de búsqueda no es igual a "xamarin". Cuando la consulta de búsqueda es igual a "xamarin", la invalidación OnSelectTemplate devuelve OtherTemplate, que muestra un mensaje básico al usuario:

Screenshot of a CollectionView runtime empty view template selection, on iOS and Android

Para más información sobre los selectores de plantillas de datos, consulte Creación de una instancia de Xamarin.Forms DataTemplateSelector.