.NET MAUI - Conversores de Valor (Value Converter)


  Neste artigo veremos oque são os conversores de valor e como usá-los no .NET MAUI.

Os conversores de valor de associação permitem converter um valor de um tipo para outro durante a associação de dados em um aplicativo .NET MAUI.

Quando você associa um valor a uma propriedade de um elemento de interface do usuário (UI) em um aplicativo .NET MAUI, às vezes é necessário converter esse valor para um tipo diferente antes de ser exibido ou usado pelo elemento UI.

Por exemplo, você pode ter um valor numérico inteiro que precisa ser exibido como uma string formatada ou um valor booleano que precisa ser convertido em um ícone específico.

No .NET MAUI podemos usar os Conversores de Valor de Associação, para definir conversores reutilizáveis que encapsulam a lógica de conversão e tornam a associação de dados mais flexível e fácil de usar.

Isso é evidenciando pelo DataBinding que permite que as propriedades de dois objetos seja vinculadas de modo que uma alteração em uma provoca alteração na outra ficando assim sincronizadas.

Existem cenários onde os tipos de dados dos objetos são incompatíveis e neste caso precisamos converter o valor da origem para o tipo de dados do valor de destino e vice-versa e fazemos isso usando os conversores de valores.

Os Conversores de Valor de Associação são classes que implementam a interface IValueConverter e fornecem métodos para converter um valor de origem para um valor de destino e vice-versa. Esses conversores podem ser definidos em seu aplicativo e reutilizados em várias partes da interface do usuário.

Ao associar um valor a uma propriedade usando o DataBinding do .NET MAUI, você pode especificar o conversor a ser usado para realizar a conversão. Isso permite que você personalize o comportamento da associação de dados de acordo com suas necessidades.

Como usar os conversores de valores

Para criar um conversor de valor precisamos seguir as seguinte etapas:

  1. Criar uma classe que implementa a interface IValueConverter
     
    public class MeuConversor : IValueConverter
    {
       public object Convert(object value, Type targetType,
              object parameter,System.Globalization.CultureInfo culture)
       {
          // Lógica de conversão aqui
          // Retorna o valor convertido

       }

       public object ConvertBack(object value, Type targetType,
              object parameter, System.Globalization.CultureInfo culture)
       {
           // Lógica de conversão inversa aqui
           // Retorna o valor convertido de volta ao tipo original

       }
    }

     

  2. Implementar o método Convert que altera os dados a partir do formato original para o formato de destino ou de exibição
    1. O parâmetro value contém o valor de origem que está sendo convertido;
    2. O parâmetro targetType especifica o tipo de destino para o valor convertido;
    3. O parâmetro parameter pode ser usado para passar parâmetros adicionais para o conversor;
    4. O parâmetro culture indica a cultura usada para a conversão;
       
  3. Implementar o método ConvertBack() , se necessário, que reverte as alterações e altera o valor a partir do formato de destino ou de exibição para o formato original.
    Normalmente, você pode lançar uma exceção NotImplementedException se a conversão inversa não for suportada.
     
  4. Use o conversor de valor personalizado em sua interface do usuário, referenciando-o como um recurso e aplicando-o nas associações de dados conforme necessário.
    Por exemplo, em um arquivo .xaml, você pode definir o conversor como um recurso e usá-lo em elementos que requerem conversão.
     
    <ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
                
    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
                
    xmlns:converters="clr-namespace:MeuApp"
                
    x:Class="MeuApp.MinhaPagina">

    <ContentPage.Resources>
        <
    ResourceDictionary>
           <
    converters:MeuConversor x:Key="MeuConversor" />
        </
    ResourceDictionary>
    </
    ContentPage.Resources>

    <!-- ... Interface do usuário ... -->
    <Label Text="{Binding MeuValor, Converter={StaticResource MeuConversor}}" />
    ...
    </
    ContentPage>
     

    Nesse exemplo, a classe MeuConversor é definida como um recurso no dicionário de recursos da página (<ContentPage.Resources>). Em seguida, o conversor é usado na associação de dados da propriedade Text do elemento Label. A propriedade MeuValor é vinculada ao Label, e o conversor é aplicado à associação usando a sintaxe {Binding MeuValor, Converter={StaticResource MeuConversor}}

Exemplo prático

Para ilustrar o uso dos conversores de valores vamos criar uma aplicação .NET MAUI no VS 2022 chamada MauiValueConverter usando o template padrão.

Esta aplicação vai exibir uma lista contendo o nome a nota dos alunos onde nome é uma string e nota é do tipo double.

