Скомпилированные привязки
Привязки данных .NET Multi-platform App UI (.NET MAUI) имеют две основные проблемы:
- Не выполняется проверка выражений привязки во время компиляции. Вместо этого привязки разрешаются во время выполнения. Таким образом, недопустимые привязки проявляются только во время выполнения в виде нарушений в поведении приложения или сообщений об ошибке.
- Затрачиваются избыточные ресурсы. Привязки разрешаются во время выполнения с использованием проверки объектов общего назначения (отражение), причем объем затрачиваемых на это ресурсов зависит от платформы.
Скомпилированные привязки повышают производительность привязки данных в приложениях .NET MAUI, разрешая выражения привязки во время компиляции, а не во время выполнения. Кроме того, такая проверка выражений привязки во время компиляции позволяет повысить эффективность устранения неполадок, поскольку недопустимые привязки выявляются как ошибки построения.
Внимание
Скомпилированные привязки требуются вместо строковых привязок в приложениях NativeAOT и в приложениях с поддержкой полной обрезки. Дополнительные сведения см. в разделе об обрезке приложения .NET MAUI и развертывания Собственного AOT.
Скомпилированные привязки в XAML
Чтобы использовать скомпилированные привязки в XAML, задайте x:DataType
атрибут для VisualElement типа объекта, к которому VisualElement привязываются его дочерние элементы. Атрибут x:DataType
рекомендуется задавать на том же уровне иерархии представления, на котором задается BindingContext. Однако этот атрибут может быть переопределен в любом месте иерархии представлений.
Внимание
Скомпилированные привязки требуют использования компиляции XAML, которая включена по умолчанию в .NET MAUI. Если вы отключили компиляцию XAML, ее необходимо включить. Дополнительные сведения см. в разделе Компиляция XAML.
Чтобы использовать скомпилированные привязки в XAML, x:DataType
атрибут должен иметь строковый литерал или тип с помощью x:Type
расширения разметки. Во время компиляции XAML любые недопустимые выражения привязки будут выявляться как ошибки построения. Тем не менее компилятор XAML будет возвращать ошибку построения только для первого обнаруженного недопустимого выражения привязки. Любые допустимые выражения привязки, которые определены для объекта VisualElement или его дочерних объектов, будут скомпилированы независимо от того, задается ли BindingContext в XAML или в коде. При компиляции выражения привязки создается скомпилированный код, который получает значение из свойства источника и присваивает его свойству адресата, который указан в разметке. Кроме того, в зависимости от выражения привязки созданный код может наблюдать изменения в значении свойства источника и обновляет свойство адресата, а также может отправлять изменения из адресата обратно в источник.
Внимание
Скомпилированные привязки отключены для любых выражений привязки XAML, определяющих Source
свойство. Это связано с тем, что свойство Source
всегда задается с использованием расширения разметки x:Reference
, которое не может быть разрешено во время компиляции.
Кроме того, скомпилированные привязки в XAML в настоящее время не поддерживаются в нескольких привязках.
По умолчанию .NET MAUI не создает предупреждения сборки для привязок XAML, которые не используют скомпилированные привязки. Однако вы можете выбрать скомпилированные предупреждения о привязках, создаваемые путем задания $(MauiStrictXamlCompilation)
свойства true
сборки в файле проекта приложения (*.csproj):
<MauiStrictXamlCompilation>true</MauiStrictXamlCompilation>
По умолчанию .NET MAUI создает предупреждения сборки для привязок XAML, которые не используют скомпилированные привязки.
Дополнительные сведения о предупреждениях компилированных привязок XAML см . в предупреждениях скомпилированных привязок XAML.
Использование скомпилированных привязок в XAML
В следующем примере показано использование скомпилированных привязок между представлениями .NET MAUI и свойствами viewmodel:
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:DataBindingDemos"
x:Class="DataBindingDemos.CompiledColorSelectorPage"
x:DataType="local:HslColorViewModel"
Title="Compiled Color Selector">
<ContentPage.BindingContext>
<local:HslColorViewModel Color="Sienna" />
</ContentPage.BindingContext>
...
<StackLayout>
<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>
Создает ContentPage экземпляр HslColorViewModel
свойства и инициализирует Color
свойство в тегах элементов свойства для BindingContext свойства. Кроме того, атрибут ContentPage определяется x:DataType
как тип viewmodel, указывающий, что любые выражения привязки в иерархии представлений ContentPage будут скомпилированы. Это можно проверить, изменив любое выражение привязки так, чтобы оно было привязано к несуществующему свойству модели представления, что приведет к ошибке сборки. Хотя в этом примере атрибуту x:DataType
присваивается строковый литерал, для него также можно задать тип с расширением разметки x:Type
. Дополнительные сведения о расширении разметки x:Type
см. в разделе Расширение разметки x:Type.
Внимание
Обратите внимание, что атрибут x:DataType
может быть переопределен в любом месте иерархии представления.
Элементы BoxView, Label и представления Slider наследуют контекст привязки из представления ContentPage. Эти представления являются целевыми объектами привязки, которые ссылаются на свойства источника в модели представления. Для свойств BoxView.Color
и Label.Text
привязки данных имеют режим OneWay
: свойства в представлении задаются по свойствам в модели представления. Тем не менее свойство Slider.Value
использует привязку TwoWay
. Поэтому каждый объект Slider задается из модели представления, а модель представления задается из каждого объекта Slider.
При первом запуске BoxViewLabel примера элементы и Slider элементы задаются из модели представления на основе начального Color
свойства, заданного при создании экземпляра модели представления. По мере управления BoxView Label ползунками элементы обновляются соответствующим образом:
Дополнительные сведения об этом селекторе цветов см. в разделе ViewModels и уведомления об изменении свойств.
Использование скомпилированных привязок в XAML в DataTemplate
Привязки в DataTemplate интерпретируются в контексте объекта, к которому применяется шаблон. Таким образом, при использовании скомпилированных привязок в DataTemplate объект DataTemplate должен объявлять тип своего объекта данных с использованием атрибута x:DataType
. Сбой этого может привести к DataTemplate наследованию неправильного x:DataType
из родительской области:
<ContentPage ...
x:DataType="local:AnimalsPageViewModel">
<!-- Binding to AnimalsPageViewModel.Animals -->
<CollectionView ItemsSource="{Binding Animals}">
<CollectionView.ItemTemplate>
<DataTemplate>
<!-- incorrect: compiler thinks you want to bind to AnimalsPageViewModel.Name -->
<Label Text="{Binding Name}" />
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</ContentPage>
В следующем примере показано правильное задание параметра x:DataType
DataTemplate:
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
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
. Класс NamedColor
использует отражение .NET для перечисления всех статических открытых полей в Colors классе и хранения их имен в коллекции, доступной из статического All
свойства. Таким образом, объект ListView заполняется экземплярами NamedColor
. Для каждого элемента в ListView в качестве контекста привязки для элемента задается объект NamedColor
. Элементы BoxView и Label в объекте ViewCell привязываются к свойствам NamedColor
.
Определяет DataTemplate атрибут NamedColor
типа, указывающий, что любые выражения привязки в DataTemplate иерархии представлений будут x:DataType
скомпилированы. Это можно проверить, изменив любое выражение привязки так, чтобы оно было привязано к несуществующему свойству NamedColor
, что приведет к ошибке построения. Хотя в этом примере атрибуту x:DataType
присваивается строковый литерал, для него также можно задать тип с расширением разметки x:Type
. Дополнительные сведения о расширении разметки x:Type
см. в разделе Расширение разметки x:Type.
При первом запуске ListView примера заполняется NamedColor
экземплярами. При выборе элемента в объекте ListView свойству BoxView.Color
присваивается цвет элемента, выбранного в ListView:
При выборе других элементов в ListView обновляется цвет элемента BoxView.
Компиляция привязок, определяющих Source
свойство
До .NET MAUI 9 компилятор XAML пропустит компиляцию привязок, определяющих Source
свойство вместо BindingContextсвойства. Из .NET MAUI 9 эти привязки можно скомпилировать, чтобы воспользоваться преимуществами повышения производительности среды выполнения. Однако эта оптимизация не включена по умолчанию, чтобы избежать нарушения существующего кода приложения. Чтобы включить эту оптимизацию, задайте $(MauiEnableXamlCBindingWithSourceCompilation)
для свойства сборки значение true
в файле проекта приложения:
<MauiEnableXamlCBindingWithSourceCompilation>true</MauiEnableXamlCBindingWithSourceCompilation>
Затем убедитесь, что все привязки помечены правильно x:DataType
и что они не наследуют неправильные типы данных из родительской области:
<HorizontalStackLayout BindingContext="{x:Reference slider}" x:DataType="Slider">
<Label Text="{Binding Value}" />
<Label Text="{Binding Text, Source={x:Reference entry}, x:DataType=Entry}" />
</HorizontalStackLayout>
Примечание.
В случаях, когда имеется привязка с объектом Source
, но она наследует x:DataType
от родительского элемента, может быть несоответствие между x:DataType
типом Source
. В этом сценарии будет создано предупреждение, а резервная привязка на основе отражения, разрешающая путь привязки во время выполнения.
Объединение скомпилированных привязок с классическими привязками в XAML
Выражения привязки компилируются только для иерархии представления, в которой определен атрибут x:DataType
. И наоборот, любые представления в иерархии, в которой не определен атрибут x:DataType
, будут использовать классические привязки. Таким образом, на одной странице могут объединяться скомпилированные и классические привязки. Например, в предыдущем разделе представления в DataTemplate используют скомпилированные привязки, а элемент BoxView, которому присваивается выбранный в ListView цвет, не использует их.
Таким образом, при тщательной проработке структуры атрибутов x:DataType
на одной странице могут одновременно использоваться скомпилированные и классические привязки. Кроме того, атрибут x:DataType
может в любом месте иерархии быть переопределен как null
с использованием расширения разметки x:Null
. Это указывает, что любые выражения привязки в иерархии этого представления будут использовать классические привязки. В следующем примере демонстрируется такой подход:
<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 будет компилироваться. Тем не менее внутреннее представление StackLayout переопределяет атрибут x:DataType
как null
с использованием расширения разметки x:Null
. Таким образом, в выражениях привязки внутреннего представления StackLayout используются классические привязки. Скомпилированные привязки используются только элементом BoxView в иерархии корневого представления StackLayout.
Дополнительные сведения о расширении разметки x:Null
см. в статье Расширение разметки x:Null.
Предупреждения о скомпилированных привязках XAML
В следующей таблице перечислены предупреждения компилятора для скомпилированных привязок и способы их устранения:
Код | Сообщение | Fix |
---|---|---|
XC0022 |
Привязка может быть скомпилирована для повышения производительности среды выполнения при x:DataType указании. |
Добавьте x:DataType в XAML, чтобы указать тип текущего BindingContext. Рекомендуется добавить x:DataType все элементы, в которых изменяется контекст привязки. |
XC0023 |
Привязка может быть скомпилирована для повышения производительности среды выполнения, если x:DataType она не является явной null . |
Замените x:DataType="{x:Null}" правильным типом. |
Код | Сообщение |
---|---|
XC0022 |
Привязка может быть скомпилирована для повышения производительности среды выполнения при x:DataType указании. Чтобы устранить это предупреждение, добавьте x:DataType в XAML тип текущего BindingContext. Рекомендуется добавить x:DataType все элементы, в которых изменяется контекст привязки. |
XC0023 |
Привязка может быть скомпилирована для повышения производительности среды выполнения, если x:DataType она не является явной null . Чтобы устранить это предупреждение, замените x:DataType="{x:Null}" правильным типом. |
XC0024 |
Привязка может быть скомпилирована неправильно, так как x:DataType заметка поступает из внешней области. Убедитесь, что вы заметите все DataTemplate элементы XAML с правильным x:DataType значением. Чтобы устранить это предупреждение, убедитесь, что все DataTemplate элементы помечены правильно x:DataType . |
XC0025 |
Привязка не была скомпилирована, так как она имеет явно заданное Source свойство и компиляция привязок с Source не включена. Рассмотрите возможность включения этой оптимизации, задав <MauiEnableXamlCBindingWithSourceCompilation>true</MauiEnableXamlCBindingWithSourceCompilation> в файле проекта правильный параметр и убедитесь, что для этой привязки задано правильное x:DataType значение. Чтобы устранить это предупреждение, включите свойство сборки в файле проекта и заметьте $(MauiEnableXamlCBindingWithSourceCompilation) все привязки соответствующим x:DataType образом. |
Чтобы убедиться, что эти предупреждения не игнорируются, рассмотрите возможность изменения конкретных предупреждений для создания ошибок со свойством $(WarningsAsErrors)
сборки:
<WarningsAsErrors>$(WarningsAsErrors);XC0022;XC0023</WarningsAsErrors>
Чтобы игнорировать эти предупреждения, используйте $(NoWarn)
свойство сборки с определенными кодами предупреждений:
<NoWarn>$(NoWarn);XC0022;XC0023</NoWarn>
Внимание
XC0022
и XC0023
предупреждения всегда будут подавляться, если $(MauiStrictXamlCompilation)
для свойства сборки не задано значение true
.
Если в файле проекта приложения задано $(TreatWarningsAsErrors)
свойство true
сборки, но вы хотите игнорировать определенные предупреждения компилятора XAML, используйте $(NoWarn)
свойство сборки для молчания этих предупреждений или $(WarningsNotAsErrors)
свойства сборки, чтобы уменьшить серьезность некоторых конкретных кодов.
По умолчанию .NET MAUI создает предупреждения сборки для привязок XAML, которые не используют скомпилированные привязки. Вы можете отказаться от скомпилированных привязок, которые обрабатываются как ошибки, задав $(MauiStrictXamlCompilation)
свойства и $(TreatWarningsAsErrors)
создайте свойства true
в файле проекта приложения (*.csproj):
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<MauiStrictXamlCompilation>true</MauiStrictXamlCompilation>
Примечание.
По умолчанию свойство сборки не публикуетсяfalse
, $(MauiStrictXamlCompilation)
если вы не публикуете приложение с помощью полной обрезки или NativeAOT.
Скомпилированные привязки в коде
Привязки, написанные в коде, обычно используют строковые пути, которые разрешаются во время выполнения с отражением. Однако метод SetBinding расширения также имеет перегрузку, которая определяет привязки с помощью Func
аргумента вместо строкового пути:
MyLabel.SetBinding(Label.TextProperty, static (Entry entry) => entry.Text);
Не все методы можно использовать для определения скомпилированной привязки. Выражение должно быть простым выражением доступа к свойствам. В следующих примерах показаны допустимые и недопустимые выражения привязки:
// Valid: Property access
static (PersonViewModel vm) => vm.Name;
static (PersonViewModel vm) => vm.Address?.Street;
// Valid: Array and indexer access
static (PersonViewModel vm) => vm.PhoneNumbers[0];
static (PersonViewModel vm) => vm.Config["Font"];
// Valid: Casts
static (Label label) => (label.BindingContext as PersonViewModel).Name;
static (Label label) => ((PersonViewModel)label.BindingContext).Name;
// Invalid: Method calls
static (PersonViewModel vm) => vm.GetAddress();
static (PersonViewModel vm) => vm.Address?.ToString();
// Invalid: Complex expressions
static (PersonViewModel vm) => vm.Address?.Street + " " + vm.Address?.City;
static (PersonViewModel vm) => $"Name: {vm.Name}";
Кроме того, Binding.Create метод задает привязку непосредственно к объекту с Func
помощью объекта и возвращает экземпляр объекта привязки:
myEntry.SetBinding(Entry.TextProperty, new MultiBinding
{
Bindings = new Collection<BindingBase>
{
Binding.Create(static (Entry entry) => entry.FontFamily, source: RelativeBindingSource.Self),
Binding.Create(static (Entry entry) => entry.FontSize, source: RelativeBindingSource.Self),
Binding.Create(static (Entry entry) => entry.FontAttributes, source: RelativeBindingSource.Self),
},
Converter = new StringConcatenationConverter()
});
Эти скомпилированные подходы к привязке обеспечивают следующие преимущества:
- Улучшена производительность привязки данных путем разрешения выражений привязки во время компиляции, а не во время выполнения.
- Лучший интерфейс устранения неполадок разработчика, так как недопустимые привязки сообщаются как ошибки сборки.
- Intellisense во время редактирования.
Производительность
Скомпилированные привязки повышают производительность привязки данных с различными преимуществами производительности:
- Скомпилированные привязки, использующие уведомление об изменении свойства (то есть привязки в режиме
OneWay
,OneWayToSource
илиTwoWay
), разрешаются примерно в 8 раз быстрее относительно классических привязок. - Скомпилированные привязки, не использующие уведомление об изменении свойства (то есть привязки в режиме
OneTime
), разрешаются примерно в 20 раз быстрее относительно классических привязок. - Задание BindingContext для скомпилированных привязок, использующих уведомление об изменении свойства (то есть для привязок в режиме
OneWay
,OneWayToSource
илиTwoWay
), выполняется примерно в 5 раз быстрее, чем задание BindingContext для классических привязок. - Задание BindingContext для скомпилированных привязок, не использующих уведомление об изменении свойства (то есть для привязок в режиме
OneTime
), выполняется примерно в 7 раз быстрее, чем задание BindingContext для классических привязок.
Выигрыш в производительности может быть еще более весомым на мобильных устройствах в зависимости от используемой платформы, версии операционной системы и типа устройства, на котором выполняется приложение.