Layouts vinculáveis em Xamarin.Forms

Os layouts vinculáveis permitem que qualquer classe de layout derivada da Layout<T> classe gere seu conteúdo vinculando-se a uma coleção de itens, com a opção de definir a aparência de cada item com um DataTemplatearquivo . Layouts associáveis são fornecidos pela classe BindableLayout, que expõe as seguintes propriedades anexadas:

  • ItemsSource – especifica a coleção de itens IEnumerable a serem exibidos pelo layout.
  • ItemTemplate – especifica o DataTemplate a ser aplicado a cada item na coleção de itens exibidos pelo layout.
  • ItemTemplateSelector – especifica o DataTemplateSelector que será usado para escolher um DataTemplate para um item em tempo de execução.

Observação

A propriedade ItemTemplate tem precedência quando as propriedades ItemTemplate e ItemTemplateSelector estão definidas.

Além disso, a classe BindableLayout expõe as seguintes propriedades associáveis:

  • EmptyView – especifica o string ou a exibição que será visualizada quando a propriedade ItemsSource for null ou quando a coleção especificada pela propriedade ItemsSource for null ou vazia. O valor padrão é null.
  • EmptyViewTemplate – especifica o DataTemplate que será exibido quando a propriedade ItemsSource for null ou quando a coleção especificada pela propriedade ItemsSource for null ou vazia. O valor padrão é null.

Observação

A propriedade EmptyViewTemplate tem precedência quando as propriedades EmptyView e EmptyViewTemplate estão definidas.

Todas essas propriedades podem ser anexadas às AbsoluteLayoutclasses , FlexLayout, GridRelativeLayout, e , que StackLayout derivam da Layout<T> classe.

A Layout<T> classe expõe uma Children coleção, à qual os elementos filho de um layout são adicionados. Quando a BindableLayout.ItemsSource propriedade é definida como uma coleção de itens e anexada a uma Layout<T>classe derivada, cada item na coleção é adicionado à Layout<T>.Children coleção para exibição pelo layout. A classe derivada do Layout<T> atualizará suas exibições filho quando a coleção subjacente for alterada. Para obter mais informações sobre o ciclo de Xamarin.Forms layout, consulte Criando um layout personalizado.

Os layouts associáveis só devem ser usados quando a coleção de itens a serem exibidos for pequena, e a rolagem e a seleção não são necessárias. Embora a rolagem possa ser fornecida encapsulando um layout associável em um ScrollView, isso não é recomendado, pois os layouts associáveis não têm virtualização da interface do usuário. Quando a rolagem é necessária, um modo de exibição rolável que inclui a virtualização da interface do usuário, como ListView ou CollectionView, deve ser usado. Não observar essa recomendação pode levar a problemas de desempenho.

Importante

Embora seja tecnicamente possível anexar um layout vinculável a qualquer classe de layout derivada da Layout<T> classe, nem sempre é prático fazê-lo, especialmente para as AbsoluteLayoutclasses , Gride RelativeLayout . Por exemplo, considere o cenário de querer exibir uma coleção de dados em um Grid usando um layout associável, em que cada item da coleção é um objeto que contém várias propriedades. Cada linha no Grid deve exibir um objeto da coleção, com cada coluna no Grid exibindo uma das propriedades do objeto. Como o DataTemplate para o layout associável só pode conter um único objeto, é necessário que esse objeto seja uma classe de layout contendo várias exibições que exibem cada uma das propriedades do objeto em uma coluna Grid específica. Embora esse cenário possa ser realizado com layouts associáveis, ele resulta em um Grid pai que contém um Grid filho para cada item na coleção associada, que é um uso altamente ineficiente e problemático do layout Grid.

Preencher um layout associável com dados

Um layout associável é preenchido com dados definindo sua propriedade ItemsSource para qualquer coleção que implemente IEnumerable e anexando-a a uma classe derivada do Layout<T>:

<Grid BindableLayout.ItemsSource="{Binding Items}" />

Este é o código C# equivalente:

IEnumerable<string> items = ...;
var grid = new Grid();
BindableLayout.SetItemsSource(grid, items);

Quando a propriedade BindableLayout.ItemsSource anexada é definida em um layout, mas a propriedade BindableLayout.ItemTemplate anexada não está definida, cada item da coleção IEnumerable será exibido por um Label que é criado pela classe BindableLayout.

