Скомпилированные привязки Xamarin.FormsXamarin.Forms Compiled Bindings

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

Скомпилированные привязки разрешаются быстрее классических, что позволяет повысить производительность привязки данных в приложениях Xamarin.Forms.Compiled bindings are resolved more quickly than classic bindings, therefore improving data binding performance in Xamarin.Forms applications.

С привязками данных связаны две основные проблемы:Data bindings have two main problems:

  1. Не выполняется проверка выражений привязки во время компиляции.There's no compile-time validation of binding expressions. Вместо этого привязки разрешаются во время выполнения.Instead, bindings are resolved at runtime. Таким образом, недопустимые привязки проявляются только во время выполнения в виде нарушений в поведении приложения или сообщений об ошибке.Therefore, any invalid bindings aren't detected until runtime when the application doesn't behave as expected or error messages appear.
  2. Затрачиваются избыточные ресурсы.They aren't cost efficient. Привязки разрешаются во время выполнения с использованием проверки объектов общего назначения (отражение), причем объем затрачиваемых на это ресурсов зависит от платформы.Bindings are resolved at runtime using general-purpose object inspection (reflection), and the overhead of doing this varies from platform to platform.

Использование скомпилированных привязок позволяет оптимизировать производительность привязки данных в приложениях Xamarin.Forms за счет разрешения выражений привязки во время компиляции, а не во время выполнения.Compiled bindings improve data binding performance in Xamarin.Forms applications by resolving binding expressions at compile-time rather than runtime. Кроме того, такая проверка выражений привязки во время компиляции позволяет повысить эффективность устранения неполадок, поскольку недопустимые привязки выявляются как ошибки построения.In addition, this compile-time validation of binding expressions enables a better developer troubleshooting experience because invalid bindings are reported as build errors.

Процесс использования скомпилированных привязок выглядит следующим образом:The process for using compiled bindings is to:

  1. Включите компиляцию XAML.Enable XAML compilation. Дополнительные сведения о компиляции XAML см. в статье Компиляция XAML.For more information about XAML compilation, see XAML Compilation.
  2. Присвойте атрибуту x:DataType объекта VisualElement тип объекта, к которому будут привязываться объект VisualElement и его дочерние объекты.Set an x:DataType attribute on a VisualElement to the type of the object that the VisualElement and its children will bind to. Обратите внимание, что этот атрибут может быть переопределен в любом месте иерархии представления.Note that this attribute can be re-defined at any location in a view hierarchy.

Примечание

Атрибут x:DataType рекомендуется задавать на том же уровне иерархии представления, на котором задается BindingContext.It's recommended to set the x:DataType attribute at the same level in the view hierarchy as the BindingContext is set.

Во время компиляции XAML любые недопустимые выражения привязки будут выявляться как ошибки построения.At XAML compile time, any invalid binding expressions will be reported as build errors. Тем не менее компилятор XAML будет возвращать ошибку построения только для первого обнаруженного недопустимого выражения привязки.However, the XAML compiler will only report a build error for the first invalid binding expression that it encounters. Любые допустимые выражения привязки, которые определены для объекта VisualElement или его дочерних объектов, будут скомпилированы независимо от того, задается ли BindingContext в XAML или в коде.Any valid binding expressions that are defined on the VisualElement or its children will be compiled, regardless of whether the BindingContext is set in XAML or code. При компиляции выражения привязки создается скомпилированный код, который получает значение из свойства источника и присваивает его свойству адресата, который указан в разметке.Compiling a binding expression generates compiled code that will get a value from a property on the source, and set it on the property on the target that's specified in the markup. Кроме того, в зависимости от выражения привязки созданный код может наблюдать изменения в значении свойства источника и обновляет свойство адресата, а также может отправлять изменения из адресата обратно в источник.In addition, depending on the binding expression, the generated code may observe changes in the value of the source property and refresh the target property, and may push changes from the target back to the source.

Важно!

Скомпилированные привязки на данный момент отключены для любых выражений привязки, которые определяют свойство Source.Compiled bindings are currently disabled for any binding expressions that define the Source property. Это связано с тем, что свойство Source всегда задается с использованием расширения разметки x:Reference, которое не может быть разрешено во время компиляции.This is because the Source property is always set using the x:Reference markup extension, which can't be resolved at compile time.

Использование скомпилированных привязокUsing compiled bindings

