André Alves de Lima

Talking about Software Development and more…

Trabalhando com Value Converters no WPF

[Atenção!!! Este artigo tem sua versão em vídeo! Se quiser pular a parte escrita e só assistir o vídeo, dê uma olhada no final do post!]

Olá pessoal, tudo certo?

Dando continuidade à série de artigos sobre o Data Binding do WPF, hoje escreverei um pouco sobre a funcionalidade de Value Converters.

Como vimos no artigo anterior, Converter é um dos membros do Data Binding do WPF. Value Converters são implementações das interfaces IValueConverter ou IMultiValueConverter que são utilizados no Data Binding do WPF para converter valores de um tipo em outro.

Por exemplo, imagine que você esteja linkando através de binding duas propriedades do tipo boolean. Porém, no seu cenário, você quer que a propriedade destino receba o valor inverso da propriedade origem. Para isso você pode implementar um Value Converter que vai inverter um valor booleano.

Outra situação em que podemos utilizar Value Converters é quando queremos formatar um DateTime ou double em um formato específico. Ou até mesmo se quisermos juntar duas propriedades da nossa fonte de dados em uma só (por exemplo, Nome + Sobrenome).

Como disse acima, existem duas interfaces que podem ser implementadas para criarmos Value Converters. Vejamos abaixo a diferença entre essas duas interfaces e quando utilizarmos uma ou outra:

IValueConverter: Implemente essa interface quando você quiser converter um único valor em um outro único valor;
IMultiValueConverter: Implemente essa interface quando você quiser converter vários valores em um único valor.

Com o passar do tempo, a Microsoft foi integrando ao .NET Framework algumas implementações básicas de Value Converters. Confira abaixo a lista de Value Converters disponíveis diretamente no WPF:

IValueConverter: AlternationConverter, BooleanToVisibilityConverter, JournalEntryListConverter, ZoomPercentageConverter;
IMultiValueConverter: BorderGapMaskConverter, JournalEntryUnifiedViewConverter, MenuScrollingVisibilityConverter, ProgressBarBrushConverter, ProgressBarHighlightConverter.

Finalmente, vamos aplicar esses conceitos na prática! Para isso, vamos criar um novo projeto do tipo WPF Application utilizando o Visual Studio 2010 e .NET Framework 4.

O primeiro converter que vamos implementar será utilizado para invertermos um valor booleano. Imagine que temos um CheckBox que deverá desabilitar um TextBox quando estiver checado e habilitar o mesmo TextBox quando estiver deschecado. O nosso XAML ficaria parecido com o seguinte:

Para conseguirmos esse comportamento, precisamos implementar o nosso converter. Então, no code behind da nossa Window, vamos incluir o seguinte código:

public class OppositeBooleanConverter : IValueConverter
{
    #region IValueConverter Members
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        return !(bool)value;
    }
    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        return !(bool)value;
    }
    #endregion
}

Vejam que a ideia é verificarmos o valor recebido no parâmetro value dos métodos Convert e ConvertBack, fazer o tratamento adequado e retornar o valor final.

Feito isso, podemos incluir o binding no nosso XAML. Para isso vamos incluir uma nova declaração de namespace apontando para o namespace da nossa aplicação, adicionar uma instância da classe OppositeBooleanConverter nos recursos do nosso StackPanel e criar o binding entre a propriedade IsChecked do CheckBox com a propriedade IsEnabled do nosso TextBox, informando como ValueConverter a nossa instância de OppositeBooleanConverter:

Rode a aplicação e veja que o comportamento é o que esperamos na nossa especificação.

Para o nosso segundo exemplo, vamos criar uma nova Window no nosso projeto e no seu code behind, vamos implementar uma classe simples chamada Pessoa (não esqueça de incluir um using System.ComponentModel pois estamos utilizando a interface INotifyPropertyChanged):