Definir a aparência do item

A aparência de cada item no layout associável pode ser definida definindo a propriedade BindableLayout.ItemTemplate anexada como um DataTemplate:

<StackLayout BindableLayout.ItemsSource="{Binding User.TopFollowers}"
             Orientation="Horizontal"
             ...>
    <BindableLayout.ItemTemplate>
        <DataTemplate>
            <controls:CircleImage Source="{Binding}"
                                  Aspect="AspectFill"
                                  WidthRequest="44"
                                  HeightRequest="44"
                                  ... />
        </DataTemplate>
    </BindableLayout.ItemTemplate>
</StackLayout>

Este é o código C# equivalente:

DataTemplate circleImageTemplate = ...;
var stackLayout = new StackLayout();
BindableLayout.SetItemsSource(stackLayout, viewModel.User.TopFollowers);
BindableLayout.SetItemTemplate(stackLayout, circleImageTemplate);

Neste exemplo, cada item na TopFollowers coleção será exibido por um CircleImage modo de exibição definido no DataTemplate:

Layout vinculável com um DataTemplate

Para obter mais informações sobre modelos de dados, consulte Xamarin.Forms Modelos de dados.

Escolher a aparência do item em tempo de execução

A aparência de cada item no layout associável pode ser escolhida em tempo de execução, com base no valor do item, definindo a propriedade BindableLayout.ItemTemplateSelector anexada como um DataTemplateSelector:

<FlexLayout BindableLayout.ItemsSource="{Binding User.FavoriteTech}"
            BindableLayout.ItemTemplateSelector="{StaticResource TechItemTemplateSelector}"
            ... />

Este é o código C# equivalente:

DataTemplateSelector dataTemplateSelector = new TechItemTemplateSelector { ... };
var flexLayout = new FlexLayout();
BindableLayout.SetItemsSource(flexLayout, viewModel.User.FavoriteTech);
BindableLayout.SetItemTemplateSelector(flexLayout, dataTemplateSelector);

O DataTemplateSelector usado no aplicativo de exemplo é mostrado no exemplo a seguir:

public class TechItemTemplateSelector : DataTemplateSelector
{
    public DataTemplate DefaultTemplate { get; set; }
    public DataTemplate XamarinFormsTemplate { get; set; }

    protected override DataTemplate OnSelectTemplate(object item, BindableObject container)
    {
        return (string)item == "Xamarin.Forms" ? XamarinFormsTemplate : DefaultTemplate;
    }
}

A classe TechItemTemplateSelector define as propriedades DefaultTemplate e XamarinFormsTemplate do DataTemplate como modelos de dados diferentes. O OnSelectTemplate método retorna o XamarinFormsTemplate, que exibe um item em vermelho escuro com um coração ao lado, quando o item é igual a "Xamarin.Forms". Quando o item não é igual a "Xamarin.Forms", o OnSelectTemplate método retorna o DefaultTemplate, que exibe um item usando a cor padrão de um Label:

Layout vinculável com um DataTemplateSelector

Para obter mais informações sobre seletores de modelo de dados, consulte Criando um Xamarin.Forms DataTemplateSelector.

Exibir uma cadeia de caracteres quando os dados não estiverem disponíveis

A propriedade EmptyView pode ser definida como uma cadeia de caracteres, que será exibida por um Label quando a propriedade ItemsSource for null ou quando a coleção especificada pela propriedade ItemsSource for null ou vazia. O XAML a seguir mostra um exemplo desse cenário:

<StackLayout BindableLayout.ItemsSource="{Binding UserWithoutAchievements.Achievements}"
             BindableLayout.EmptyView="No achievements">
    ...
</StackLayout>

O resultado é que, quando a coleção associada a dados fornull, a cadeia de caracteres definida como o valor da propriedade EmptyView é exibida:

Captura de tela de uma exibição vazia de cadeia de caracteres de layout vinculável, no iOS e Android

Visualizar exibições quando os dados não estiverem disponíveis

A propriedade EmptyView pode ser definida como um modo de exibição, que será exibido quando a propriedade ItemsSource for nullou quando a coleção especificada pela propriedade ItemsSource fornull ou vazia. Pode ser uma exibição única ou uma exibição que contém várias exibições filho. O exemplo XAML a seguir mostra a propriedade EmptyView definida como uma exibição que contém várias exibições filho:

<StackLayout BindableLayout.ItemsSource="{Binding UserWithoutAchievements.Achievements}">
    <BindableLayout.EmptyView>
        <StackLayout>
            <Label Text="None."
                   FontAttributes="Italic"
                   FontSize="{StaticResource smallTextSize}" />
            <Label Text="Try harder and return later?"
                   FontAttributes="Italic"
                   FontSize="{StaticResource smallTextSize}" />
        </StackLayout>
    </BindableLayout.EmptyView>
    ...
</StackLayout>

O resultado é que, quando a coleção associada a dados fornull, o StackLayout e suas exibições filho são exibidas.

Captura de ecrã de uma vista vazia de esquema vinculável com várias vistas, no iOS e Android

Da mesma forma, o EmptyViewTemplate pode ser definido como um DataTemplate, que será exibido quando a propriedade ItemsSource for null, ou quando a coleção especificada pela propriedade ItemsSource for null ou vazia. O DataTemplate pode conter uma única exibição ou uma exibição que contenha várias exibições filho. Além disso, o BindingContext do EmptyViewTemplate será herdado do BindingContext doBindableLayout. O exemplo XAML a seguir mostra a propriedade EmptyViewTemplate definida como um DataTemplate que contém uma única exibição:

<StackLayout BindableLayout.ItemsSource="{Binding UserWithoutAchievements.Achievements}">
    <BindableLayout.EmptyViewTemplate>
        <DataTemplate>
            <Label Text="{Binding Source={x:Reference usernameLabel}, Path=Text, StringFormat='{0} has no achievements.'}" />
        </DataTemplate>
    </BindableLayout.EmptyViewTemplate>
    ...
</StackLayout>

O resultado é que, quando a coleção associada a dados é null, o Label no DataTemplate é exibido:

Captura de tela de um modelo de exibição vazio de layout vinculável, no iOS e Android

Observação

A propriedade EmptyViewTemplate não pode ser definida por meio de um DataTemplateSelector.

Escolher um EmptyView em tempo de execução

Exibições que serão exibidas como um EmptyView quando os dados não estiverem disponíveis podem ser definidas como objetos ContentView em um ResourceDictionary. A propriedade EmptyView pode ser definida como uma ContentView específica, com base em alguma lógica de negócios, em tempo de execução. O XAML a seguir mostra um exemplo desse cenário:

<ContentPage ...>
    <ContentPage.Resources>
        ...    
        <ContentView x:Key="BasicEmptyView">
            <StackLayout>
                <Label Text="No achievements."
                       FontSize="14" />
            </StackLayout>
        </ContentView>
        <ContentView x:Key="AdvancedEmptyView">
            <StackLayout>
                <Label Text="None."
                       FontAttributes="Italic"
                       FontSize="14" />
                <Label Text="Try harder and return later?"
                       FontAttributes="Italic"
                       FontSize="14" />
            </StackLayout>
        </ContentView>
    </ContentPage.Resources>

    <StackLayout>
        ...
        <Switch Toggled="OnEmptyViewSwitchToggled" />

        <StackLayout x:Name="stackLayout"
                     BindableLayout.ItemsSource="{Binding UserWithoutAchievements.Achievements}">
            ...
        </StackLayout>
    </StackLayout>
</ContentPage>

O XAML define dois objetos ContentView no ResourceDictionary no nível da página, com o objeto Switch controlando qual objeto ContentView será definido como o valor da propriedade EmptyView. Quando o Switch é alternado, o manipulador de eventos OnEmptyViewSwitchToggled executa o método ToggleEmptyView:

void ToggleEmptyView(bool isToggled)
{
    object view = isToggled ? Resources["BasicEmptyView"] : Resources["AdvancedEmptyView"];
    BindableLayout.SetEmptyView(stackLayout, view);
}

O método ToggleEmptyView define a propriedade EmptyView do objeto stackLayout como um dos dois objetos ContentView armazenados no ResourceDictionary, com base no valor da propriedade Switch.IsToggled. Em seguida, quando a coleção associada a dados é null, o ContentView objeto definido como a EmptyView propriedade é exibido:

Captura de tela da opção de exibição vazia em tempo de execução, no iOS e Android