На странице скомпилированного средства выбора цвета демонстрируется использование скомпилированных привязок между представлениями Xamarin.Forms и свойствами модели представления:The Compiled Color Selector page demonstrates using compiled bindings between Xamarin.Forms views and ViewModel properties:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:local="clr-namespace:DataBindingDemos"
             x:Class="DataBindingDemos.CompiledColorSelectorPage"
             Title="Compiled Color Selector">
    ...
    <StackLayout x:DataType="local:HslColorViewModel">
        <StackLayout.BindingContext>
            <local:HslColorViewModel Color="Sienna" />
        </StackLayout.BindingContext>
        <BoxView Color="{Binding Color}"
                 ... />
        <StackLayout Margin="10, 0">
            <Label Text="{Binding Name}" />
            <Slider Value="{Binding Hue}" />
            <Label Text="{Binding Hue, StringFormat='Hue = {0:F2}'}" />
            <Slider Value="{Binding Saturation}" />
            <Label Text="{Binding Saturation, StringFormat='Saturation = {0:F2}'}" />
            <Slider Value="{Binding Luminosity}" />
            <Label Text="{Binding Luminosity, StringFormat='Luminosity = {0:F2}'}" />
        </StackLayout>
    </StackLayout>    
</ContentPage>

Корневое представление StackLayout создает экземпляр HslColorViewModel и инициализирует свойство Color в тегах элемента свойства для свойство BindingContext.The root StackLayout instantiates the HslColorViewModel and initializes the Color property within property element tags for the BindingContext property. Это корневое представление StackLayout также определяет атрибут x:DataType как тип модели представления, указывая, что любое выражение привязки в иерархии корневого представления StackLayout будет компилироваться.This root StackLayout also defines the x:DataType attribute as the ViewModel type, indicating that any binding expressions in the root StackLayout view hierarchy will be compiled. Это можно проверить, изменив любое выражение привязки так, чтобы оно было привязано к несуществующему свойству модели представления, что приведет к ошибке построения.This can be verified by changing any of the binding expressions to bind to a non-existent ViewModel property, which will result in a build error.

Важно!

Обратите внимание, что атрибут x:DataType может быть переопределен в любом месте иерархии представления.The x:DataType attribute can be re-defined at any point in a view hierarchy.

Элементы BoxView, Label и представления Slider наследуют контекст привязки из представления StackLayout.The BoxView, Label elements, and Slider views inherit the binding context from the StackLayout. Эти представления являются адресатами (целевыми объектами) привязки, которые ссылаются на свойства источника в модели представления.These views are all binding targets that reference source properties in the ViewModel. Для свойства BoxView.Color и свойства Label.Text привязки данных имеют режим OneWay: свойства в представлении задаются по свойствам в модели представления.For the BoxView.Color property, and the Label.Text property, the data bindings are OneWay – the properties in the view are set from the properties in the ViewModel. Тем не менее свойство Slider.Value использует привязку TwoWay.However, the Slider.Value property uses a TwoWay binding. Поэтому каждый объект Slider задается из модели представления, а модель представления задается из каждого объекта Slider.This allows each Slider to be set from the ViewModel, and also for the ViewModel to be set from each Slider.

При первом запуске приложения элементы BoxView, Label и элементы Slider получают значения из модели представления в зависимости от первоначального свойства Color, заданного при создании экземпляра модели представления.When the application is first run, the BoxView, Label elements, and Slider elements are all set from the ViewModel based on the initial Color property set when the ViewModel was instantiated. Эти действия показаны на следующих снимках экрана:This is shown in the following screenshots:

Скомпилированное средство выбора цветаCompiled Color Selector

При выполнении действий с ползунками элементы BoxView и Label соответствующим образом обновляются.As the sliders are manipulated, the BoxView and Label elements are updated accordingly.

Дополнительные сведения о средстве выбора цвета см. в статье Уведомления об изменении свойства и модели представления.For more information about this color selector, see ViewModels and Property-Change Notifications.

Использование скомпилированных привязок в DataTemplateUsing compiled bindings in a DataTemplate

Привязки в DataTemplate интерпретируются в контексте объекта, к которому применяется шаблон.Bindings in a DataTemplate are interpreted in the context of the object being templated. Таким образом, при использовании скомпилированных привязок в DataTemplate объект DataTemplate должен объявлять тип своего объекта данных с использованием атрибута x:DataType.Therefore, when using compiled bindings in a DataTemplate, the DataTemplate needs to declare the type of its data object using the x:DataType attribute.

