创建 Xamarin.Forms DataTemplate

可通过内联方式创建数据模板,也可在 ResourceDictionary 中或根据自定义类型或适当的 Xamarin.Forms 单元类型进行创建。 本文对每种技术进行了探讨。

DataTemplate 的常见使用场景是在 ListView 中显示来自对象集合的数据。 可通过将 ListView.ItemTemplate 属性设置为 DataTemplate,来管理 ListView 中每个单元的数据外观。 可使用多种技术实现此目的:

无论使用何种技术,结果都是 ListView 中每个单元的外观由 DataTemplate 定义,如以下屏幕截图所示:

使用 DataTemplate 的 ListView

创建内联 DataTemplate

可以将 ListView.ItemTemplate 属性设置为内联 DataTemplate。 如果不需要在其他地方重复使用数据模板,则应使用内联模板,该模板作为相应控件属性的直接子级进行放置。 DataTemplate 中指定的元素定义每个单元的外观,如以下 XAML 代码示例所示:

<ListView Margin="0,20,0,0">
    <ListView.ItemsSource>
        <x:Array Type="{x:Type local:Person}">
            <local:Person Name="Steve" Age="21" Location="USA" />
            <local:Person Name="John" Age="37" Location="USA" />
            <local:Person Name="Tom" Age="42" Location="UK" />
            <local:Person Name="Lucas" Age="29" Location="Germany" />
            <local:Person Name="Tariq" Age="39" Location="UK" />
            <local:Person Name="Jane" Age="30" Location="USA" />
        </x:Array>
    </ListView.ItemsSource>
    <ListView.ItemTemplate>
        <DataTemplate>
            <ViewCell>
                <Grid>
                    ...
                    <Label Text="{Binding Name}" FontAttributes="Bold" />
                    <Label Grid.Column="1" Text="{Binding Age}" />
                    <Label Grid.Column="2" Text="{Binding Location}" HorizontalTextAlignment="End" />
                </Grid>
            </ViewCell>
        </DataTemplate>
    </ListView.ItemTemplate>
</ListView>

内联 DataTemplate 的子级必须属于或派生自 Cell 类型。 本示例使用派生自 CellViewCellViewCell 内的布局在此处由 Grid 进行管理。 Grid 包含三个 Label 实例,这些实例将其 Text 属性绑定到集合中每个 Person 对象的相应属性。

以下代码示例显示相应的 C# 代码:

public class WithDataTemplatePageCS : ContentPage
{
    public WithDataTemplatePageCS()
    {
        ...
        var people = new List<Person>
        {
            new Person { Name = "Steve", Age = 21, Location = "USA" },
            ...
        };

        var personDataTemplate = new DataTemplate(() =>
        {
            var grid = new Grid();
            ...
            var nameLabel = new Label { FontAttributes = FontAttributes.Bold };
            var ageLabel = new Label();
            var locationLabel = new Label { HorizontalTextAlignment = TextAlignment.End };

            nameLabel.SetBinding(Label.TextProperty, "Name");
            ageLabel.SetBinding(Label.TextProperty, "Age");
            locationLabel.SetBinding(Label.TextProperty, "Location");

            grid.Children.Add(nameLabel);
            grid.Children.Add(ageLabel, 1, 0);
            grid.Children.Add(locationLabel, 2, 0);

            return new ViewCell { View = grid };
        });

        Content = new StackLayout
        {
            Margin = new Thickness(20),
            Children = {
                ...
                new ListView { ItemsSource = people, ItemTemplate = personDataTemplate, Margin = new Thickness(0, 20, 0, 0) }
            }
        };
    }
}

在 C# 中,内联 DataTemplate 是使用指定 Func 参数的构造函数重载创建的。

使用类型创建 DataTemplate

也可以将 ListView.ItemTemplate 属性设置为根据某个单元类型创建的 DataTemplate。 这种方法的优点在于,由该单元类型定义的外观可由整个应用程序中的多个数据模板重复使用。 以下 XAML 代码展示了此方法的一个示例:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:local="clr-namespace:DataTemplates"
             ...>
    <StackLayout Margin="20">
        ...
        <ListView Margin="0,20,0,0">
           <ListView.ItemsSource>
                <x:Array Type="{x:Type local:Person}">
                    <local:Person Name="Steve" Age="21" Location="USA" />
                    ...
                </x:Array>
            </ListView.ItemsSource>
            <ListView.ItemTemplate>
                <DataTemplate>
                    <local:PersonCell />
                </DataTemplate>
            </ListView.ItemTemplate>
        </ListView>
    </StackLayout>
</ContentPage>

在这里,ListView.ItemTemplate 属性设置为根据定义单元外观的自定义类型创建的 DataTemplate。 该自定义类型必须派生自 ViewCell 类型,如以下代码示例所示:

