Базовые привязки Xamarin.FormsXamarin.Forms Basic Bindings

Скачать пример Скачать примерDownload Sample Download the sample

Привязка данных Xamarin.Forms связывает пару свойств между двумя объектами, по крайней мере один из которых обычно является объектом пользовательского интерфейса.A Xamarin.Forms data binding links a pair of properties between two objects, at least one of which is usually a user-interface object. Эти два объекта называются целевым объектом и источником:These two objects are called the target and the source:

  • Целевой объект — это объект (и свойство), к которому устанавливается привязка данных.The target is the object (and property) on which the data binding is set.
  • Источник — это объект (и свойство), на который ссылается привязка данных.The source is the object (and property) referenced by the data binding.

Это различие иногда может сбивать с толку. В самом простом случае данные поступают от источника к целевому объекту, то есть целевое свойство устанавливается на основе значения свойства источника.This distinction can sometimes be a little confusing: In the simplest case, data flows from the source to the target, which means that the value of the target property is set from the value of the source property. Однако в некоторых случаях данные могут поступать от целевого объекта к источнику или перемещаться в обоих направлениях.However, in some cases, data can alternatively flow from the target to the source, or in both directions. Чтобы избежать путаницы, помните, что привязка данных всегда устанавливается к целевому объекту, даже если он предоставляет, а не получает данные.To avoid confusion, keep in mind that the target is always the object on which the data binding is set even if it's providing data rather than receiving data.

Привязки с контекстом привязкиBindings with a Binding Context

Хотя привязки данных обычно указываются полностью в XAML, полезно посмотреть привязки данных в коде.Although data bindings are usually specified entirely in XAML, it's instructive to see data bindings in code. Страница Базовая привязка кода содержит файл XAML с Label и Slider:The Basic Code Binding page contains a XAML file with a Label and a Slider:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="DataBindingDemos.BasicCodeBindingPage"
             Title="Basic Code Binding">
    <StackLayout Padding="10, 0">
        <Label x:Name="label"
               Text="TEXT"
               FontSize="48"
               HorizontalOptions="Center"
               VerticalOptions="CenterAndExpand" />

        <Slider x:Name="slider"
                Maximum="360"
                VerticalOptions="CenterAndExpand" />
    </StackLayout>
</ContentPage>

Slider имеет значение в диапазоне от 0 до 360.The Slider is set for a range of 0 to 360. Цель этой программы состоит в повороте Label с помощью объекта Slider.The intent of this program is to rotate the Label by manipulating the Slider.

Без привязки данных необходимо настроить событие ValueChanged объекта Slider в обработчике событий, который обращается к свойству Value объекта Slider и задает это значение свойству Rotation объекта Label.Without data bindings, you would set the ValueChanged event of the Slider to an event handler that accesses the Value property of the Slider and sets that value to the Rotation property of the Label. Привязка данных автоматизирует эту задачу — обработчик событий и код в ней больше не нужны.The data binding automates that job; the event handler and the code within it are no longer necessary.

Вы можете задать привязку к экземпляру любого класса, производного от BindableObject, который включает производные Element, VisualElement, View и View.You can set a binding on an instance of any class that derives from BindableObject, which includes Element, VisualElement, View, and View derivatives. Привязка всегда настраивается в целевом объекте.The binding is always set on the target object. Привязка ссылается на исходный объект.The binding references the source object. Чтобы настроить привязку данных, используйте следующие два члена класса цели.To set the data binding, use the following two members of the target class:

  • Свойство BindingContext задает исходный объект.The BindingContext property specifies the source object.
  • Метод SetBinding указывает целевое свойство и исходное свойство.The SetBinding method specifies the target property and source property.

В этом примере Label является целевым объектом привязки, а Slider — источником привязки.In this example, the Label is the binding target, and the Slider is the binding source. Изменения в источнике Slider влияют на угол поворота целевого объекта Label.Changes in the Slider source affect the rotation of the Label target. Данные поступают от источника к целевому объекту.Data flows from the source to the target.

Метод SetBinding, определенный объектом BindableObject, имеет аргумент типа BindingBase, из которого производится класс Binding, но есть другие методы SetBinding, определенные классом BindableObjectExtensions.The SetBinding method defined by BindableObject has an argument of type BindingBase from which the Binding class derives, but there are other SetBinding methods defined by the BindableObjectExtensions class. Файл с выделенным кодом в примере базовой привязки кода использует более простой метод расширения SetBinding из этого класса.The code-behind file in the Basic Code Binding sample uses a simpler SetBinding extension method from this class.