Nosso objetivo será criar um conversor de valor para exibir as notas inferiores a 7 na cor vermelha e superiores a 7 na cor azul conforme mostra a imagem abaixo:

Crie no projeto a pasta Entities e nesta pasta crie a classe Aluno :

public class Aluno
{
 
public string Nome { get; set; }
 
public double Nota { get; set; }
}

Vamos criar uma pasta Services no projeto e nesta pasta criar a interface IAlunoService e a classe concreta AlunoService que implementa esta interface. Aqui estamos criando uma lista de objetos Aluno atribuindo o nome e a nota para podermos exibir no controle ListView.

1- IAlunoService

public interface IAlunoService
{
   ObservableCollection<Aluno> GetAlunos();
}

2- AlunoService

public class AlunoService : IAlunoService
{
  
private ObservableCollection<Aluno> alunos;
  
public ObservableCollection<Aluno> GetAlunos()
   {
     alunos =
new ObservableCollection<Aluno>
     {
      
new Aluno { Nome = "Maria", Nota = 9.3 },
      
new Aluno { Nome = "Paulo", Nota = 7.8 },
      
new Aluno { Nome = "Sandra", Nota = 5.5 },
      
new Aluno { Nome = "Carlos", Nota = 5.9 },
      
new Aluno { Nome = "Silvia", Nota = 8.4 },
      
new Aluno { Nome = "Jaime", Nota = 7.5 },
      
new Aluno { Nome = "Debora", Nota = 5.3 },
      
new Aluno { Nome = "Elena", Nota = 7.4 }
     };
    
return alunos;
   }
}

Observe que usamos a classe ObservableCollection<T> para representar coleções de alunos que podem ser visualmente vinculados a uma interface do usuário. Essa classe herda da classe Collection<T> e implementa a interface INotifyCollectionChanged, que notifica automaticamente as alterações na coleção para qualquer vinculação de dados.

Ao usar uma ObservableCollection<Aluno>, você permite que a interface do usuário seja atualizada automaticamente quando a coleção de alunos for modificada. Por exemplo, se você adicionar, remover ou alterar um aluno na coleção, a interface do usuário será notificada e poderá atualizar a exibição dos dados correspondentes.

Se você usar uma classe List<Aluno> ou Collection<Aluno>, não terá o benefício da notificação automática de alterações. Isso significa que, se a coleção de alunos for modificada, você precisará atualizar manualmente a interface do usuário para refletir essas alterações.

Vamos criar a Content Page AlunosView.xaml em uma pasta Views do projeto :

<?xml version="1.0" encoding="utf-8" ?>
<
ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
            
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
            
x:Class="MauiValueConverter.Views.AlunosView"
             Title="Notas dos Alunos">

<ListView x:Name="lvAlunos" HasUnevenRows="True">
  <
ListView.ItemTemplate>
    <
DataTemplate>
        <
ViewCell>
           <
StackLayout Orientation="Horizontal" Padding="10">
             <
Label Text="{Binding Nome}"
                   
TextColor="Black"
                   
FontSize="30"
                   
HorizontalOptions="StartAndExpand"
              />
              <
Label Text="{Binding Nota}"
                    
TextColor="Black"
                    
FontSize="30"
                    
FontAttributes="Bold"
                    
HorizontalOptions="EndAndExpand"
               />
            </
StackLayout>
          </
ViewCell>
       </
DataTemplate>
   </
ListView.ItemTemplate>
</
ListView>
</
ContentPage>

A view AlunosView definida acima vai exibir os nomes e as notas usando a cor Black para todas as notas.

Vamos  usar os recursos do pacote Microsoft.Extensions.DependencyInjection, que é uma biblioteca de DI padrão no ecossistema .NET para aplicar a injeção de dependência.

Para usar a injeção de dependência no .NET MAUI precisamos configurar e registrar os serviços usando a interface IServiceCollection no ponto de inicialização da sua aplicação. Em seguida, poderemos obter esses serviços injetados nas classes usando a injeção de dependência.

Para isso inclua o código abaixo no arquivo App.xaml.cs :

public partial class App : Application
{
  
public App()
   {
      InitializeComponent();
      var services = new ServiceCollection();
      services.AddTransient<IAlunoService, AlunoService>();
     
// Construir o provedor de serviços
     
var serviceProvider = services.BuildServiceProvider();
     
// Resolver a instância de IAlunoService
     
var alunoService = serviceProvider.GetService<IAlunoService>();
     
// Definie a página principal e injetar o serviço
      MainPage = new NavigationPage(new AlunosView(alunoService));
   }
}

Agora no arquivo AlunosView.xaml.cs vamos definir o código abaixo:

public partial class AlunosView : ContentPage
{
  
private readonly IAlunoService _alunoService;
  
public AlunosView(IAlunoService alunoService)
   {
     InitializeComponent();
     _alunoService = alunoService;
     lvAlunos.ItemsSource = _alunoService.GetAlunos();
   }
}

Neste código , IAlunoService é a interface que define as operações do serviço e AlunoService é a implementação concreta. A classe App configura a injeção de dependência ao registrar AlunoService como a implementação para a interface IAlunoService. Em seguida, ela resolve uma instância de IAlunoService do provedor de serviços e a passa para a classe AlunosView por meio do construtor.

Convertendo de double para Color

Vamos agora criar a classe ConverteDoubleToColor na pasta Services que vai implementar a interface IValueConverter e que vai converter os valores das notas de double para uma Cor definida na implementação do método Convert conforme mostrado a seguir:

public class ConverteDoubleToColor : IValueConverter
{
   Color color =
new Color();
  
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
   {
     
double nota = (double)value;
     
return nota < 6.9 ? Color.FromRgb(255, 0, 0) : Color.FromRgba(0, 38, 255, 255);
   }
  
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
   {
    
throw new NotImplementedException();
   }
}

Agora precisamos aplicar o conversor criado à nossa view AlunosView alterando o código do arquivo .xaml conforme abaixo:

<?xml version="1.0" encoding="utf-8" ?>
<
ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
            
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
            
x:Class="MauiValueConverter.Views.AlunosView"
            
xmlns:converters="clr-namespace:MauiValueConverter.Services"
            
Title="Notas dos Alunos">

   <ContentPage.Resources>
     <
converters:ConverteDoubleToColor x:Key="colorConverter" />
   </
ContentPage.Resources>

   <
ListView x:Name="lvAlunos" HasUnevenRows="True">
      <
ListView.ItemTemplate>
         <
DataTemplate>
           <
ViewCell>
             <
StackLayout Orientation="Horizontal" Padding="10">
                <
Label Text="{Binding Nome}"
                      
TextColor="Black"
                      
FontSize="30"
                      
HorizontalOptions="StartAndExpand"/>

                <Label Text="{Binding Nota}"
                      
TextColor="{Binding Nota, Converter={StaticResource colorConverter}}"
                      
FontSize="30"
                      
FontAttributes="Bold"
                      
HorizontalOptions="EndAndExpand"/>
              </
StackLayout>
             </
ViewCell>
           </
DataTemplate>
         </
ListView.ItemTemplate>
    </
ListView>

</ContentPage>

Os destaques deste código são :

1-  xmlns:converters="clr-namespace:MauiValueConverter.Services"

Neste trecho, está sendo definido um namespace converters e está sendo associado ao namespace MauiValueConverter.Services no código-behind. Essa declaração permite que você use o namespace converters para referenciar as classes de conversor de valor definidas no namespace MauiValueConverter.Services dentro do arquivo XAML.

2- <ContentPage.Resources>
     <
converters:ConverteDoubleToColor x:Key="colorConverter" />
   </
ContentPage.Resources>

Nesse trecho, está sendo definido um recurso chamado colorConverter dentro da seção <ContentPage.Resources>. Esse recurso é do tipo ConverteDoubleToColor, uma classe de conversor de valor definida no namespace MauiValueConverter.Services. Ele é registrado com a chave x:Key="colorConverter", permitindo que seja referenciado posteriormente usando essa chave.

3-  <Label Text="{Binding Nota}"
               
TextColor="{Binding Nota, Converter={StaticResource colorConverter}}"
               
FontSize="30"
               
FontAttributes="Bold"
               
HorizontalOptions="EndAndExpand"/>

Neste código, definimos uma Label onde o atributo Text  está vinculado à propriedade Nota do objeto de dados atual (Binding). O atributo TextColor está usando uma conversão de valor (value converter) através do StaticResource chamado colorConverter, que foi definido anteriormente como um recurso. A propriedade TextColor será definida usando o resultado da conversão de valor realizada pelo colorConverter.

Executando o projeto iremos obter o resultado abaixo:

Mostramos assim um exemplo prático de utilização dos conversores de valores no .NET MAUI.

Pegue o projeto aqui:   MauiValueConverter.zip (sem as referências)

"A lei e os profetas duraram até João; desde então é anunciado o reino de Deus, e todo o homem emprega força para entrar nele.
E é mais fácil passar o céu e a terra do que cair um til da lei."
Lucas 16:16-17

Referências:


José Carlos Macoratti