Vinculação de dados com tipos incompatíveis

Concluído

Às vezes, os dados que você está usando não correspondem ao tipo de dados da propriedade do controle que exibe os dados. Por exemplo, você pode ter um valor monetário armazenado em um tipo decimal e querer que seja exibido em um controle do tipo Label formatado como moeda. Um exemplo mais complicado seria com o aplicativo de meteorologia apresentado no módulo. Uma imagem deve, supostamente, ser exibida com base no valor de enumeração da previsão do tempo Sunny ou Cloudy, mas você não pode vincular o valor de enumeração da origem à propriedade de imagem de um destino. Esta unidade analisa as maneiras pelas quais você pode converter dados,

Formatação da cadeia de caracteres

Um caso comum de tipos incompatíveis é um tipo intrínseco que você deseja exibir como uma cadeia de caracteres formatada. como, por exemplo, quando você quer exibir partes de um valor de DateTime ou formatar um tipo decimal como moeda.

Suponhamos que você queira exibir o valor devido em uma fatura e tenha a seguinte propriedade no seu objeto de dados:

public decimal BillAmount { get; set; }

O valor devido acaba sendo 22,0304. Você poderia usar dois controles de rótulo para exibir uma parte em texto e o valor em dólares (ou reais), conforme demonstrado no snippet de código a seguir:

<HorizontalStackLayout>
    <Label Text="You owe" Margin="0,0,5,0" />
    <Label Text="{Binding BillAmount}" />
    <Label Text="to the bank" Margin="5,0,0,0" />
</HorizontalStackLayout>

Isso gera uma cadeia de caracteres para a interface do usuário que se parece com You owe 22.0304 to the bank, mas o símbolo da moeda está faltando e podemos ter muitas ou muito poucas casas decimais com base na moeda local. Normalmente, você processaria o valor como uma cadeia de caracteres com o especificador de formato "C" (ou moeda) no código, da seguinte maneira:

string formattedBillAmount = string.Format("{0:C}", BillAmount);

Porém, para usar formatação na vinculação de dados, você precisa ter o objeto de dados que forneça essa cadeia de caracteres formatada como uma propriedade diferente ou, alternativamente, interceptá-la de alguma forma e formatá-la por conta própria. Felizmente, as vinculações do .NET MAUI fornecem uma maneira de formatar cadeias de caracteres com a propriedade de vinculação StringFormat. A cadeia de caracteres de formato segue as mesmas regras que o método String.Format. Coloque a cadeia de caracteres de formato entre aspas para que o analisador XAML não fique confuso com as chaves. O parâmetro 0 do formato da cadeia de caracteres é o valor da propriedade processado pela vinculação.

<Label Text="{Binding BillAmount, StringFormat='You owe {0:C} to the bank'}" />

Considere o XAML a seguir, que demonstra como exibir o BillAmount usando as duas maneiras:

<VerticalStackLayout Padding="10">
    <HorizontalStackLayout>
        <Label Text="You owe" Margin="0,0,5,0" />
        <Label Text="{Binding BillAmount}" />
        <Label Text="to the bank" Margin="5,0,0,0" />
    </HorizontalStackLayout>
    <HorizontalStackLayout>
        <Label Text="{Binding BillAmount, StringFormat='You owe {0:C} to the bank'}" />
    </HorizontalStackLayout>
</VerticalStackLayout>

A imagem a seguir ilustra o resultado produzido na tela pelo XAML:

Captura de tela de um aplicativo .NET MAUI do Android exibindo dois controles de rótulo diferentes com texto relacionado à cobrança. O texto de um rótulo é formatado com moeda em USD.

O XAML que usa a propriedade de vinculação StringFormat é mais simples do que o outro XAML, sendo que você tem acesso ao poderoso sistema de formatação de cadeia de caracteres do NET.

Conversão de tipo personalizado

A propriedade de vinculação StringFormat é conveniente para exibir um valor como uma cadeia de caracteres, mas não quando você quer converter um outro tipo em algo como uma Color ou Image. Nesses casos, você precisa escrever um código de conversão personalizado.

Suponha que você esteja solicitando que o usuário escolha uma senha e queira usar uma cor na interface do usuário para indicar a força da senha. Existem três níveis de força: fraca, boa e forte. A força é baseada no número de caracteres da senha. Para fornecer ao usuário um feedback imediato sobre a força da senha, você quer que a tela de fundo do controle Entry que contém a senha seja alterada com base na força. A imagem a seguir demonstra esses três níveis de força: fraca, boa e forte.

Captura de tela de um aplicativo .NET MAUI do Android com três controles de entrada, cada um com um plano de fundo colorido diferente.

Dos três controles de inserção na captura de tela, o primeiro tem quatro caracteres inseridos e resulta em um fundo vermelho. O segundo tem nove caracteres inseridos e um fundo amarelo. O último controle de inserção tem 15 caracteres e um fundo azul.

Esses níveis são atribuídos à enumeração Strength:

private enum Strength
{
    Weak,
    Good,
    Strong
}

Um objeto de dados é atribuído como o BindingContext da página, que contém a força da senha com a propriedade PasswordStrength. À medida que o usuário digita em uma senha, a propriedade PasswordStrength é alterada de acordo com o comprimento da senha. Como o objeto de dados contém a propriedade PasswordStrength, você vincula essa propriedade à BackgroundColor do controle Entry:

<Entry BackgroundColor="{Binding PasswordStrength} ... />

Porém, existe um problema. A PasswordStrength é do tipo Strength, enquanto BackgroundColor é uma Color. Esses tipos não são compatíveis entre si. O .NET MAUI fornece uma maneira de corrigir esses problemas de incompatibilidade de tipo: a propriedade Converter da vinculação.

Um conversor de vinculação faz exatamente o que seu nome diz: converte a origem no destino em uma vinculação. Os conversores são implementados por meio da interface do IValueConverter.

Implementar IValueConverter

Você cria sua lógica de conversão em uma classe que implementa a interface do IValueConverter. Normalmente, os nomes dessas classes terminam com o nome Converter para deixar clara a sua finalidade.

A interface IValueConverter define dois métodos:

  • Convert: converte a propriedade da origem da vinculação na propriedade da interface do usuário do destino da vinculação.

  • ConvertBack: converte a propriedade da interface do usuário do destino da vinculação de volta para a propriedade da origem da vinculação.

    Esse método é usado raramente e não é usado nesse cenário. A maioria dos conversores gera um NotSupportedException para indicar que essa conversão não tem suporte.

Aqui está o contrato da interface:

public interface IValueConverter
{
    object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture);
    object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture);
}

Lembre-se de que o cenário em que estamos trabalhando está vinculando a propriedade Entry.BackgroundColor à propriedade PasswordStrength do objeto de dados, que é uma enumeração de Strength.

<Entry BackgroundColor="{Binding PasswordStrength} ... />

A enumeração de Strength precisa ser convertida em uma Color, então o conversor precisa avaliar qual valor de Strength é fornecido e retornar uma Color diferente. O código a seguir demonstra essa conversão:

namespace MyProject.Converters;

class StrengthToColorConverter : IValueConverter
{
    public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
    {
        return (Strength)value! switch
        {
            Strength.Weak => Colors.OrangeRed,
            Strength.Good => Colors.Yellow,
            Strength.Strong => Colors.LightBlue,
            _ => Colors.LightBlue
        };
    }

    public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture) =>
        throw new NotImplementedException();
}

Vamos detalhar esse código:

  • O método Convert tem quatro parâmetros. De modo geral, você pode descartar os dois últimos parâmetros, a menos que tenha um motivo específico para usá-los.
  • O parâmetro value contém o valor de entrada. Nesse exemplo, é um valor de enumeração, Strength.
  • O parâmetro targetType é ignorado. Mas você poderia usar esse parâmetro para validar que o tipo de propriedade com a qual o conversor está sendo usado é uma Color. Isso é omitido no exemplo para simplificar.
  • Uma expressão de alternância é usada para retornar uma cor diferente com base no valor de Strength.

Uso do conversor no XAML

Após a classe do conversor ter sido criada, você precisa criar uma instância dela e referenciá-la na vinculação. A maneira padrão de instanciar o conversor está no dicionário de recursos do elemento raiz.

Primeiro, mapeie um namespace XML para o namespace do .NET que contém o conversor. No exemplo de código a seguir, o namespace XML do cvt é mapeado para o namespace MyProject.Converters no .NET:

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:cvt="clr-namespace:MyProject.Converters"
             ...

Em seguida, crie uma instância em ContentPage.Resources:

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:cvt="clr-namespace:MyProject.Converters"
             x:Class="MyProject.PasswordExample">
    <ContentPage.Resources>
        <cvt:StrengthToColorConverter x:Key="StrengthToColorConverter" />
    </ContentPage.Resources>

Agora, uma instância do conversor está no dicionário de recursos com a chave StrengthToColorConverter, que, por acaso, tem o mesmo nome do tipo. Essa é uma maneira típica de nomear conversores, já que você costuma ter apenas um único conversor que é reutilizado ao longo do XAML. Se, por algum motivo, você precisasse de várias instâncias de conversor, as chaves teriam que ser diferentes entre si.

Para terminar, faça referência ao conversor na vinculação. Como o conversor está em um dicionário de recursos, você usa a extensão de marcação {StaticResource} para fazer referência a ele. O conversor é atribuído à propriedade do Converter da vinculação:

<Entry BackgroundColor="{Binding PasswordStrength, Converter={StaticResource StrengthToColorConverter}}" ... />

Verifique seu conhecimento

1.

Para exibir e formatar um valor como uma cadeia de caracteres na vinculação de dados, qual seria a melhor abordagem?

2.

Qual tipo é usado na conversão de vinculação de dados?