public partial class BasicCodeBindingPage : ContentPage
{
    public BasicCodeBindingPage()
    {
        InitializeComponent();

        label.BindingContext = slider;
        label.SetBinding(Label.RotationProperty, "Value");
    }
}

Объект Label является целевым объектом привязки, поэтому на этом объекте устанавливается это свойство и в нем вызывается метод.The Label object is the binding target so that's the object on which this property is set and on which the method is called. Свойство BindingContext указывает источник привязки, то есть Slider.The BindingContext property indicates the binding source, which is the Slider.

Метод SetBinding вызывается в целевом объекте привязки, но указывает свойство целевого объекта и свойство источника.The SetBinding method is called on the binding target but specifies both the target property and the source property. Свойство целевого объекта указывается как объект BindableProperty: Label.RotationProperty.The target property is specified as a BindableProperty object: Label.RotationProperty. Свойство источника указывается как строка и определяет свойство Value объекта Slider.The source property is specified as a string and indicates the Value property of Slider.

Метод SetBinding раскрывает одно из самых важных правил привязки данных:The SetBinding method reveals one of the most important rules of data bindings:

целевое свойство должно поддерживаться свойством, подходящим для привязки.The target property must be backed by a bindable property.

Это правило означает, что целевой объект должен быть экземпляром класса, производного от BindableObject.This rule implies that the target object must be an instance of a class that derives from BindableObject. Сведения о привязываемых объектах и свойствах см. в разделе Привязываемые свойства.See the Bindable Properties article for an overview of bindable objects and bindable properties.

Для свойства источника такого правила нет. Оно указывается как строка.There is no such rule for the source property, which is specified as a string. На внутреннем уровне используется отражение для доступа к фактическому свойству.Internally, reflection is used to access the actual property. В данном случае, однако, свойство Value также поддерживается привязываемым свойством.In this particular case, however, the Value property is also backed by a bindable property.

Код можно упростить. Привязываемое свойство RotationProperty определяется элементом VisualElement и наследуется Label и ContentPage, поэтому имя класса не требуется в вызове метода SetBinding:The code can be simplified somewhat: The RotationProperty bindable property is defined by VisualElement, and inherited by Label and ContentPage as well, so the class name isn't required in the SetBinding call:

label.SetBinding(RotationProperty, "Value");

Но лучше включить имя класса, чтобы помнить о целевом объекте.However, including the class name is a good reminder of the target object.

При изменении свойства объекта Slider объект Label поворачивается соответствующим образом:As you manipulate the Slider, the Label rotates accordingly:

Базовая привязка кодаBasic Code Binding

Страница Базовая привязка Xaml идентична странице Базовая привязка кода, но она определяет всю привязку данных в XAML:The Basic Xaml Binding page is identical to Basic Code Binding except that it defines the entire data binding in XAML:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="DataBindingDemos.BasicXamlBindingPage"
             Title="Basic XAML Binding">
    <StackLayout Padding="10, 0">
        <Label Text="TEXT"
               FontSize="80"
               HorizontalOptions="Center"
               VerticalOptions="CenterAndExpand"
               BindingContext="{x:Reference Name=slider}"
               Rotation="{Binding Path=Value}" />

        <Slider x:Name="slider"
                Maximum="360"
                VerticalOptions="CenterAndExpand" />
    </StackLayout>
</ContentPage>

Как и в коде, привязка данных задается в целевом объекте, то есть Label.Just as in code, the data binding is set on the target object, which is the Label. Используются два расширения разметки XAML.Two XAML markup extensions are involved. Их легко узнать по разделителям в виде фигурной скобки:These are instantly recognizable by the curly brace delimiters:

  • Расширение разметки x:Reference требуется для ссылки на исходный объект, то есть Slider с именем slider.The x:Reference markup extension is required to reference the source object, which is the Slider named slider.
  • Расширения разметки Binding связывает свойство Rotation объекта Label со свойством Value объекта Slider.The Binding markup extension links the Rotation property of the Label to the Value property of the Slider.

