Introdução a DataBinding em WPF

Veja neste artigo como funciona e como implementar ligações de dados (DataBinding) em WPF (Windows Presentation Foundation), atualizando automaticamente dados entre elementos da interface do usuário e o modelo de negócio.

WPF fornece uma maneira simples e poderosa para atualização automática de dados entre o modelo de negócio e interface do usuário. Esse mecanismo é chamado de ligação de dados. Sempre quando há alteração em seus modelos de negócios ele reflete automaticamente as atualizações de interface do usuário e vice-versa. Este é o método preferido na WPF para colocar os dados para a interface do utilizador.

Ver também: Introdução ao Data Binding no WPF - Revista Easy Net 16

Ligação de dados pode ser unidirecional (fonte -> destino ou destino <- fonte) ou bidirecional (fonte <-> alvo).

A fonte de uma ligação de dados pode ser uma propriedade. NET normal ou um DependencyProperty. A propriedade de destino da ligação deve ser um DependencyProperty, como mostra a Figura 1.

Figura 1. Esquema de funcionamento do DataBinding

Para fazer corretamente databinding, os dois lados de uma ligação devem ter uma forma de notificação de alteração que notifica a ligação quando é para atualizar o valor de destino. Nas propriedades normais do .NET isto é feito usando evento PropertyChanged da interface INotifyPropertyChanged. Em DependencyProperties é feito pelo retorno da chamada PropertyChanged do metadados da propriedade.

Ligação de dados é normalmente feita em XAML usando a extensão de marcação . O exemplo da Listagem 1 mostra uma ligação simples entre o texto de uma TextBox e um Label, que reflete o valor digitado.

<stackpanel> <textbox x:name="txtSeuTxt" /> <label content="{binding text, elementname= txtseutxt, updatesourcetrigger=propertychanged}" /> </stackpanel>
Listagem 1. Ligação entre TextBox e Label

DataContext

Cada controle WPF que é herdado de FrameworkElement tem uma propriedade DataContext. Esta propriedade é destinada a ser usada para carregar o conteúdo do seu objeto.

A propriedade DataContext pode ser visualizada por elementos filhos do objeto pai. Assim, você pode definir o DataContext em um contêiner de layout superior e seu valor é herdado a todos os elementos filhos. Isto é muito útil se você quiser construir um formulário que está vinculado a mesma BindingModel, como mostra a Listagem 2.

<stackpanel datacontext="{staticresource listapessoa}"> <textbox text="{binding primeironome}"/> <textbox text="{binding sobrenome}"/> <textbox text="{binding endereco}"/> <textbox text="{binding numero}"/> <textbox text="{binding bairro}"/> <textbox text="{binding cidade}"/> <textbox text="{binding estado}"/> </stackpanel>
Listagem 2. Propriedade DataContext do contêiner herdada pelos elementos filhos

ValueConverters

Se você deseja ligar duas propriedades de diferentes tipos juntos, você precisa usar um ValueConverter. O ValueConverter converte o valor de um tipo de fonte para um tipo de destino e vice-versa. WPF já inclui alguns conversores de valor, mas na maioria dos casos você terá que escrever o seu próprio conversor de tipo, implementando a interface IValueConverter.

Um exemplo típico é o de vincular um membro booleano (Boolean) para a propriedade de visibilidade (Visibility). Uma vez que a visibilidade é um valor de enumeração que pode ser Visible, Collapsed ou Hidden, para isso você terá que usar um conversor de valor, como na Listagem 3.

<stackpanel> <stackpanel.resources> <booleantovisibilityconverter x:key="booltovis" /> </stackpanel.resources> <checkbox x:name="chkshowdetails" content="show details" /> <stackpanel x:name="detailspanel" visibility="{binding ischecked, elementname=chkshowdetails, converter={staticresource booltovis}}"> </stackpanel> </stackpanel>
Listagem 3. Usando conversor de valor booleano

O exemplo da Listagem 4 mostra um conversor que converte um simples boleano (Boolean) a uma propriedade de visibilidade (Visibility). Note-se que tal um conversor já faz parte do quadro. NET.

public class BooleanToVisibilityConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { if (value is Boolean) { return ((bool)value) ? Visibility.Visible : Visibility.Collapsed; }   return value; }   public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { throw new NotImplementedException(); } }
Listagem 4. Conversor de Boolean para Visibility
Nota: Você pode derivar o seu conversor de valor de MarkupExtension e retornar a sua própria instância na substituição ProviderValue. Então você pode usá-lo diretamente, sem referência a ele a partir dos recursos.
Nota: Quando você receber o erro “Não existem construtores sem parâmetros”, você precisa adicionar um construtor sem parâmetros, ele é necessário apenas para uso do designer WPF que por não ter esse construtor pode gerar erro em tempo de execução.

Uma forma muito comum e popular para sincronizar dados entre o modelo de dados e a interface do usuário no WPF é usar DataBinding. O valor do modelo é transferido para interface do usuário, uma vez, quando a ligação é feita. Mas para cada alteração posterior, o modelo deve notificar a propriedade que está bindeada para transferir o valor novamente. Isto é feito através da implementação da interface INotifyPropertyChanged no BindingModel (Classe que representa o modelo de negócio).

No setter de cada propriedade acoplada deve haver algum código para notificar o evento PropertyChanged, passando o nome da propriedade modificada como uma string. Isso tem algumas desvantagens:

Propriedades de Notificação

Uma forma básica de implementar Notificação pode ser vista na Listagem 5.

private string _nome;   public string Nome { get { return _nome; } set { _nome = value; PropertyChanged("Nome"); } }   private void PropertyChanged(string prop) { if( PropertyChanged != null ) { PropertyChanged(this, new PropertyChangedEventArgs(prop); } }
Listagem 5. Implementando notificação de forma básica

Já na Listagem 6 temos uma forma mais elegante de implementar Notificação. A implementação básica requer duas linhas de código no setter e o nome da propriedade é passada como uma string, o que não é muito eficaz. Uma solução mais elegante é usar o poder de árvores de expressão para obter o nome da propriedade de uma expressão lambda.

private string _name; public string Name { get { return _name; } set { PropertyChanged.ChangeAndNotify(ref _name, value, () => Name); } } public static bool ChangeAndNotify<T>(this PropertyChangedEventHandler handler, ref T field, T value, Expression<Func<T>> memberExpression) { if (memberExpression == null) { throw new ArgumentNullException("memberExpression"); } var body = memberExpression.Body as MemberExpression; if (body == null) { throw new ArgumentException("Lambda must return a property."); } if (EqualityComparer<T>.Default.Equals(field, value)) { return false; }   var vmExpression = body.Expression as ConstantExpression; if (vmExpression != null) { LambdaExpression lambda = Expression.Lambda(vmExpression); Delegate vmFunc = lambda.Compile(); object sender = vmFunc.DynamicInvoke();   if( handler != null) { handler(sender, new PropertyChangedEventArgs(body.Member.Name)); } }   field = value; return true; }
Listagem 6. Implementando notificação com árvores de expressão

Claro que poderemos obter o mesmo resultado usando maneiras mais simples porém temos que tomar muito mais cuidado ao modificar propriedades (ex: mudar o nome de _name para _nome).

Concluímos aqui este artigo, onde vimos os recursos do WPF para ligação de dados entre elementos da interface e a lógica de negócios.

Até a próxima.

Ebook exclusivo
Dê um upgrade no início da sua jornada. Crie sua conta grátis e baixe o e-book

Artigos relacionados