Compartir a través de


Definir una propiedad EmptyView para CollectionView

Examinar ejemplo. Examinar el ejemplo

CollectionView de .NET Multi-platform App UI (.NET MAUI) define las siguientes propiedades que se pueden usar para proporcionar comentarios de 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, consulta Plantillas de datos.

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, static (MonkeysViewModel vm) => vm.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:

Recorte de pantalla de una lista vertical CollectionView con una vista vacía de texto.

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

<Grid Margin="20" RowDefinitions="Auto,*">
    <SearchBar x:Name="searchBar"
               SearchCommand="{Binding FilterCommand}"
               SearchCommandParameter="{Binding x:DataType='SearchBar', Source={x:Reference searchBar}, Path=Text}"
               Placeholder="Filter" />
    <CollectionView ItemsSource="{Binding Monkeys}"
                    Grid.Row="1">
        <CollectionView.ItemTemplate>
            <DataTemplate x:DataType="models:Monkey">
                ...
            </DataTemplate>
        </CollectionView.ItemTemplate>
        <CollectionView.EmptyView>
            <ContentView>
                <StackLayout HorizontalOptions="Center"
                             VerticalOptions="Center">
                    <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>
</Grid>

En este ejemplo, lo que parece una redundancia se ha agregado como el elemento raíz de EmptyView. Esto se debe a que, internamente, EmptyView se agrega a un contenedor nativo que no proporciona ningún contexto para el diseño de .NET MAUI. 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:

StackLayout stackLayout = new StackLayout();
stackLayout.Add(new Label { Text = "No results matched your filter.", ... } );
stackLayout.Add(new Label { Text = "Try a broader filter?", ... } );

SearchBar searchBar = new SearchBar { ... };
CollectionView collectionView = new CollectionView
{
    EmptyView = new ContentView
    {
        Content = stackLayout
    }
};
collectionView.SetBinding(ItemsView.ItemsSourceProperty, static (MonkeysViewModel vm) => vm.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:

Recorte de pantalla de una lista vertical CollectionView con una vista vacía personalizada.

Mostrar 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:

<Grid Margin="20" RowDefinitions="Auto,*">
    <SearchBar x:Name="searchBar"
               SearchCommand="{Binding FilterCommand}"
               SearchCommandParameter="{Binding x:DataType='SearchBar', Source={x:Reference searchBar}, Path=Text}"
               Placeholder="Filter" />
    <CollectionView ItemsSource="{Binding Monkeys}"
                    Grid.Row="1">
        <CollectionView.ItemTemplate>
            <DataTemplate x:DataType="models:Monkey">
                ...
            </DataTemplate>
        </CollectionView.ItemTemplate>
        <CollectionView.EmptyView>
            <views:FilterData x:DataType="SearchBar"
                              Filter="{Binding Source={x:Reference searchBar}, Path=Text}" />
        </CollectionView.EmptyView>
        <CollectionView.EmptyViewTemplate>
            <DataTemplate>
                <Label x:DataType="controls:FilterData"
                       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>
</Grid>

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:

Recorte de pantalla de una lista vertical CollectionView con una plantilla de vista vacía.

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://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:models="clr-namespace:CollectionViewDemos.Models"
             xmlns:viewmodels="clr-namespace:CollectionViewDemos.ViewModels"
             x:Class="CollectionViewDemos.Views.EmptyViewSwapPage"
             Title="EmptyView (swap)"
             x:DataType="viewmodels:MonkeysViewModel">
    <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>

    <Grid Margin="20" RowDefinitions="Auto, Auto, *">
        <SearchBar x:Name="searchBar"
                   SearchCommand="{Binding FilterCommand}"
                   SearchCommandParameter="{Binding x:DataType='SearchBar', Source={x:Reference searchBar}, Path=Text}"
                   Placeholder="Filter" />
        <HorizontalStackLayout Grid.Row="1">
            <Label Text="Toggle EmptyViews" />
            <Switch Toggled="OnEmptyViewSwitchToggled" />
        </HorizontalStackLayout>
        <CollectionView x:Name="collectionView"
                        ItemsSource="{Binding Monkeys}"
                        Grid.Row="2">
            <CollectionView.ItemTemplate>
                <DataTemplate x:DataType="models:Monkey">
                    ...
                </DataTemplate>
            </CollectionView.ItemTemplate>
        </CollectionView>
    </Grid>
</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:

Recorte de pantalla de una lista vertical CollectionView con vistas vacías intercambiadas.

Para obtener más información sobre los diccionarios de recursos, consulta Diccionarios de recursos.

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"
             xmlns:viewmodels="clr-namespace:CollectionViewDemos.ViewModels"
             x:DataType="viewmodels:MonkeysViewModel">
    <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>

    <Grid Margin="20" RowDefinitions="Auto,*">
        <SearchBar x:Name="searchBar"
                   SearchCommand="{Binding FilterCommand}"
                   SearchCommandParameter="{Binding x:DataType='SearchBar', Source={x:Reference searchBar}, Path=Text}"
                   Placeholder="Filter" />
        <CollectionView ItemsSource="{Binding Monkeys}"
                        EmptyView="{Binding x:DataType='SearchBar', Source={x:Reference searchBar}, Path=Text}"
                        EmptyViewTemplate="{StaticResource SearchSelector}"
                        Grid.Row="1" />
    </Grid>
</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, static (MonkeysViewModel vm) => vm.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 DefaultTemplate y OtherTemplate DataTemplate propiedades que se establecen en diferentes plantillas de datos. 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:

Recorte de pantalla de una selección de plantilla de vista vacía del entorno de ejecución de CollectionView.

Para obtener más información sobre los selectores de plantillas de datos, consulta Creación de un DataTemplateSelector.