Desenvolvimento - ASP. NET

Value Converters: Realizando binding entre tipos incompatíveis

Veja neste artigo como realizar o binging entre tipos incompatíveis em aplicações que utilizam XAML.

por Joel Rodrigues



Em aplicações WPF, Windows Phone, Windows Store ou Silverlight, o Binding é um dos recursos mais utilizados quando lidamos com exibição e entrada de dados. Com esse recurso conseguimos ligar as propriedades de um componente visual, como um TextBox, às propriedades de um objeto que existe apenas no código, ou seja, uma instância de uma classe, bem como também conseguimos ligar as propriedades de dois componentes visuais.

Essa funcionalidade é, ou deveria ser, conhecida por todo desenvolvedor que utilize XAML na estruturação do layout de suas aplicações. O objetivo deste artigo não é o de apresentar em detalhes o recurso de Binding, mas sim tratar algumas situações especiais, onde precisamos interligar propriedades de tipos incompatíveis.

Para entendermos melhor em que situações o tema deste artigo é útil, vamos começar com um exemplo bastante simples, onde iremos exibir em um ListBox dados referente a Contas a Pagar, simulando o que seria uma parte de um sistema de controle financeiro, por exemplo. Nesse ListBox (Listagem 1), iremos exibir diretamente três propriedades simples da classe ContaPagar, apresentando cada uma em um TextBlock.

Listagem 1: Exibição de dados simples em ListBox

<ListBox Name="listContas">
    <ListBox.ItemTemplate>
        <DataTemplate>
            <StackPanel Orientation="Horizontal">
                <TextBlock Text="{Binding Identificacao}" Width="200"/>
                <TextBlock Text="{Binding Valor, StringFormat={}{0:C}}" Width="100"/>
                <TextBlock Text="{Binding Vencimento, StringFormat={}{0:d}}" Width="100"/>
            </StackPanel>
        </DataTemplate>
    </ListBox.ItemTemplate>
</ListBox>

No código C# da janela podemos criar uma lista com alguns itens e exibi-los no ListBox (Listagem 2), o que irá resultar no que vemos na Figura 1.

Listagem 2: Listando dados no ListBox

var contas = new ContaPagar[]{
    new ContaPagar(){
        Identificacao = "Nota Fiscal de Fornecedor",
        Valor = 1500,
        Vencimento = DateTime.Today.AddDays(-1)},
    new ContaPagar(){
        Identificacao = "Impostos Municipais",
        Valor = 400,
        Vencimento = DateTime.Today},
    new ContaPagar(){
        Identificacao = "Energia Elétrica",
        Valor = 120,
        Vencimento = DateTime.Today.AddDays(1)}
};
listContas.ItemsSource = contas;
Exibição de dados simples

Figura 1: Exibição de dados simples

Como estamos listando apenas os valores de cada propriedade diretamente em um TextBlock, não há dificuldade ou segredo para ligar a propriedade Text às propriedades da classe. Porém, imagine que precisamos agora alterar a forma como os dados são exibidos no ListBox, alterando a cor do texto do campo de data de acordo com sua situação. Se a conta já estiver vencida, ou seja, se a data de vencimento for inferior à data atual, o campo Vencimento deve aparecer na cor vermelha. Se a conta estiver vencendo na data atual, o texto deve aparecer amarelo e se o vencimento ainda for em uma data futura, o texto deve aparecer azul.

Nós não dispomos de uma propriedade Cor, por exemplo, na classe ContaPagar, e nem faz sentido adicioná-la, pois ela não diz respeito a uma característica real do objeto de domínio Conta a Pagar. Precisamos alterar a propriedade ForeGround dos TextBoxes, que é do tipo Brush, a partir da propriedade Vencimento da classe ContaPagar, que é do tipo DateTime. Observe que, como foi dito no início do artigo, temos a necessidade aqui de interligar propriedades de tipos diferentes, então não podemos simplesmente liga-las diretamente no Binding. Para esse tipo de situação, dispomos de um recurso chamado Value Converter, que atua como um agente intermediário entre o dado a ser exibido e a propriedade do componente.

