Compartilhar via


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 redução total ativada.

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.

Observação

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 que não usam associações compiladas, a menos que você tenha habilitado NativeAOT para seu aplicativo. 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>

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.

O exemplo a seguir demonstra como usar associações compiladas em 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.

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.

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 Binding.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.