Vinculações compiladas
As ligações de dados do .NET Multi-platform App UI (.NET MAUI) têm dois problemas principais:
- Não existe validação em tempo de compilação para expressões de ligação. Em vez disso, as associações são resolvidas em tempo de execução. Portanto, quaisquer ligações inválidas não são detetadas até o tempo de execução, quando o aplicativo não se comporta como esperado ou mensagens de erro aparecem.
- Eles não são rentáveis. As ligações são resolvidas em tempo de execução usando inspeção de objeto de uso geral (reflexão), e a sobrecarga de fazer isso varia de plataforma para plataforma.
As associações compiladas melhoram o desempenho da vinculação de dados em aplicativos .NET MAUI, resolvendo expressões de vinculação em tempo de compilação em vez de tempo de execução. Além disso, essa validação em tempo de compilação de expressões de vinculação permite uma melhor experiência de solução de problemas do desenvolvedor porque ligações inválidas são relatadas como erros de compilação.
Importante
Associações compiladas são necessárias em vez de associações baseadas em cadeia de caracteres em aplicativos NativeAOT e em aplicativos com corte completo habilitado. Para obter mais informações, consulte Trim a .NET MAUI app e Native AOT deployment.
Associações compiladas em XAML
Para usar associações compiladas em XAML, defina um atributo x:DataType
em um VisualElement para o tipo do objeto ao qual o VisualElement e seus filhos se conectarão. É recomendável definir o atributo x:DataType
no mesmo nível na hierarquia de exibição que o BindingContext está definido. No entanto, esse atributo pode ser redefinido em qualquer local em uma hierarquia de exibição.
Importante
As associações compiladas exigem o uso da compilação XAML, que é habilitada por padrão no .NET MAUI. Se você desabilitou a compilação XAML, precisará habilitá-la. Para obter mais informações, consulte Compilação XAML.
Para usar associações compiladas em XAML, o atributo x:DataType
deve ser definido como um literal de cadeia de caracteres ou um tipo usando a extensão de marcação x:Type
. No momento da compilação XAML, quaisquer expressões de vinculação inválidas serão relatadas como erros de compilação. No entanto, o compilador XAML só relatará um erro de compilação para a primeira expressão de associação inválida que encontrar. Todas as expressões de vinculação válidas definidas no VisualElement ou em seus filhos serão compiladas, independentemente de o BindingContext estar definido em XAML ou código. A compilação de uma expressão de vinculação compila um código que irá buscar um valor de uma propriedade na fonte e definir esse valor na propriedade no alvo especificada na marcação. Além disso, dependendo da expressão de ligação, o código gerado pode observar alterações no valor da propriedade de origem e atualizar a propriedade de destino , e pode enviar as alterações da propriedade de destino de volta para a propriedade de origem .
Importante
As associações compiladas são desabilitadas para quaisquer expressões de associação XAML que definam a propriedade Source
. Isso ocorre porque a propriedade Source
é sempre definida usando a extensão de marcação x:Reference
, que não pode ser resolvida em tempo de compilação.
Além disso, associações compiladas em XAML não são suportadas atualmente em associações múltiplas.
Por padrão, o .NET MAUI não produz avisos de compilação para associações XAML que não usam associações compiladas. No entanto, você pode optar por avisos de associações compiladas que estão sendo produzidos definindo a propriedade $(MauiStrictXamlCompilation)
build como true
no arquivo de projeto do seu aplicativo (*.csproj):
<MauiStrictXamlCompilation>true</MauiStrictXamlCompilation>
Por padrão, o .NET MAUI produz avisos de compilação para associações XAML que não usam associações compiladas.
Para mais informações sobre os avisos de associações XAML compiladas, consulte avisos de associações XAML compiladas.
Usar associações compiladas em XAML
O exemplo a seguir demonstra o uso de ligações compiladas entre vistas .NET MAUI e propriedades do modelo de visualização.
<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>
O ContentPage instancia o HslColorViewModel
e inicializa a propriedade Color
dentro das tags do elemento de propriedade para a propriedade BindingContext. O ContentPage também define o atributo x:DataType
como o tipo viewmodel, indicando que todas as expressões de ligação na hierarquia de exibição ContentPage serão compiladas. Isso pode ser verificado alterando qualquer uma das expressões de vinculação para vincular a uma propriedade viewmodel inexistente, o que resultará em um erro de compilação. Embora este exemplo defina o atributo x:DataType
como um literal de texto, também pode ser definido como um tipo com a extensão de marcação x:Type
. Para obter mais informações sobre a extensão de marcação x:Type
, consulte x:Type Markup Extension.
Importante
O atributo x:DataType
pode ser redefinido em qualquer ponto de uma hierarquia de exibição.
Os elementos BoxView, Label e as visualizações Slider herdam o contexto de ligação do ContentPage. Esses vistas são todos destinos vinculativos que fazem referência às propriedades de origem no modelo de exibição. Para a propriedade BoxView.Color
e a propriedade Label.Text
, as associações de dados são OneWay
– as propriedades na exibição são definidas a partir das propriedades no viewmodel. No entanto, a propriedade Slider.Value
usa uma vinculação TwoWay
. Isto permite que cada Slider seja configurado a partir do viewmodel, e também que o viewmodel seja ajustado a partir de cada Slider.
Quando o exemplo é executado pela primeira vez, os elementos BoxView, Label e Slider são todos definidos a partir do viewmodel com base na propriedade Color
inicial definida quando o viewmodel foi instanciado. À medida que os controles deslizantes são manipulados, os elementos BoxView e Label são atualizados de acordo:
Para obter mais informações sobre este seletor de cores, consulte ViewModels e notificações de alteração de propriedade.
Usar associações compiladas em XAML em um DataTemplate
As ligações em um DataTemplate são interpretadas no contexto do objeto que está sendo modelado. Portanto, ao usar associações compiladas em um DataTemplate, o DataTemplate precisa declarar o tipo de seu objeto de dados usando o atributo x:DataType
. Se não fizer isso, o DataTemplate pode herdar um x:DataType
incorreto do seu escopo pai:
<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>
O exemplo a seguir demonstra a configuração correta do x:DataType
em um DataTemplate:
<ContentPage ...
x:DataType="local:AnimalsPageViewModel">
<!-- Binding to AnimalsPageViewModel.Animals -->
<CollectionView ItemsSource="{Binding Animals}">
<CollectionView.ItemTemplate>
<DataTemplate x:DataType="local:Animal">
<!-- correct: compiler knows you want to bind to Animal.Name -->
<Label Text="{Binding Name}" />
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</ContentPage>
Embora este exemplo defina o atributo x:DataType
como um literal de cadeia de caracteres, ele também pode ser definido como um tipo com a extensão de marcação x:Type
. Para obter mais informações sobre a extensão de marcação x:Type
, consulte x:Type Markup Extension.
Compilar associações que definem a propriedade Source
Antes do .NET MAUI 9, o compilador XAML ignorava a compilação de associações que definem a propriedade Source
em vez do BindingContext. A partir do .NET MAUI 9, essas associações podem ser compiladas para aproveitar o melhor desempenho de tempo de execução. No entanto, essa otimização não está habilitada por padrão para evitar a quebra do código do aplicativo existente. Para habilitar essa otimização, defina a propriedade $(MauiEnableXamlCBindingWithSourceCompilation)
build como true
no arquivo de projeto do seu aplicativo:
<MauiEnableXamlCBindingWithSourceCompilation>true</MauiEnableXamlCBindingWithSourceCompilation>
Em seguida, certifique-se de que todas as suas ligações estão anotadas com a x:DataType
correta e que não herdam tipos de dados errados do escopo dos seus pais.
<HorizontalStackLayout BindingContext="{x:Reference slider}" x:DataType="Slider">
<Label Text="{Binding Value}" />
<Label Text="{Binding Text, Source={x:Reference entry}, x:DataType=Entry}" />
</HorizontalStackLayout>
Observação
Nos casos em que há uma ligação com um Source
, mas ele herda o x:DataType
do pai, pode haver uma incompatibilidade entre o x:DataType
e o tipo de Source
. Nesse cenário, um aviso será gerado e ocorrerá um fallback para uma ligação baseada em reflexão que resolve o caminho de vinculação em tempo de execução.
Combine associações compiladas com associações clássicas em XAML
As expressões de vinculação são compiladas apenas para a hierarquia de exibição na qual o atributo x:DataType
está definido. Por outro lado, quaisquer modos de exibição em uma hierarquia na qual o atributo x:DataType
não esteja definido usarão associações clássicas. Portanto, é possível combinar ligações compiladas e ligações clássicas em uma página. Por exemplo, na seção anterior, as visualizações dentro do DataTemplate usam associações compiladas, enquanto o BoxView, que é definido para a cor selecionada no ListView, não.
A estruturação cuidadosa de x:DataType
atributos pode, portanto, levar a uma página usando ligações compiladas e clássicas. Como alternativa, o atributo x:DataType
pode ser redefinido em qualquer ponto de uma hierarquia de exibição para null
usando a extensão de marcação x:Null
. Isso indica que todas as expressões de vinculação dentro da hierarquia de exibição usarão associações clássicas. O exemplo a seguir demonstra essa abordagem:
<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>
A raiz StackLayout define o atributo x:DataType
como sendo do tipo HslColorViewModel
, indicando que qualquer expressão de associação na hierarquia de visualização da raiz StackLayout será compilada. No entanto, o StackLayout interno redefine o atributo x:DataType
para null
com a expressão de marcação x:Null
. Portanto, as expressões de ligação dentro do StackLayout interno usam ligações clássicas. Somente o BoxView, dentro da hierarquia de exibição StackLayout raiz, usa associações compiladas.
Para obter mais informações sobre a expressão de marcação x:Null
, consulte x:Null Markup Extension.
Avisos de associações compiladas XAML
A tabela a seguir lista os avisos do compilador para associações compiladas e como resolvê-los:
Código | Mensagem | Corrigir |
---|---|---|
XC0022 |
A vinculação pode ser compilada para melhorar o desempenho do tempo de execução se x:DataType for especificado. |
Adicione x:DataType ao seu XAML para especificar o tipo do BindingContextatual. É uma prática recomendada adicionar x:DataType a todos os elementos em que o contexto de vinculação muda. |
XC0023 |
A vinculação pode ser compilada para otimizar o desempenho durante a execução se x:DataType não estiver explicitamente null . |
Substitua x:DataType="{x:Null}" pelo tipo correto. |
Código | Mensagem |
---|---|
XC0022 |
A vinculação pode ser convertida em código compilado para melhorar o desempenho durante a execução se x:DataType for especificado. Para corrigir esse aviso, adicione x:DataType ao seu XAML para especificar o tipo do BindingContextatual. É uma prática recomendada adicionar x:DataType a todos os elementos em que o contexto de vinculação muda. |
XC0023 |
A vinculação pode ser compilada para melhorar a performance em tempo de execução, se x:DataType não estiver explicitamente null . Para corrigir esse aviso, substitua x:DataType="{x:Null}" pelo tipo correto. |
XC0024 |
A vinculação pode ser compilada incorretamente, uma vez que a anotação x:DataType vem de um escopo externo. Certifique-se de anotar todos os elementos XAML DataTemplate com o x:DataType correto. Para corrigir esse aviso, verifique se todos os elementos DataTemplate estão anotados com a x:DataType correta. |
XC0025 |
A vinculação não foi compilada porque tem uma propriedade Source explicitamente definida e a compilação de ligações com Source não está habilitada. Considere habilitar essa otimização definindo o <MauiEnableXamlCBindingWithSourceCompilation>true</MauiEnableXamlCBindingWithSourceCompilation> em seu arquivo de projeto e certifique-se de que a x:DataType correta seja especificada para essa ligação. Para corrigir esse aviso, habilite a propriedade $(MauiEnableXamlCBindingWithSourceCompilation) build no arquivo de projeto e anote todas as suas associações com o x:DataType apropriado. |
Para garantir que esses avisos não sejam ignorados, considere alterar avisos específicos para erros durante a compilação com a propriedade de compilação $(WarningsAsErrors)
.
<WarningsAsErrors>$(WarningsAsErrors);XC0022;XC0023</WarningsAsErrors>
Para ignorar esses avisos, use a propriedade $(NoWarn)
build com códigos de aviso específicos:
<NoWarn>$(NoWarn);XC0022;XC0023</NoWarn>
Importante
XC0022
e XC0023
avisos serão sempre suprimidos, a menos que a propriedade de build $(MauiStrictXamlCompilation)
esteja definida como true
.
Se você definir a propriedade $(TreatWarningsAsErrors)
build para true
no arquivo de projeto do seu aplicativo, mas quiser ignorar certos avisos do compilador XAML, use a propriedade $(NoWarn)
build para silenciar esses avisos ou a propriedade build $(WarningsNotAsErrors)
para reduzir a gravidade de alguns códigos específicos.
Por padrão, o .NET MAUI produz avisos de compilação para associações XAML que não usam associações compiladas. Você pode optar por tratar avisos de associações compiladas como erros ao definir as propriedades de build $(MauiStrictXamlCompilation)
e $(TreatWarningsAsErrors)
como true
no ficheiro de projeto do seu aplicativo (*.csproj):
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<MauiStrictXamlCompilation>true</MauiStrictXamlCompilation>
Observação
Por padrão, a propriedade $(MauiStrictXamlCompilation)
build é false
a menos que você esteja publicando seu aplicativo usando corte completo ou NativeAOT.
Ligações compiladas no código
As ligações escritas em código normalmente usam caminhos de cadeia de caracteres que são resolvidos em tempo de execução com reflexão. No entanto, o método de extensão SetBinding também tem uma sobrecarga que define ligações usando um argumento Func
em vez de um caminho de cadeia de caracteres:
MyLabel.SetBinding(Label.TextProperty, static (Entry entry) => entry.Text);
Nem todos os métodos podem ser usados para definir uma ligação compilada. A expressão deve ser uma expressão simples de acesso à propriedade. Os exemplos a seguir mostram expressões de vinculação válidas e inválidas:
// 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}";
Advertência
Um erro de compilador CS0272 ocorrerá se o acessador definido para uma propriedade ou indexador estiver inacessível. Se isso ocorrer, aumente a acessibilidade do acessor.
Além disso, o método BindingBase.Create define a ligação diretamente no objeto com um Func
e retorna a ocorrência do objeto de ligação:
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()
});
Essas abordagens vinculativas compiladas oferecem os seguintes benefícios:
- Melhor desempenho de vinculação de dados resolvendo expressões de vinculação em tempo de compilação em vez de tempo de execução.
- Uma melhor experiência de solução de problemas do desenvolvedor porque associações inválidas são relatadas como erros de compilação.
- Intellisense durante a edição.
Desempenho
As associações compiladas melhoram o desempenho da vinculação de dados, com o benefício de desempenho variando:
- Uma associação compilada que usa notificação de alteração de propriedade (ou seja, uma ligação
OneWay
,OneWayToSource
ouTwoWay
) é resolvida aproximadamente 8 vezes mais rápido do que uma associação clássica. - Uma associação compilada que não usa notificação de alteração de propriedade (ou seja, uma vinculação
OneTime
) é resolvida aproximadamente 20 vezes mais rápido do que uma associação clássica. - Definir o BindingContext em uma associação compilada que usa notificação de alteração de propriedade (ou seja, uma ligação
OneWay
,OneWayToSource
ouTwoWay
) é aproximadamente 5 vezes mais rápido do que definir o BindingContext em uma associação clássica. - Definir o BindingContext em uma associação compilada que não usa notificação de alteração de propriedade (ou seja, uma vinculação
OneTime
) é aproximadamente 7 vezes mais rápido do que definir o BindingContext em uma associação clássica.
Essas diferenças de desempenho podem ser ampliadas em dispositivos móveis, dependendo da plataforma que está sendo usada, da versão do sistema operacional que está sendo usada e do dispositivo no qual o aplicativo está sendo executado.