Value Converters

Os value converters são classes que implementam a interface IValueConverter, contida no namespace System.Windows.Data. Essa interface expões dois métodos que são descritos a seguir:

  • Convert: esse método recebe como um de seus parâmetros o valor da propriedade que está sendo utilizada no Binding (em nosso exemplo, a propriedade Vencimento) e retorna um valor referente à conversão desta propriedade para o tipo desejado (Brush, no nosso caso).
  • ConvertBack: funciona de forma inversa ao Convert, recebendo a propriedade convertida e devolvendo o valor original.

Um exemplo nativo de converter que está disponível no namespace System.Windows.Controls é o BooleanToVisibiltyConverter, que converte valores de Boolean para Visibility e também o inverso. Este converter é muito útil quando precisamos exibir ou ocultar algum controle com base em uma propriedade booleana do objeto de dados.

Vamos então criar nosso converter para atender à nossa necessidade. Adicionaremos uma classe chamada DateTimeToBrushConverter e implementaremos apenas seu método Convert, pois em nosso exemplo a conversão inversa não será feita. O código desta classe pode ser visto na Listagem 3.

Nota: Apesar de não ser obrigatório, costuma-se usar o seguinte padrão de nomenclatura para os value converters: <Tipo original>To<Tipo destino>Converter. Por exemplo, BooleanToVisibility ou DateTimeToBrush.

Listagem 3: Value Converter customizado

public class DateTimeToBrushConverter:IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        DateTime data = DateTime.Parse(value.ToString());
        if (data < DateTime.Today)
            return Brushes.Red;
        else if (data == DateTime.Today)
            return Brushes.Yellow;
        else return Brushes.Blue;
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

Observe que dependendo da data recebida no parâmetro value, que neste caso será a propriedade Vencimento, retornamos um Brush diferente.

Para utilizar o converter, basta referenciar seu namespace no XAML da janela onde encontra-se o componente que receberá os dados (Listagem 4), declarar um resource nessa janela do tipo do converter criado (Listagem 5) e em seguida adicionar a propriedade Converter no Binding (Listagem 6).

Listagem 4: Importando na janela o namespace do converter

xmlns:local="clr-namespace:WpfApplication1"

Listagem 5: Declarando o converter na janela

<Window.Resources>
    <local:DateTimeToBrushConverter x:Key="dateBrushConverter"/>
</Window.Resources>

O código da Listagem 5 deve ficar imediatamente dentro da tag Window.

Listagem 6: Utilizando o converter no Binding

<TextBlock Text="{Binding Vencimento, StringFormat={}{0:d}}" Width="100" Foreground="{Binding Vencimento, Converter={StaticResource dateBrushConverter}}"/>

Se executarmos nossa aplicação agora, já teremos o resultado esperado, como podemos ver na Figura 2.

Propriedade exibida com converter

Figura 2: Propriedade exibida com converter

Neste exemplo não foi preciso implementar o método ConvertBack, pois em nenhum momento o usuário vai alterar diretamente a cor do TextBlock para que isso surta efeito sobre o vencimento da conta. Mas caso houvesse necessidade de realizar o Binding bidirecional, o procedimento da conversão inversa segue o mesmo raciocínio do Convert, apenas fazendo o caminho contrário.

Com os value converters é possível adequar tipos incompatíveis e obter o resultado desejado de uma forma simples e elegante. Imagine como seria fazer essa formatação manualmente, iterando sobre os itens do ListBox sempre que eles mudassem, verificando a data e então alterando sua cor. Com este exemplo já fica claro o ganho de produtividade que temos ao usar os converters e o quão mais limpo e organizado fica nosso código, pois o mesmo converter pode ser reutilizado sempre que necessário.

Joel Rodrigues

Joel Rodrigues - Técnico em Informática - IFRN Cursando Bacharelado em Ciências e Tecnologia - UFRN Programador .NET/C# e Delphi há quase 3 anos, já tendo trabalhado com Webservices, WPF, Windows Phone 7 e ASP.NET, possui ainda conhecimentos em HTML, CSS e Javascript (JQuery).