Привязываемые макеты в Xamarin.Forms

Download Sample Скачайте пример

Привязываемые макеты позволяют любому классу макета, наследуемому из Layout<T> класса, создавать его содержимое путем привязки к коллекции элементов, при этом параметром можно задать внешний вид каждого элемента.DataTemplate Привязываемые макеты предоставляются классом BindableLayout , который предоставляет следующие присоединенные свойства:

  • ItemsSource — указывает коллекцию элементов, отображаемых IEnumerable макетом.
  • ItemTemplate — задает DataTemplate применение к каждому элементу в коллекции элементов, отображаемых макетом.
  • ItemTemplateSelector — указывает DataTemplateSelector , что будет использоваться для выбора DataTemplate элемента во время выполнения.

Примечание.

Свойство ItemTemplate имеет приоритет, если заданы оба ItemTemplateItemTemplateSelector свойства.

Кроме того, BindableLayout класс предоставляет следующие привязываемые свойства:

  • EmptyView — указывает string или представление, которое будет отображаться, если ItemsSource свойство равно nullили когда коллекция, указанная ItemsSource свойством null , или пуста. Значение по умолчанию — null.
  • EmptyViewTemplate — задает DataTemplate значение, которое будет отображаться, если ItemsSource свойство равно nullили когда коллекция, указанная ItemsSource свойством, или null пуста. Значение по умолчанию — null.

Примечание.

Свойство EmptyViewTemplate имеет приоритет, если заданы оба EmptyViewEmptyViewTemplate свойства.

Все эти свойства могут быть присоединены к AbsoluteLayoutклассам , GridFlexLayoutRelativeLayoutи StackLayout классам, производным от Layout<T> класса.

Класс Layout<T> предоставляет Children коллекцию, к которой добавляются дочерние элементы макета. BindableLayout.ItemsSource Если свойству присвоено значение коллекции элементов и присоединено к Layout<T>производному классу, каждый элемент в коллекции добавляется в Layout<T>.Children коллекцию для отображения макетом. Затем производный Layout<T>класс обновляет дочерние представления при изменении базовой коллекции. Дополнительные сведения о цикле макета Xamarin.Forms см. в разделе "Создание пользовательского макета".

Привязываемые макеты следует использовать только в том случае, если коллекция элементов для отображения невелика, а прокрутка и выделение не требуются. Хотя прокрутка может быть предоставлена путем упаковки привязываемого макета в a ScrollView, это не рекомендуется, так как привязываемые макеты не имеют виртуализации пользовательского интерфейса. Если требуется прокрутка, необходимо использовать прокручиваемое представление, включающее виртуализацию пользовательского интерфейса, например ListView или CollectionView. Сбой в наблюдении за этой рекомендацией может привести к проблемам с производительностью.

Внимание

Хотя технически можно подключить привязываемый макет к любому классу макета, наследуемому от Layout<T> класса, это не всегда удобно сделать, особенно для AbsoluteLayoutклассов и GridRelativeLayout классов. Например, рассмотрим сценарий отображения коллекции данных в Grid привязываемом макете, где каждый элемент в коллекции является объектом, содержащим несколько свойств. Каждая строка в Grid коллекции должна отображать объект из коллекции с каждым столбцом в Grid отображении одного из свойств объекта. DataTemplate Так как для привязываемого макета может содержаться только один объект, это необходимо для того, чтобы этот объект был классом макета, содержащим несколько представлений, каждый из которых отображает одно из свойств объекта в определенном Grid столбце. Хотя этот сценарий можно реализовать с привязываемыми макетами, он приводит к Grid родительскому элементу, содержашему дочерний Grid элемент в связанной коллекции, что является очень неэффективным и проблемным использованием макета Grid .

Заполнение привязываемого макета данными

Привязываемый макет заполняется данными, задав его ItemsSource свойство любой коллекции, реализующей IEnumerableи присоединяя ее к производном классу Layout<T>:

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

Эквивалентный код на C# выглядит так:

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

BindableLayout.ItemsSource Если присоединенное свойство задано в макете, но BindableLayout.ItemTemplate присоединенное свойство не задано, каждый элемент в IEnumerable коллекции будет отображаться Label классом, созданным классомBindableLayout.

Определение внешнего вида элемента

Внешний вид каждого элемента в привязываемом макете можно определить, задав BindableLayout.ItemTemplate присоединенное свойство следующим 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>