Дополнительные сведения о расширениях разметки XAML см. в статье Расширения разметки XAML.See the article XAML Markup Extensions for more information about XAML markup extensions. Расширение разметки x:Reference поддерживается классом ReferenceExtension; Binding поддерживается классом BindingExtension.The x:Reference markup extension is supported by the ReferenceExtension class; Binding is supported by the BindingExtension class. Как указывают префиксы пространства имен XML, x:Reference является частью спецификации XAML 2009, хотя Binding является частью Xamarin.Forms.As the XML namespace prefixes indicate, x:Reference is part of the XAML 2009 specification, while Binding is part of Xamarin.Forms. Обратите внимание, что в фигурных скобках нет кавычек.Notice that no quotation marks appear within the curly braces.

Легко забыть расширения разметки x:Reference при задании BindingContext.It's easy to forget the x:Reference markup extension when setting the BindingContext. Нередко разработчики ошибочно задают свойство непосредственно для имени источника привязки следующим образом.It's common to mistakenly set the property directly to the name of the binding source like this:

BindingContext="slider"

Это неправильно.But that's not right. Эта разметка задает свойство BindingContext для объекта string, в котором символы складываются в слово slider.That markup sets the BindingContext property to a string object whose characters spell "slider"!

Обратите внимание, что свойство источника указано в свойстве Path класса BindingExtension, которое соответствует свойству Path класса Binding.Notice that the source property is specified with the Path property of BindingExtension, which corresponds with the Path property of the Binding class.

Разметку, показанную на странице Базовая привязка XAML, можно упростить. Расширения разметки XAML, такие как x:Reference и Binding, могут иметь определенные атрибуты свойства содержимого. Для расширения разметки XAML это означает, что имя свойства не нужно отображать.The markup shown on the Basic XAML Binding page can be simplified: XAML markup extensions such as x:Reference and Binding can have content property attributes defined, which for XAML markup extensions means that the property name doesn't need to appear. Свойство Name является свойством содержимого x:Reference, а свойство Path является свойством содержимого Binding, то есть их можно удалить из выражения:The Name property is the content property of x:Reference, and the Path property is the content property of Binding, which means that they can be eliminated from the expressions:

<Label Text="TEXT"
       FontSize="80"
       HorizontalOptions="Center"
       VerticalOptions="CenterAndExpand"
       BindingContext="{x:Reference slider}"
       Rotation="{Binding Value}" />

Привязки без контекста привязкиBindings without a Binding Context

Свойство BindingContext является важным компонентом привязок данных, но оно не всегда необходимо.The BindingContext property is an important component of data bindings, but it is not always necessary. Вместо этого можно указать исходный объект в вызове SetBinding или расширении разметки Binding.The source object can instead be specified in the SetBinding call or the Binding markup extension.

Это показано в примере Альтернативной привязки кода.This is demonstrated in the Alternative Code Binding sample. Файл XAML аналогичен примеру Базовая привязка кода, только объект Slider определен для управления свойством Scale объекта Label.The XAML file is similar to the Basic Code Binding sample except that the Slider is defined to control the Scale property of the Label. Поэтому Slider имеет значение в диапазоне от –2 до 2:For that reason, the Slider is set for a range of –2 to 2:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="DataBindingDemos.AlternativeCodeBindingPage"
             Title="Alternative Code Binding">
    <StackLayout Padding="10, 0">
        <Label x:Name="label"
               Text="TEXT"
               FontSize="40"
               HorizontalOptions="Center"
               VerticalOptions="CenterAndExpand" />

        <Slider x:Name="slider"
                Minimum="-2"
                Maximum="2"
                VerticalOptions="CenterAndExpand" />
    </StackLayout>
</ContentPage>

Файл с выделенным кодом задает привязку с помощью метода SetBinding, определенного объектом BindableObject.The code-behind file sets the binding with the SetBinding method defined by BindableObject. Аргумент является конструктором для класса Binding:The argument is a constructor for the Binding class:

public partial class AlternativeCodeBindingPage : ContentPage
{
    public AlternativeCodeBindingPage()
    {
        InitializeComponent();

        label.SetBinding(Label.ScaleProperty, new Binding("Value", source: slider));
    }
}

Конструктор Binding имеет 6 параметров, поэтому параметр source указан с именованным аргументом.The Binding constructor has 6 parameters, so the source parameter is specified with a named argument. Аргумент является объектом slider.The argument is the slider object.

Выполнение программы может быть немного неожиданным:Running this program might be a little surprising:

Альтернативная привязка кодаAlternative Code Binding

На экране iOS слева показано, как выглядит экран, когда страница открывается в первый раз.The iOS screen on the left shows how the screen looks when the page first appears. Где объект Label?Where is the Label?

Проблема в том, что Slider имеет начальное значение 0.The problem is that the Slider has an initial value of 0. В результате свойство Scale объекта Label также имеет значение 0 и его значение по умолчанию 1 переопределяется.This causes the Scale property of the Label to be also set to 0, overriding its default value of 1. Поэтому объект Label поначалу не видно.This results in the Label being initially invisible. Как показано на снимках экрана Android и универсальной платформы Windows (UWP), вы можете управлять объектом Slider, чтобы объект Label снова появился, но его первоначальное исчезновение сбивает с толку.As the Android and Universal Windows Platform (UWP) screenshots demonstrate, you can manipulate the Slider to make the Label appear again, but its initial disappearance is disconcerting.

Из следующей статьи вы узнаете, как избежать этой проблемы с помощью инициализации объекта Slider из значения по умолчанию свойства Scale.You'll discover in the next article how to avoid this problem by initializing the Slider from the default value of the Scale property.

Примечание

Класс VisualElement также определяет свойства ScaleX и ScaleY, которые могут масштабировать VisualElement по-разному в горизонтальном и вертикальном направлениях.The VisualElement class also defines ScaleX and ScaleY properties, which can scale the VisualElement differently in the horizontal and vertical directions.

Страница Альтернативная привязка XAML показывает эквивалентную привязку полностью в XAML:The Alternative XAML Binding page shows the equivalent binding entirely in XAML:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="DataBindingDemos.AlternativeXamlBindingPage"
             Title="Alternative XAML Binding">
    <StackLayout Padding="10, 0">
        <Label Text="TEXT"
               FontSize="40"
               HorizontalOptions="Center"
               VerticalOptions="CenterAndExpand"
               Scale="{Binding Source={x:Reference slider},
                               Path=Value}" />

        <Slider x:Name="slider"
                Minimum="-2"
                Maximum="2"
                VerticalOptions="CenterAndExpand" />
    </StackLayout>
</ContentPage>

Теперь расширение разметки Binding имеет два свойства — Source и Path, разделенные запятой.Now the Binding markup extension has two properties set, Source and Path, separated by a comma. Если вы предпочитаете, они могут отображаться на одной строке:They can appear on the same line if you prefer:

Scale="{Binding Source={x:Reference slider}, Path=Value}" />

Свойство Source задано встроенному расширению разметки x:Reference, которое в противном случае имеет тот же синтаксис, что и параметр BindingContext.The Source property is set to an embedded x:Reference markup extension that otherwise has the same syntax as setting the BindingContext. Обратите внимание, что в фигурных скобках нет кавычек и эти два свойства должны быть разделены запятой.Notice that no quotation marks appear within the curly braces, and that the two properties must be separated by a comma.

Свойство содержимого расширения разметки Binding — Path, но часть Path= расширения разметки можно исключить только в том случае, если это первое свойство в выражении.The content property of the Binding markup extension is Path, but the Path= part of the markup extension can only be eliminated if it is the first property in the expression. Чтобы исключить часть Path=, необходимо поменять два свойства местами:To eliminate the Path= part, you need to swap the two properties:

Scale="{Binding Value, Source={x:Reference slider}}" />

Хотя расширения разметки XAML обычно разделены с помощью фигурных скобок, они также могут быть выражены как элементы объекта:Although XAML markup extensions are usually delimited by curly braces, they can also be expressed as object elements:

<Label Text="TEXT"
       FontSize="40"
       HorizontalOptions="Center"
       VerticalOptions="CenterAndExpand">
    <Label.Scale>
        <Binding Source="{x:Reference slider}"
                 Path="Value" />
    </Label.Scale>
</Label>

Теперь свойства Source и Path являются обычными атрибутами XAML: значения заключены в кавычки и атрибуты не разделяются запятыми.Now the Source and Path properties are regular XAML attributes: The values appear within quotation marks and the attributes are not separated by a comma. Расширение разметки x:Reference также может стать элементом объекта:The x:Reference markup extension can also become an object element:

