Xamarin.Forms CollectionView EmptyView

CollectionView 定义了以下属性,这些属性可用于在没有要显示的数据时提供用户反馈:

  • EmptyView,类型为 object,是指当 ItemsSource 属性为 null 时或当 ItemsSource 属性指定的集合为 null 或为空时,将显示的字符串、绑定或视图。 默认值为 null
  • EmptyViewTemplate,类型为 DataTemplate,用于对指定 EmptyView 设置格式的模板。 默认值为 null

所有这些属性都由 BindableProperty 对象提供支持,这意味着这些属性可以作为数据绑定的目标。

设置 EmptyView 属性的主要使用场景是,在对 CollectionView 执行筛选操作后没有获得数据时显示用户反馈,以及在从 Web 服务检索数据时显示用户反馈。

注意

如果需要,可将 EmptyView 属性设置为包含交互式内容的视图。

有关数据模板的详细信息,请参阅 Xamarin.Forms 数据模板

当数据不可用时显示字符串

EmptyView 属性可设置为字符串,当 ItemsSource 属性为 null 时,或当 ItemsSource 属性指定的集合为 null 或为空时,将显示该字符串。 以下 XAML 显示了此方案的示例:

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

等效 C# 代码如下:

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

结果是,由于数据绑定集合为 null,因此将显示设置为 EmptyView 属性值的字符串:

iOS 和 Android 上带有文本空视图的 CollectionView 垂直列表的屏幕截图

当数据不可用时显示视图

EmptyView 属性可设置为视图,当 ItemsSource 属性为 null 时,或 ItemsSource 属性指定的集合为 null 或为空时,将显示该视图。 这可以是单个视图,也可以是包含多个子视图的视图。 以下 XAML 示例显示了将 EmptyView 属性设置为包含多个子视图的视图:

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

在此示例中,已将看起来像是冗余的 ContentView 添加为 EmptyView 的根元素。 这是因为,在内部 EmptyView 会添加到不为 Xamarin.Forms 布局提供任何上下文的一个原生容器。 因此,要定位构成 EmptyView 的视图,必须添加根布局,而根布局的子布局可以在根布局中定位。

等效 C# 代码如下:

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");

SearchBar 执行 FilterCommand 时,将对 CollectionView 显示的集合进行筛选,以查找存储在 SearchBar.Text 属性中的搜索词。 如果筛选操作不生成任何数据,则会显示设置为 EmptyView 属性值的 StackLayout

iOS 和 Android 上具有自定义空视图的 CollectionView 垂直列表的屏幕截图

当数据不可用时显示模板化的自定义类型

EmptyView 属性可设置为自定义类型,当 ItemsSource 属性为 null 时,或当 ItemsSource 属性指定的集合为 null 或为空时,将显示其模板。 EmptyViewTemplate 属性可设置为定义 EmptyView 外观的 DataTemplate。 以下 XAML 显示了此方案的示例:

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

等效 C# 代码如下:

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

FilterData 类型定义 Filter 属性和相应的 BindableProperty

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); }
    }
}

EmptyView 属性设置为 FilterData 对象,Filter 属性数据绑定到 SearchBar.Text 属性。 当 SearchBar 执行 FilterCommand 时,将对 CollectionView 显示的集合进行筛选,以查找存储在 Filter 属性中的搜索词。 如果筛选操作未生成任何数据,则会显示 DataTemplate 中定义的 Label(已设置为 EmptyViewTemplate 属性值):

iOS 和 Android 上具有空视图模板的 CollectionView 垂直列表的屏幕截图

注意

当数据不可用时显示模板化自定义类型时,可将 EmptyViewTemplate 属性设置为包含多个子视图的视图。

在运行时选择 EmptyView

当数据不可用时将以 EmptyView 显示的视图,可在 ResourceDictionary 中定义为 ContentView 对象。 然后,可以在运行时根据某些业务逻辑将 EmptyView 属性设置为特定的 ContentView。 以下 XAML 显示了此方案的示例:

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

此 XAML 在页面级 ResourceDictionary 中定义两个 ContentView 对象,其中 Switch 对象控制将哪个 ContentView 对象设置为 EmptyView 属性值。 切换 Switch 时,OnEmptyViewSwitchToggled 事件处理程序会执行 ToggleEmptyView 方法:

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

ToggleEmptyView 方法根据 Switch.IsToggled 属性的值,将 collectionView 对象的 EmptyView 属性设置为 ResourceDictionary 中存储的两个 ContentView 对象之一。 当 SearchBar 执行 FilterCommand 时,将对 CollectionView 显示的集合进行筛选,以查找存储在 SearchBar.Text 属性中的搜索词。 如果筛选操作未生成任何数据,则会显示设置为 EmptyView 属性的 ContentView 对象:

iOS 和 Android 上具有已交换空视图的 CollectionView 垂直列表的屏幕截图

有关资源字典的详细信息,请参阅 Xamarin.Forms 资源字典

在运行时选择 EmptyViewTemplate

通过将 CollectionView.EmptyViewTemplate 属性设置为 DataTemplateSelector 对象,可以在运行时根据 EmptyView 的值选择其外观:

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

等效 C# 代码如下:

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

EmptyView 属性设置为 SearchBar.Text 属性,并且 EmptyViewTemplate 属性设置为 SearchTermDataTemplateSelector 对象。

SearchBar 执行 FilterCommand 时,将对 CollectionView 显示的集合进行筛选,以查找存储在 SearchBar.Text 属性中的搜索词。 如果筛选操作没有产生任何数据,则会将由 SearchTermDataTemplateSelector 对象的选择的 DataTemplate 设置为 EmptyViewTemplate 属性并显示。

以下示例显示了 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;
    }
}

SearchTermTemplateSelector 类定义设置为不同数据模板的 DefaultTemplateOtherTemplateDataTemplate 属性。 当搜索查询不等于“xamarin”时,OnSelectTemplate 重写将返回 DefaultTemplate,并向用户显示一条消息。 当搜索查询等于“xamarin”时,OnSelectTemplate 重写将返回 OtherTemplate,并向用户显示基本消息:

iOS 和 Android 上 CollectionView 运行时空视图模板选择的屏幕截图

有关数据模板选择器的详细信息,请参阅创建 Xamarin.Forms DataTemplateSelector