Эквивалентный код на C# выглядит так:

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

В этом примере каждый элемент в TopFollowers коллекции будет отображаться представлениемCircleImage, определенным в :DataTemplate

Bindable layout with a DataTemplate

Дополнительные сведения о шаблонах данных см. в разделе Общие сведения о шаблонах данныхXamarin.Forms.

Выбор внешнего вида элемента во время выполнения

Внешний вид каждого элемента в привязываемом макете можно выбрать во время выполнения на основе значения элемента, задав BindableLayout.ItemTemplateSelector присоединенное свойство следующим DataTemplateSelectorобразом:

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

Эквивалентный код на C# выглядит так:

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

Используемый DataTemplateSelector в примере приложения показан в следующем примере:

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

Класс TechItemTemplateSelector определяет DefaultTemplate и XamarinFormsTemplateDataTemplate свойства, которые задаются различными шаблонами данных. Метод OnSelectTemplate возвращает XamarinFormsTemplateэлемент , который отображает элемент в темно-красном цвете с сердцем рядом с ним, когда элемент равен "Xamarin.Forms". Если элемент не равен "Xamarin.Forms", OnSelectTemplate метод возвращает DefaultTemplateэлемент, который отображает элемент с использованием цвета по умолчанию:Label

Bindable layout with a DataTemplateSelector

Дополнительные сведения о селекторах шаблонов данных см. в разделе "Создание Xamarin.Forms объекта DataTemplateSelector".

Отображение строки при недоступности данных

Свойство EmptyView может быть задано строкой, которая будет отображаться Label в случае, если ItemsSource свойство имеет nullзначение, или когда коллекция, указанная ItemsSource свойством null , или пуста. В следующем коде XAML показан пример этого сценария:

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

Результатом является то, что при сборе nullпривязанных к данным строке EmptyView отображается значение свойства:

Screenshot of a bindable layout string empty view, on iOS and Android

Отображение представлений при недоступности данных

Свойство EmptyView может быть задано в представлении, которое будет отображаться, если ItemsSource свойство равно nullили когда коллекция, указанная ItemsSource свойством null , или пуста. Это может быть одно представление или представление, содержащее несколько дочерних представлений. В следующем примере XAML показано EmptyView свойство, заданное для представления, содержащего несколько дочерних представлений:

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

Результатом является то, что при отображении коллекции nullStackLayout привязанных к данным представлений и его дочерних представлений.

Screenshot of a bindable layout empty view with multiple views, on iOS and Android

Аналогичным образом EmptyViewTemplate можно задать DataTemplateзначение , которое будет отображаться, если ItemsSource свойство равно nullили когда коллекция, указанная ItemsSource свойством, или null пуста. Может DataTemplate содержать одно представление или представление, содержащее несколько дочерних представлений. Кроме того, BindingContextEmptyViewTemplate наследуется от BindingContext объекта BindableLayout. В следующем примере XAML показан набор EmptyViewTemplate свойств, DataTemplate содержащий одно представление:

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

Результатом является то, что при сборе nullLabelDataTemplate привязанных к данным данных отображается в ней:

Screenshot of a bindable layout empty view template, on iOS and Android

Примечание.

Свойство EmptyViewTemplate не может быть задано через объект DataTemplateSelector.

Выбор EmptyView во время выполнения

Представления, которые будут отображаться как EmptyView недоступные данные, можно определить как ContentView объекты в объекте ResourceDictionary. Затем EmptyView свойство может быть задано на основе определенной ContentViewбизнес-логики во время выполнения. В следующем коде XAML показан пример этого сценария:

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

XAML определяет два ContentView объекта на уровне ResourceDictionaryстраницы с Switch объектом, определяющим, какой ContentView объект будет задан в качестве EmptyView значения свойства. Switch При переключение OnEmptyViewSwitchToggled обработчик событий выполняет ToggleEmptyView метод:

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

Метод ToggleEmptyView задает EmptyView свойство stackLayout объекта одному из двух ContentView объектов, хранящихся в объекте ResourceDictionary, на основе значения Switch.IsToggled свойства. Затем, когда коллекция nullпривязанных к данным данных, ContentView объект, заданный в качестве EmptyView свойства, отображается:

Screenshot of empty view choice at runtime, on iOS and Android