<Label Text="TEXT"
       FontSize="40"
       HorizontalOptions="Center"
       VerticalOptions="CenterAndExpand">
    <Label.Scale>
        <Binding Path="Value">
            <Binding.Source>
                <x:Reference Name="slider" />
            </Binding.Source>
        </Binding>
    </Label.Scale>
</Label>

Это нестандартный синтаксис, но иногда он необходим, если используются составные объекты.This syntax isn't common, but sometimes it's necessary when complex objects are involved.

Пока в примерах мы задавали свойство BindingContext и свойство Source класса Binding для расширения разметки x:Reference, чтобы ссылаться на другое представление на странице.The examples shown so far set the BindingContext property and the Source property of Binding to an x:Reference markup extension to reference another view on the page. Эти два свойства имеют тип Object, и их можно задать для любого объекта, который содержит свойства, подходящие для источников привязки.These two properties are of type Object, and they can be set to any object that includes properties that are suitable for binding sources.

В следующих статьях вы увидите, что можно задать свойство BindingContext или Source для расширения разметки x:Static, чтобы ссылаться на значение статического свойства или поля, или расширения разметки StaticResource, чтобы ссылаться на объект, хранящийся в словаре ресурсов, или непосредственно для объекта, который обычно (но не всегда) является экземпляром модели представления.In the articles ahead, you'll discover that you can set the BindingContext or Source property to an x:Static markup extension to reference the value of a static property or field, or a StaticResource markup extension to reference an object stored in a resource dictionary, or directly to an object, which is generally (but not always) an instance of a ViewModel.

Свойство BindingContext также может быть задано для объекта Binding, чтобы свойства Source и Path объекта Binding определяли контекст привязки.The BindingContext property can also be set to a Binding object so that the Source and Path properties of Binding define the binding context.

Наследование контекста привязкиBinding Context Inheritance

В этой статье вы увидели, что можно указать исходный объект с помощью свойства BindingContext или Source объекта Binding.In this article, you've seen that you can specify the source object using the BindingContext property or the Source property of the Binding object. Если заданы оба свойства, свойство Source объекта Binding имеет приоритет над BindingContext.If both are set, the Source property of the Binding takes precedence over the BindingContext.

Свойство BindingContext имеет очень важную характеристику:The BindingContext property has an extremely important characteristic:

настройка свойства BindingContext наследуется через визуальное дерево.The setting of the BindingContext property is inherited through the visual tree.

Как вы увидите, это может быть очень удобно для упрощения выражений привязки, а в некоторых случаях —, особенно в сценариях "модель — представление — модель представления" (MVVM) —, даже необходимо.As you'll see, this can be very handy for simplifying binding expressions, and in some cases — particularly in Model-View-ViewModel (MVVM) scenarios — it is essential.

Пример Наследование контекста привязки — это простая демонстрация наследования контекста привязки:The Binding Context Inheritance sample is a simple demonstration of the inheritance of the binding context:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="DataBindingDemos.BindingContextInheritancePage"
             Title="BindingContext Inheritance">
    <StackLayout Padding="10">

        <StackLayout VerticalOptions="FillAndExpand"
                     BindingContext="{x:Reference slider}">

            <Label Text="TEXT"
                   FontSize="80"
                   HorizontalOptions="Center"
                   VerticalOptions="EndAndExpand"
                   Rotation="{Binding Value}" />

            <BoxView Color="#800000FF"
                     WidthRequest="180"
                     HeightRequest="40"
                     HorizontalOptions="Center"
                     VerticalOptions="StartAndExpand"
                     Rotation="{Binding Value}" />
        </StackLayout>

        <Slider x:Name="slider"
                Maximum="360" />

    </StackLayout>
</ContentPage>

Свойство BindingContext объекта StackLayout устанавливается для объекта slider.The BindingContext property of the StackLayout is set to the slider object. Этот контекст привязки наследуется объектами Label и BoxView, оба из которых имеют свойства Rotation, зависящие от свойства Value объекта Slider:This binding context is inherited by both the Label and the BoxView, both of which have their Rotation properties set to the Value property of the Slider:

Наследование контекста привязкиBinding Context Inheritance

В следующей статье вы увидите, как режим привязки может изменить поток данных между целевым и исходным объектами.In the next article, you'll see how the binding mode can change the flow of data between target and source objects.

Другие видео о Xamarin см. на Channel 9 и YouTube.Find more Xamarin videos on Channel 9 and YouTube.