public class Pessoa : INotifyPropertyChanged
{
    private string nome;
    public string Nome
    {
        get { return nome; }
        set
        {
            nome = value;
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs("Nome"));
        }
    }

    private string sobrenome;
    public string Sobrenome
    {
        get { return sobrenome; }
        set
        {
            sobrenome = value;
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs("Sobrenome"));
        }
    }

    private DateTime dataNascimento;
    public DateTime DataNascimento
    {
        get { return dataNascimento; }
        set
        {
            dataNascimento = value;
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs("DataNascimento"));
        }
    }

    #region INotifyPropertyChanged Members
    public event PropertyChangedEventHandler PropertyChanged;
    #endregion
}

Já no XAML da nossa Window, vamos definir três TextBoxes linkados cada um com uma propriedade da nossa classe Pessoa, como definido abaixo:

Vamos incluir também no construtor da nossa Window a inicialização da instância da classe Pessoa:

public partial class Window3 : Window
{
    public Window3()
    {
        InitializeComponent();
        ((Pessoa)meuStackPanel.DataContext).Nome = "André";
        ((Pessoa)meuStackPanel.DataContext).Sobrenome = "Lima";
        ((Pessoa)meuStackPanel.DataContext).DataNascimento = new DateTime(1984, 12, 20);
    }
}

Feito isso, altere no App.xaml a StartupUri apontando para essa nossa outra Window e execute a aplicação.

Note que a Data de Nascimento é exibida no formato completo (data + hora). E se quisermos que seja exibido somente o dia? Podemos implementar um Value Converter para fazer a formatação. Esse Converter receberá o valor a ser formatado (parâmetro value) e a formatação desejada (parâmetro parameter):

public class FormatConverter : IValueConverter
{
    #region IValueConverter Members
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        return string.Format(parameter.ToString(), value);
    }
    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        throw new NotImplementedException();
    }
    #endregion
}

E então, podemos utilizar esse FormatConverter no nosso binding:

Notem que para passarmos parâmetros ao nosso Converter utilizamos o elemento ConverterParameter do binding. O valor passado nesse elemento será recebido no parâmetro parameter das funções Convert e ConvertBack.

Apesar de podermos suprir essa necessidade utilizando Value Converters, a partir do .NET 3.5 isso não é mais necessário. Isso porque nessa versão foi adicionado ao binding do WPF o elemento StringFormat, onde podemos passar o formato desejado para aquele valor. Portanto, no nosso caso, ao invés de utilizarmos o Value Converter, poderíamos ter feito da seguinte maneira muito mais simples:

Até agora só vimos exemplos de implementações da interface IValueConverter. Para finalizar, vamos ver uma implementação de IMultiValueConverter, que utilizaremos para mostrar em um outro TextBox as propriedades Nome e Sobrenome concatenadas.

Para isso, vamos implementar o ConcatenateStringsConverter, utilizando a interface IMultiValueConverter:

public class ConcatenateStringsConverter : IMultiValueConverter
{
    #region IMultiValueConverter Members
    public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        StringBuilder result = new StringBuilder();

        foreach (string value in values)
            result.AppendFormat("{0} ", value);

        return result.ToString();
    }
    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
    {
        throw new NotImplementedException();
    }
    #endregion
}

Notem que a diferença básica entre a interface IValueConverter e IMultiValueConverter está no primeiro parâmetro das funções Convert e ConvertBack. Enquanto a interface IValueConverter espera somente um object, a interface IMultiValueConverter espera um array de objects.

Agora no nosso XAML podemos utilizar esse nosso novo Converter:

Verifiquem que a construção do binding quando utilizamos MultiValueConverters é um pouco diferente. Primeiro especificamos a propriedade cujo binding será aplicado e dentro dela definimos um MultiBinding especificando o Converter. Então, dentro desse MultiBinding, especificamos os Paths que serão passados em cada posição do array de valores recebidos nas funções Convert e ConvertBack.

E com isso concluo este artigo sobre Value Converters no WPF. Se você quiser ver a versão em vídeo deste artigo, confira o vídeo publicado no MSDN neste link.

Até a próxima!

André Alves de Lima.

5 thoughts on “Trabalhando com Value Converters no WPF

Deixe uma resposta

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *