Associações compiladas

Procurar amostra. Procurar no exemplo

As associações de dados do .NET MAUI (.NET Multi-platform App UI) têm dois problemas principais:

  1. Não há validação em tempo de compilação para expressões de associação. Em vez disso, as associações são resolvidas em runtime. Sendo assim, associações inválidas não são detectadas até o runtime, quando o aplicativo não se comporta conforme esperado ou mensagens de erro são exibidas.
  2. Elas não são econômicas. Associações são resolvidas em runtime usando a inspeção de objeto para uso geral (reflexão), e a sobrecarga de fazer isso varia de uma plataforma para outra.

Associações compiladas melhoram o desempenho de associação de dados em aplicativos do .NET MAUI resolvendo expressões de associação em tempo de compilação, em vez de runtime. Além disso, essa validação em tempo de compilação das expressões de associação permite uma melhor experiência de solução de problemas para o desenvolvedor porque associações inválidas são relatadas como erros de build.

Importante

As 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 Cortar um aplicativo .NET MAUI e implantação de AOT nativa.

Associações compiladas em XAML

Para usar associações compiladas em XAML, defina um atributo x:DataType em um VisualElement como o tipo do objeto ao qual VisualElement e seus filhos se associarão. É recomendável definir o atributo x:DataType no mesmo nível da hierarquia de exibição em que BindingContext está definido. No entanto, esse atributo pode ser redefinido em qualquer localização de uma hierarquia de exibição.

Importante

As associações compiladas demandam 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 saber mais, consulte XAML Compilation (Compilação de 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 tempo de compilação de XAML, as expressões de associação inválidas serão relatadas como erros de build. No entanto, o compilador XAML relatará um erro de build somente para a primeira expressão de associação inválida que encontrar. Expressões de associação válidas definidas no VisualElement ou em seus filhos serão compiladas, independentemente de BindingContext estar definido no XAML ou no código. Compilar uma expressão de associação gera o código compilado que obterá um valor de uma propriedade na origem e o definirá na propriedade de destino especificada na marcação. Além disso, dependendo da expressão de associação, o código gerado poderá observar alterações no valor da propriedade de origem e atualizar a propriedade de destino, e pode enviar por push alterações do destino para a origem.

Importante

As associações compiladas estão desabilitadas para expressões de associação XAML que definem a propriedade Source. Isso acontece 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, no momento as associações compiladas em XAML não são suportadas em várias associações.

Por padrão, o .NET MAUI não produz avisos de build 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 ao definir a propriedade de build $(MauiStrictXamlCompilation) como true no arquivo de projeto do aplicativo (*.csproj):

<MauiStrictXamlCompilation>true</MauiStrictXamlCompilation>

Por padrão, o .NET MAUI produz avisos de build para associações XAML que não usam associações compiladas.

Para obter mais informações sobre avisos de associações compiladas XAML, consulte Avisos de associações compiladas XAML.

Usar associações compiladas em XAML

O exemplo a seguir demonstra o uso de associações compiladas entre exibições do .NET MAUI e propriedades do modelo de exibiçã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 de marcas de elemento de propriedade para a propriedade BindingContext. O ContentPage também define o atributo x:DataType como o tipo viewmodel, indicando que as expressões de associação na hierarquia de exibição do ContentPage serão compiladas. Isso pode ser verificado alterando qualquer uma das expressões de associação para se associar a uma propriedade viewmodel inexistente, o que causará um erro de build. 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 mais informações sobre a extensão de marcação x:Type, consulte Extensão de marcação x:Type.

Importante

O atributo x:DataType pode ser redefinido em qualquer ponto de uma hierarquia de exibição.

Os elementos BoxView, Label e as exibições Slider herdam o contexto de associação da ContentPage. Essas exibições são destinos de associação que referenciam as propriedades de origem no viewmodel. Para as propriedades BoxView.Color e Label.Text, as associações de dados são OneWay – as propriedades na exibição são definidas nas propriedades do viewmodel. No entanto, a propriedade Slider.Value usa uma associação TwoWay. Isso permite que cada Slider seja definido no viewmodel e também que o viewmodel seja definido em cada Slider.

Quando o exemplo é executado pela primeira vez, os elementos BoxView, Label e Slider são definidos no viewmodel com base na propriedade Color inicial definida quando foi criada uma instância do viewmodel. Conforme os controles deslizantes são manipulados, os elementos BoxView e Label são atualizados:

Seletor de cores compilado.

Para mais informações sobre este seletor de cores, confira ViewModels e notificações de alteração de propriedade.

Usar associações compiladas em XAML em um DataTemplate

Associaçõ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. Não fazer isso pode resultar na DataTemplate herança de um incorreto x:DataType de 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 de x:DataType um :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>

A propriedade ListView.ItemsSource é definida como a propriedade estática NamedColor.All. A classe NamedColor usa a reflexão do .NET para enumerar todos os campos públicos estáticos na classe Colors e armazená-los com seus nomes em uma coleção acessível na propriedade estática All. Portanto, o ListView é preenchido com todas as instâncias de NamedColor. Para cada item em ListView, o contexto de associação para o item é definido como um objeto NamedColor. Os elementos BoxView e Label no ViewCell estão associados às propriedades em NamedColor.

O DataTemplate define o atributo x:DataType como o tipo NamedColor, indicando que as expressões de associação na hierarquia de exibição DataTemplate serão compiladas. Isso pode ser verificado alterando qualquer uma das expressões de associação para se associar a uma propriedade NamedColor inexistente, o que causará um erro de build. 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 mais informações sobre a extensão de marcação x:Type, consulte Extensão de marcação x:Type.

Quando o exemplo é executado pela primeira vez, o ListView é preenchido com instâncias de NamedColor. Quando um item no ListView é selecionado, a propriedade BoxView.Color é definida como a cor do item selecionado no ListView:

Lista de cores compilada.

Selecionar outros itens no ListView atualiza a cor do BoxView.

Compilar associações que definem a Source propriedade

Antes do .NET MAUI 9, o compilador XAML ignorava a compilação de associações que definem a Source propriedade em vez do BindingContext. No .NET MAUI 9, essas associações podem ser compiladas para aproveitar o melhor desempenho do runtime. No entanto, essa otimização não é habilitada por padrão para evitar a quebra do código do aplicativo existente. Para habilitar essa otimização, defina a $(MauiEnableXamlCBindingWithSourceCompilation) propriedade build como true no arquivo de projeto do seu aplicativo:

<MauiEnableXamlCBindingWithSourceCompilation>true</MauiEnableXamlCBindingWithSourceCompilation>

Em seguida, verifique se todas as associações estão anotadas com o correto x:DataType e se não herdam tipos de dados incorretos do escopo pai:

<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 associação com um Source, mas ele herda o x:DataType do pai, pode haver uma incompatibilidade entre o x:DataType e o tipo do Source. Nesse cenário, um aviso será gerado e ocorrerá um fallback para uma associação baseada em reflexão que resolve o caminho de associação em runtime.

Combinar associações compiladas a associações clássicas em XAML

Expressões de associação são compiladas apenas para a hierarquia de exibição em que o atributo x:DataType está definido. Por outro lado, exibições em uma hierarquia na qual o atributo x:DataType não está definido usarão associações clássicas. Portanto, é possível combinar associações compiladas e associações clássicas em uma página. Por exemplo, na seção anterior, os modos de exibição dentro do DataTemplate usam associações compiladas, enquanto o BoxView definido como a cor selecionada no ListView não faz isso.

Estruturar cuidadosamente os atributos x:DataType, portanto, pode levar a uma página que usa associações compiladas e clássicas. Como alternativa, o atributo x:DataType pode ser redefinido a qualquer momento em uma hierarquia de exibição como null usando a extensão de marcação x:Null. Fazer isso indica que qualquer expressão de associação de dentro da hierarquia de exibição usará 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>   

O StackLayout raiz define o atributo x:DataType como o tipo HslColorViewModel, indicando que as expressões de associação na hierarquia de exibição StackLayout raiz serão compiladas. No entanto, o StackLayout interno redefine o atributo x:DataType como null com a expressão de marcação x:Null. Portanto, as expressões de associação no StackLayout interno usam associaçõ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, confira Extensão de marcação x:Null.

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 Fix
XC0022 A associação pode ser compilada para melhorar o desempenho do tempo de execução, se x:DataType especificada. Adicione x:DataType ao XAML para especificar o tipo do arquivo .BindingContext É uma prática recomendada adicionar x:DataType a todos os elementos em que o contexto de associação é alterado.
XC0023 A associação pode ser compilada para melhorar o desempenho do tempo de execução se x:DataType não for explicitamente null. Substitua x:DataType="{x:Null}" pelo tipo certo.
Código Mensagem
XC0022 A associação pode ser compilada para melhorar o desempenho do tempo de execução, se x:DataType especificada.

Para corrigir esse aviso, adicione x:DataType ao seu XAML para especificar o tipo do arquivo .BindingContext É uma prática recomendada adicionar x:DataType a todos os elementos em que o contexto de associação é alterado.
XC0023 A associação pode ser compilada para melhorar o desempenho do tempo de execução se x:DataType não for explicitamente null.

Para corrigir esse aviso, substitua x:DataType="{x:Null}" pelo tipo correto.
XC0024 A associação pode ser compilada incorretamente, pois a x:DataType anotação vem de um escopo externo. Certifique-se de anotar todos os DataTemplate elementos XAML com o x:DataType.

Para corrigir esse aviso, certifique-se de que todos os DataTemplate elementos sejam anotados com o x:DataType.
XC0025 A associação não foi compilada porque tem uma propriedade definida Source explicitamente e a compilação de associações com Source não está habilitada. Considere habilitar essa otimização definindo o no arquivo de <MauiEnableXamlCBindingWithSourceCompilation>true</MauiEnableXamlCBindingWithSourceCompilation> projeto e verifique se o correto x:DataType está especificado para essa associação.

Para corrigir esse aviso, habilite a $(MauiEnableXamlCBindingWithSourceCompilation) propriedade build no arquivo de projeto e anote todas as ligações com o x:DataType.

Para garantir que esses avisos não sejam ignorados, considere alterar avisos específicos para erros de build com a $(WarningsAsErrors) propriedade build:

<WarningsAsErrors>$(WarningsAsErrors);XC0022;XC0023</WarningsAsErrors>

Para ignorar esses avisos, use a $(NoWarn) propriedade build com códigos de aviso específicos:

<NoWarn>$(NoWarn);XC0022;XC0023</NoWarn>

Importante

XC0022 e XC0023 os avisos sempre serão suprimidos, a menos que a propriedade build $(MauiStrictXamlCompilation) seja definida como true.

Se você definir a $(TreatWarningsAsErrors) propriedade build como true no arquivo de projeto do aplicativo, mas quiser ignorar determinados avisos do compilador XAML, use a $(NoWarn) propriedade build para silenciar esses avisos ou a $(WarningsNotAsErrors) propriedade build para reduzir a gravidade de alguns códigos específicos.

Por padrão, o .NET MAUI produz avisos de build para associações XAML que não usam associações compiladas. Você pode optar por avisos de associações compiladas sendo tratados como erros definindo as $(MauiStrictXamlCompilation) propriedades e $(TreatWarningsAsErrors) build como true no arquivo de projeto do aplicativo (*.csproj):

<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<MauiStrictXamlCompilation>true</MauiStrictXamlCompilation>

Observação

Por padrão, a $(MauiStrictXamlCompilation) propriedade build é false a menos que você esteja publicando seu aplicativo usando corte completo ou NativeAOT.

Associações compiladas no código

As associações escritas em código normalmente usam caminhos de cadeia de caracteres que são resolvidos no runtime com reflexão. No entanto, o método de extensão SetBinding também tem uma sobrecarga que define associaçõ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 associação compilada. A expressão deve ser uma expressão de acesso de propriedade simples. Os exemplos a seguir mostram expressões de associaçã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}";

Além disso, o método BindingBase.Create define a associação diretamente no objeto com um Func, e retorna a instância do objeto da associaçã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 de associação compiladas fornecem os seguintes benefícios:

  • Maior desempenho da associação de dados resolvendo expressões de associação em tempo de compilação, em vez de runtime.
  • Uma melhor experiência de solução de problemas para o desenvolvedor porque associações inválidas são relatadas como erros de build.
  • IntelliSense durante a edição.

Desempenho

Associações compiladas melhoram o desempenho da associação de dados, com benefícios de desempenho variáveis.

  • Uma associação compilada que usa notificação de alteração de propriedade (ou seja, uma associação OneWay, OneWayToSource ou TwoWay) é resolvida aproximadamente oito 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 associação OneTime) é resolvida aproximadamente vinte 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 associação OneWay, OneWayToSource ou TwoWay) é aproximadamente cinco 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 associação OneTime), é aproximadamente sete vezes mais rápido do que definir o BindingContext em uma associação clássica.

Essas diferenças de desempenho podem aumentar em dispositivos móveis, dependendo da plataforma usada, da versão do sistema operacional usado e do dispositivo no qual o aplicativo está em execução.