Criando um Xamarin.Forms DataTemplate

Baixar exemplo Baixar o exemplo

Os modelos de dados podem ser criados embutidos, em um ResourceDictionary ou em um tipo personalizado ou tipo de célula apropriado Xamarin.Forms . Este artigo explora cada técnica.

Um cenário de uso comum para um DataTemplate é exibir dados de uma coleção de objetos em um ListView. A aparência dos dados para cada célula no ListView pode ser gerenciada definindo a propriedade ListView.ItemTemplate como um DataTemplate. Há várias técnicas que podem ser usadas para fazer isso:

Independentemente da técnica que está sendo usada, o resultado é que a aparência de cada célula no ListView é definida por um DataTemplate, conforme mostrado nas seguintes capturas de tela:

ListView com um DataTemplate

Criando um DataTemplate embutido

A propriedade ListView.ItemTemplate pode ser definida como um DataTemplate embutido. Um modelo embutido, que é inserido como um filho direto de uma propriedade de controle apropriada, deverá ser usado se não houver necessidade de reutilizar o modelo de dados em outro lugar. Os elementos especificados no DataTemplate definem a aparência de cada célula, conforme mostrado no exemplo de código XAML a seguir:

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

O filho de um DataTemplate embutido deve ser do tipo Cell ou derivado dele. Este exemplo usa um ViewCell, que deriva de Cell. O layout dentro do ViewCell é gerenciado aqui por um Grid. O Grid contém três instâncias Label que associam suas propriedades Text às propriedades adequadas de cada objeto Person na coleção.

O código C# equivalente é mostrado no exemplo de código a seguir:

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

Em C#, o DataTemplate embutido é criado usando uma sobrecarga de construtor que especifica um argumento Func.

Criando um DataTemplate com um tipo

A propriedade ListView.ItemTemplate também pode ser definida como uma DataTemplate criada com base em um tipo de célula. A vantagem dessa abordagem é que a aparência definida pelo tipo de célula pode ser reutilizado por vários modelos de dados em todo o aplicativo. O código XAML a seguir mostra um exemplo dessa abordagem:

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

Aqui, a propriedade ListView.ItemTemplate é definida como um DataTemplate criado com base em um tipo personalizado que define a aparência da célula. O tipo personalizado deve ser derivado do tipo ViewCell, conforme mostrado no exemplo de código a seguir:

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

Dentro do ViewCell, o layout é gerenciado aqui por um Grid. O Grid contém três instâncias Label que associam suas propriedades Text às propriedades adequadas de cada objeto Person na coleção.

O código C# equivalente é mostrado no exemplo a seguir:

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

Em C#, o DataTemplate é criado usando uma sobrecarga de construtor que especifica o tipo de célula como um argumento. O tipo de célula deve ser derivado do tipo ViewCell, conforme mostrado no exemplo de código a seguir:

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

Observação

Observe que Xamarin.Forms também inclui tipos de célula que podem ser usados para exibir dados simples em ListView células. Para saber mais, confira Cell Appearance (Aparência da célula).

Criando um DataTemplate como um recurso

Os modelos de dados também podem ser criados como objetos reutilizáveis em um ResourceDictionary. Isso é feito dando a cada declaração um atributo x:Key exclusivo, que fornece a ela uma chave descritiva no ResourceDictionary, conforme mostrado no exemplo de código XAML a seguir:

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

O DataTemplate é atribuído à propriedade ListView.ItemTemplate usando a extensão de marcação StaticResource. Observe que, embora o DataTemplate seja definido no ResourceDictionary da página, ele também poderia ser definido no nível do controle ou do aplicativo.

O exemplo de código a seguir mostra a página equivalente em 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 };
      }
    };
  }
}

O DataTemplate é adicionado ao ResourceDictionary usando o método Add, que especifica uma cadeia de caracteres Key usada para referenciar o DataTemplate ao recuperá-la.

Resumo

Este artigo explicou como criar modelos de dados, embutidos, com base em um tipo personalizado ou em um ResourceDictionary. Um modelo embutido deve ser usado caso não haja necessidade de reutilizar o modelo de dados em outro lugar. Como alternativa, um modelo de dados pode ser reutilizado, definindo-o como um tipo personalizado ou como um recurso de aplicativo de nível de controle, de página ou de aplicativo.