Criando um Xamarin.Forms DataTemplateSelector

Um DataTemplateSelector pode ser usado para escolher um DataTemplate em runtime com base no valor de uma propriedade associada a dados. Isso permite que várias instâncias de DataTemplates sejam aplicadas ao mesmo tipo de objeto, para personalizar a aparência de objetos específicos. Este artigo demonstra como criar e consumir um DataTemplateSelector.

Um seletor de modelo de dados permite cenários como uma associação do ListView a uma coleção de objetos, em que a aparência de cada objeto no ListView pode ser escolhida em runtime pelo seletor de modelo de dados que retorna um DataTemplate específico.

Criando um DataTemplateSelector

Um seletor de modelo de dados é implementado criando uma classe herdada de DataTemplateSelector. O método OnSelectTemplate é substituído para retornar um determinado DataTemplate, conforme mostrado no exemplo de código a seguir:

public class PersonDataTemplateSelector : DataTemplateSelector
{
  public DataTemplate ValidTemplate { get; set; }
  public DataTemplate InvalidTemplate { get; set; }

  protected override DataTemplate OnSelectTemplate (object item, BindableObject container)
  {
    return ((Person)item).DateOfBirth.Year >= 1980 ? ValidTemplate : InvalidTemplate;
  }
}

O método OnSelectTemplate retorna o modelo apropriado com base no valor da propriedade DateOfBirth. O modelo a ser retornado é o valor da propriedade ValidTemplate ou da propriedade InvalidTemplate, que são definidas ao consumir o PersonDataTemplateSelector.

Uma instância da classe de seletor de modelo de dados pode ser atribuída a Xamarin.Forms propriedades de controle, como ListView.ItemTemplate. Para obter uma lista de propriedades válidas, confira Criando um DataTemplate.

Limitações

As instâncias DataTemplateSelector têm as seguintes limitações:

  • A subclasse DataTemplateSelector deve sempre retornar o mesmo modelo para os mesmos dados se consultada várias vezes.
  • A subclasse DataTemplateSelector não deve retornar outra subclasse DataTemplateSelector.
  • A subclasse DataTemplateSelector não deve retornar novas instâncias de um DataTemplate em cada chamada. Em vez disso, a mesma instância deve ser retornada. Não fazer isso criará uma perda de memória e desabilitará a virtualização.
  • No Android, não pode haver mais de 20 modelos de dados diferentes por ListView.

Consumindo um DataTemplateSelector em XAML

Em XAML, o PersonDataTemplateSelector pode ser instanciado ao declará-lo como um recurso, conforme mostrado no exemplo de código a seguir:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:local="clr-namespace:Selector;assembly=Selector" x:Class="Selector.HomePage">
    <ContentPage.Resources>
        <ResourceDictionary>
            <DataTemplate x:Key="validPersonTemplate">
                <ViewCell>
                   ...
                </ViewCell>
            </DataTemplate>
            <DataTemplate x:Key="invalidPersonTemplate">
                <ViewCell>
                   ...
                </ViewCell>
            </DataTemplate>
            <local:PersonDataTemplateSelector x:Key="personDataTemplateSelector"
                ValidTemplate="{StaticResource validPersonTemplate}"
                InvalidTemplate="{StaticResource invalidPersonTemplate}" />
        </ResourceDictionary>
    </ContentPage.Resources>
  ...
</ContentPage>

Esse nível de página ResourceDictionary define duas instâncias DataTemplate e uma instância PersonDataTemplateSelector. A instância PersonDataTemplateSelector define suas propriedades ValidTemplate e InvalidTemplate como as instâncias DataTemplate apropriadas usando a extensão de marcação StaticResource. Observe que, embora os recursos sejam definidos no ResourceDictionary da página, eles também podem ser definidos no nível de controle ou do aplicativo.

A instância PersonDataTemplateSelector é consumida atribuindo-a à propriedade ListView.ItemTemplate, conforme mostrado no exemplo de código a seguir:

<ListView x:Name="listView" ItemTemplate="{StaticResource personDataTemplateSelector}" />

Em runtime, o ListView chama o método PersonDataTemplateSelector.OnSelectTemplate para cada um dos itens na coleção subjacente, com a chamada passando o objeto de dados como o parâmetro item. O DataTemplate retornado pelo método é aplicado a esse objeto.

As capturas de tela a seguir mostram o resultado do ListView aplicando o PersonDataTemplateSelector a cada objeto na coleção subjacente:

ListView com um Seletor de Modelo de Dados

Qualquer objeto Person que tem o valor da propriedade DateOfBirth maior que ou igual a 1980 é exibido em verde, com os objetos restantes sendo exibidos em vermelho.

Consumindo um DataTemplateSelector em C#

Em C#, o PersonDataTemplateSelector pode ser instanciado e atribuído à propriedade ListView.ItemTemplate, conforme mostrado no exemplo de código a seguir:

public class HomePageCS : ContentPage
{
  DataTemplate validTemplate;
  DataTemplate invalidTemplate;

  public HomePageCS ()
  {
    ...
    SetupDataTemplates ();
    var listView = new ListView {
      ItemsSource = people,
      ItemTemplate = new PersonDataTemplateSelector {
        ValidTemplate = validTemplate,
        InvalidTemplate = invalidTemplate }
    };

    Content = new StackLayout {
      Margin = new Thickness (20),
      Children = {
        ...
        listView
      }
    };
  }
  ...  
}

A instância PersonDataTemplateSelector define suas propriedades ValidTemplate e InvalidTemplate como as instâncias DataTemplate apropriadas pelo método SetupDataTemplates. Em runtime, o ListView chama o método PersonDataTemplateSelector.OnSelectTemplate para cada um dos itens na coleção subjacente, com a chamada passando o objeto de dados como o parâmetro item. O DataTemplate retornado pelo método é aplicado a esse objeto.

Resumo

Este artigo demonstrou como criar e consumir um DataTemplateSelector. Um DataTemplateSelector pode ser usado para escolher um DataTemplate em runtime com base no valor de uma propriedade associada a dados. Isso permite que várias instâncias de DataTemplate sejam aplicadas ao mesmo tipo de objeto, para personalizar a aparência de objetos específicos.