На странице скомпилированного списка цветов демонстрируется использование скомпилированных привязок в DataTemplate:The Compiled Color List page demonstrates using compiled bindings in a DataTemplate:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:local="clr-namespace:DataBindingDemos"
             x:Class="DataBindingDemos.CompiledColorListPage"
             Title="Compiled Color List">
    <Grid>
        ...
        <ListView x:Name="colorListView"
                  ItemsSource="{x:Static local:NamedColor.All}"
                  ... >
            <ListView.ItemTemplate>
                <DataTemplate x:DataType="local:NamedColor">
                    <ViewCell>
                        <StackLayout Orientation="Horizontal">
                            <BoxView Color="{Binding Color}"
                                     ... />
                            <Label Text="{Binding FriendlyName}"
                                   ... />
                        </StackLayout>
                    </ViewCell>
                </DataTemplate>
            </ListView.ItemTemplate>
        </ListView>
        <!-- The BoxView doesn't use compiled bindings -->
        <BoxView Color="{Binding Source={x:Reference colorListView}, Path=SelectedItem.Color}"
                 ... />
    </Grid>
</ContentPage>

Свойство ListView.ItemsSource получает статическое свойство NamedColor.All.The ListView.ItemsSource property is set to the static NamedColor.All property. Класс NamedColor использует отражение .NET для перечисления всех открытых статических полей в структуре Color и их хранения с именами в коллекции, доступной из статического свойства All.The NamedColor class uses .NET reflection to enumerate all the static public fields in the Color structure, and to store them with their names in a collection that is accessible from the static All property. Таким образом, объект ListView заполняется экземплярами NamedColor.Therefore, the ListView is filled with all of the NamedColor instances. Для каждого элемента в ListView в качестве контекста привязки для элемента задается объект NamedColor.For each item in the ListView, the binding context for the item is set to a NamedColor object. Элементы BoxView и Label в объекте ViewCell привязываются к свойствам NamedColor.The BoxView and Label elements in the ViewCell are bound to NamedColor properties.

Обратите внимание, что DataTemplate определяет атрибут x:DataType как тип NamedColor, указывая, что любое выражение привязки в иерархии представления DataTemplate будет компилироваться.Note that the DataTemplate defines the x:DataType attribute to be the NamedColor type, indicating that any binding expressions in the DataTemplate view hierarchy will be compiled. Это можно проверить, изменив любое выражение привязки так, чтобы оно было привязано к несуществующему свойству NamedColor, что приведет к ошибке построения.This can be verified by changing any of the binding expressions to bind to a non-existent NamedColor property, which will result in a build error.

При первом запуске приложения объект ListView заполняется экземплярами NamedColor.When the application is first run, the ListView is populated with NamedColor instances. При выборе элемента в объекте ListView свойству BoxView.Color присваивается цвет элемента, выбранного в ListView:When an item in the ListView is selected, the BoxView.Color property is set to the color of the selected item in the ListView:

Скомпилированный список цветовCompiled Color List

При выборе других элементов в ListView обновляется цвет элемента BoxView.Selecting other items in the ListView updates the color of the BoxView.

Объединение скомпилированных привязок с классическими привязкамиCombining compiled bindings with classic bindings

Выражения привязки компилируются только для иерархии представления, в которой определен атрибут x:DataType.Binding expressions are only compiled for the view hierarchy that the x:DataType attribute is defined on. И наоборот, любые представления в иерархии, в которой не определен атрибут x:DataType, будут использовать классические привязки.Conversely, any views in a hierarchy on which the x:DataType attribute is not defined will use classic bindings. Таким образом, на одной странице могут объединяться скомпилированные и классические привязки.It's therefore possible to combine compiled bindings and classic bindings on a page. Например, в предыдущем разделе представления в DataTemplate используют скомпилированные привязки, а элемент BoxView, которому присваивается выбранный в ListView цвет, не использует их.For example, in the previous section the views within the DataTemplate use compiled bindings, while the BoxView that's set to the color selected in the ListView does not.

Таким образом, при тщательной проработке структуры атрибутов x:DataType на одной странице могут одновременно использоваться скомпилированные и классические привязки.Careful structuring of x:DataType attributes can therefore lead to a page using compiled and classic bindings. Кроме того, атрибут x:DataType может в любом месте иерархии быть переопределен как null с использованием расширения разметки x:Null.Alternatively, the x:DataType attribute can be re-defined at any point in a view hierarchy to null using the x:Null markup extension. Это указывает, что любые выражения привязки в иерархии этого представления будут использовать классические привязки.Doing this indicates that any binding expressions within the view hierarchy will use classic bindings. Этот подход демонстрируется на странице смешанных привязок:The Mixed Bindings page demonstrates this approach:

<StackLayout x:DataType="local:HslColorViewModel">
    <StackLayout.BindingContext>
        <local:HslColorViewModel Color="Sienna" />
    </StackLayout.BindingContext>
    <BoxView Color="{Binding Color}"
             VerticalOptions="FillAndExpand" />
    <StackLayout x:DataType="{x:Null}"
                 Margin="10, 0">
        <Label Text="{Binding Name}" />
        <Slider Value="{Binding Hue}" />
        <Label Text="{Binding Hue, StringFormat='Hue = {0:F2}'}" />
        <Slider Value="{Binding Saturation}" />
        <Label Text="{Binding Saturation, StringFormat='Saturation = {0:F2}'}" />
        <Slider Value="{Binding Luminosity}" />
        <Label Text="{Binding Luminosity, StringFormat='Luminosity = {0:F2}'}" />
    </StackLayout>
</StackLayout>   

Корневое представление StackLayout задает атрибут x:DataType как тип HslColorViewModel, указывая, что любое выражение привязки в иерархии корневого представления StackLayout будет компилироваться.The root StackLayout sets the x:DataType attribute to be the HslColorViewModel type, indicating that any binding expression in the root StackLayout view hierarchy will be compiled. Тем не менее внутреннее представление StackLayout переопределяет атрибут x:DataType как null с использованием расширения разметки x:Null.However, the inner StackLayout redefines the x:DataType attribute to null with the x:Null markup expression. Таким образом, в выражениях привязки внутреннего представления StackLayout используются классические привязки.Therefore, the binding expressions within the inner StackLayout use classic bindings. Скомпилированные привязки используются только элементом BoxView в иерархии корневого представления StackLayout.Only the BoxView, within the root StackLayout view hierarchy, uses compiled bindings.

Дополнительные сведения о расширении разметки x:Null см. в статье Расширение разметки x:Null.For more information about the x:Null markup expression, see x:Null Markup Extension.

ПроизводительностьPerformance

Использование скомпилированных привязок позволяет повысить производительность привязки данных с учетом различных факторов.Compiled bindings improve data binding performance, with the performance benefit varying. Результаты модульного тестирования демонстрируют следующее:Unit testing reveals that:

  • Скомпилированные привязки, использующие уведомление об изменении свойства (то есть привязки в режиме OneWay, OneWayToSource или TwoWay), разрешаются примерно в 8 раз быстрее относительно классических привязок.A compiled binding that uses property-change notification (i.e. a OneWay, OneWayToSource, or TwoWay binding) is resolved approximately 8 times quicker than a classic binding.
  • Скомпилированные привязки, не использующие уведомление об изменении свойства (то есть привязки в режиме OneTime), разрешаются примерно в 20 раз быстрее относительно классических привязок.A compiled binding that doesn't use property-change notification (i.e. a OneTime binding) is resolved approximately 20 times quicker than a classic binding.
  • Задание BindingContext для скомпилированных привязок, использующих уведомление об изменении свойства (то есть для привязок в режиме OneWay, OneWayToSource или TwoWay), выполняется примерно в 5 раз быстрее, чем задание BindingContext для классических привязок.Setting the BindingContext on a compiled binding that uses property change notification (i.e. a OneWay, OneWayToSource, or TwoWay binding) is approximately 5 times quicker than setting the BindingContext on a classic binding.
  • Задание BindingContext для скомпилированных привязок, не использующих уведомление об изменении свойства (то есть для привязок в режиме OneTime), выполняется примерно в 7 раз быстрее, чем задание BindingContext для классических привязок.Setting the BindingContext on a compiled binding that doesn't use property change notification (i.e. a OneTime binding) is approximately 7 times quicker than setting the BindingContext on a classic binding.

Выигрыш в производительности может быть еще более весомым на мобильных устройствах в зависимости от используемой платформы, версии операционной системы и типа устройства, на котором выполняется приложение.These performance differences can be magnified on mobile devices, dependent upon the platform being used, the version of the operating system being used, and the device on which the application is running.