<ViewCell xmlns="http://xamarin.com/schemas/2014/forms"
          xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
          x:Class="DataTemplates.PersonCell">
     <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="0.5*" />
            <ColumnDefinition Width="0.2*" />
            <ColumnDefinition Width="0.3*" />
        </Grid.ColumnDefinitions>
        <Label Text="{Binding Name}" FontAttributes="Bold" />
        <Label Grid.Column="1" Text="{Binding Age}" />
        <Label Grid.Column="2" Text="{Binding Location}" HorizontalTextAlignment="End" />
    </Grid>
</ViewCell>

ViewCell 内的布局在此处由 Grid 进行管理。 Grid 包含三个 Label 实例,这些实例将其 Text 属性绑定到集合中每个 Person 对象的相应属性。

以下示例展示了等效的 C# 代码:

public class WithDataTemplatePageFromTypeCS : ContentPage
{
    public WithDataTemplatePageFromTypeCS()
    {
        ...
        var people = new List<Person>
        {
            new Person { Name = "Steve", Age = 21, Location = "USA" },
            ...
        };

        Content = new StackLayout
        {
            Margin = new Thickness(20),
            Children = {
                ...
                new ListView { ItemTemplate = new DataTemplate(typeof(PersonCellCS)), ItemsSource = people, Margin = new Thickness(0, 20, 0, 0) }
            }
        };
    }
}

在 C# 中,DataTemplate 是使用构造函数重载创建的,该重载将单元类型指定为参数。 单元类型必须派生自 ViewCell 类型,如以下代码示例所示:

public class PersonCellCS : ViewCell
{
    public PersonCellCS()
    {
        var grid = new Grid();
        ...
        var nameLabel = new Label { FontAttributes = FontAttributes.Bold };
        var ageLabel = new Label();
        var locationLabel = new Label { HorizontalTextAlignment = TextAlignment.End };

        nameLabel.SetBinding(Label.TextProperty, "Name");
        ageLabel.SetBinding(Label.TextProperty, "Age");
        locationLabel.SetBinding(Label.TextProperty, "Location");

        grid.Children.Add(nameLabel);
        grid.Children.Add(ageLabel, 1, 0);
        grid.Children.Add(locationLabel, 2, 0);

        View = grid;
    }
}

注意

请注意,Xamarin.Forms 还包括可用于在 ListView 单元中显示简单数据的单元类型。 有关详细信息,请参阅单元外观

将 DataTemplate 创建为资源

也可以将数据模板创建为 ResourceDictionary 中的可重用对象。 这是通过为每个声明赋予一个唯一的 x:Key 属性来实现的,此属性为声明提供 ResourceDictionary 中的描述性键,如以下 XAML 代码示例所示:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             ...>
    <ContentPage.Resources>
        <ResourceDictionary>
            <DataTemplate x:Key="personTemplate">
                 <ViewCell>
                    <Grid>
                        ...
                    </Grid>
                </ViewCell>
            </DataTemplate>
        </ResourceDictionary>
    </ContentPage.Resources>
    <StackLayout Margin="20">
        ...
        <ListView ItemTemplate="{StaticResource personTemplate}" Margin="0,20,0,0">
            <ListView.ItemsSource>
                <x:Array Type="{x:Type local:Person}">
                    <local:Person Name="Steve" Age="21" Location="USA" />
                    ...
                </x:Array>
            </ListView.ItemsSource>
        </ListView>
    </StackLayout>
</ContentPage>

使用 StaticResource 标记扩展将 DataTemplate 分配给 ListView.ItemTemplate 属性。 请注意,虽然 DataTemplate 是在页面的 ResourceDictionary 中定义的,但它也可以在控件级别或应用程序级别定义。

以下代码示例展示了 C# 中的等效页面:

public class WithDataTemplatePageCS : ContentPage
{
  public WithDataTemplatePageCS ()
  {
    ...
    var personDataTemplate = new DataTemplate (() => {
      var grid = new Grid ();
      ...
      return new ViewCell { View = grid };
    });

    Resources = new ResourceDictionary ();
    Resources.Add ("personTemplate", personDataTemplate);

    Content = new StackLayout {
      Margin = new Thickness(20),
      Children = {
        ...
        new ListView { ItemTemplate = (DataTemplate)Resources ["personTemplate"], ItemsSource = people };
      }
    };
  }
}

使用 Add 方法将 DataTemplate 添加到 ResourceDictionary,该方法指定了用于在检索到 DataTemplate 时对其进行引用的 Key 字符串。

总结

本文说明了如何以内联方式、根据自定义类型或在 ResourceDictionary 中创建数据模板。 如果不需要在其他地方重复使用数据模板,则应使用内联模板。 或者,可将数据模板定义为自定义类型或控件级别、页面级别或应用程序级别资源,从而重复使用数据模板。