This article has been localized into Portuguese by the community.
Respondendo a mudanças
Até agora, neste tutorial, criamos principalmente ligações entre elementos da interface do usuário e classes existentes, mas, em aplicações da vida real, você obviamente estará vinculado a seus próprios objetos de dados. Isso é igualmente fácil, mas, depois de começar, você pode descobrir algo que o desaponta: as alterações não são refletidas automaticamente, como nos exemplos anteriores. Como você aprenderá neste artigo, você precisa apenas de um pouco de trabalho extra para que isso aconteça, mas, felizmente, o WPF torna isso muito fácil.
Respondendo a mudanças na fonte de dados
Existem dois cenários diferentes em que você pode ou não querer manipular ao lidar com alterações de fonte de dados: Alterações na lista de itens e alterações nas propriedades associadas em cada um dos objetos de dados. O modo de lidar com eles pode variar, dependendo do que você está fazendo e do que está procurando realizar, mas o WPF vem com duas soluções muito fáceis que você pode usar: o ObservableCollection e a interface INotifyPropertyChanged.
O exemplo a seguir mostrará por que precisamos dessas duas coisas:
<Window x:Class="WpfTutorialSamples.DataBinding.ChangeNotificationSample"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="ChangeNotificationSample" Height="150" Width="300">
<DockPanel Margin="10">
<StackPanel DockPanel.Dock="Right" Margin="10,0,0,0">
<Button Name="btnAddUser" Click="btnAddUser_Click">Add user</Button>
<Button Name="btnChangeUser" Click="btnChangeUser_Click" Margin="0,5">Change user</Button>
<Button Name="btnDeleteUser" Click="btnDeleteUser_Click">Delete user</Button>
</StackPanel>
<ListBox Name="lbUsers" DisplayMemberPath="Name"></ListBox>
</DockPanel>
</Window>
using System;
using System.Collections.Generic;
using System.Windows;
namespace WpfTutorialSamples.DataBinding
{
public partial class ChangeNotificationSample : Window
{
private List<User> users = new List<User>();
public ChangeNotificationSample()
{
InitializeComponent();
users.Add(new User() { Name = "John Doe" });
users.Add(new User() { Name = "Jane Doe" });
lbUsers.ItemsSource = users;
}
private void btnAddUser_Click(object sender, RoutedEventArgs e)
{
users.Add(new User() { Name = "New user" });
}
private void btnChangeUser_Click(object sender, RoutedEventArgs e)
{
if(lbUsers.SelectedItem != null)
(lbUsers.SelectedItem as User).Name = "Random Name";
}
private void btnDeleteUser_Click(object sender, RoutedEventArgs e)
{
if(lbUsers.SelectedItem != null)
users.Remove(lbUsers.SelectedItem as User);
}
}
public class User
{
public string Name { get; set; }
}
}
Tente executá-lo por conta própria e observe como, mesmo que você adicione algo à lista ou altere o nome de um dos usuários, nada na interface do usuário será atualizado . O exemplo é bastante simples, com uma classe User que manterá o nome do usuário, um ListBox para mostrá-los e alguns botões para manipular a lista e seu conteúdo. O ItemsSource da lista é atribuído a uma lista rápida de alguns usuários que criamos no construtor da janela. O problema é que nenhum dos botões parece funcionar. Vamos consertar isso em dois passos fáceis.
Refletindo alterações na fonte de dados da lista
O primeiro passo é fazer com que a interface do usuário responda às alterações na origem da lista (ItemsSource), como quando adicionamos ou excluímos um usuário. O que precisamos é de uma lista que notifique quaisquer destinos de alterações em seu conteúdo e, felizmente, o WPF fornece um tipo de lista que fará exatamente isso. Chama-se ObservableCollection e você o usa de maneira muito parecida com uma List normal, com poucas diferenças.
No último exemplo, que você encontrará abaixo, nós simplesmente substituímos a List<User> com um ObservableCollection<User> - é tudo que é preciso! Isso fará com que o botão Adicionar e Excluir funcione, mas não fará nada pelo botão "Alterar nome", porque a alteração ocorrerá no próprio objeto de dados vinculados e não na lista de fontes - a segunda etapa tratará desse cenário .
Refletindo mudanças nos objetos de dados
A segunda etapa é permitir que nossa classe de usuário personalizada implemente a interface INotifyPropertyChanged. Ao fazer isso, nossos objetos User são capazes de alertar a camada da interface do usuário de alterações em suas propriedades. Isso é um pouco mais complicado do que apenas mudar o tipo de lista, como fizemos acima, mas ainda é uma das maneiras mais simples de realizar essas atualizações automáticas.
O exemplo final e funcional
Com as duas alterações descritas acima, agora temos um exemplo que refletirá as alterações na fonte de dados. Se parece com isso:
<Window x:Class="WpfTutorialSamples.DataBinding.ChangeNotificationSample"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="ChangeNotificationSample" Height="135" Width="300">
<DockPanel Margin="10">
<StackPanel DockPanel.Dock="Right" Margin="10,0,0,0">
<Button Name="btnAddUser" Click="btnAddUser_Click">Add user</Button>
<Button Name="btnChangeUser" Click="btnChangeUser_Click" Margin="0,5">Change user</Button>
<Button Name="btnDeleteUser" Click="btnDeleteUser_Click">Delete user</Button>
</StackPanel>
<ListBox Name="lbUsers" DisplayMemberPath="Name"></ListBox>
</DockPanel>
</Window>
using System;
using System.Collections.Generic;
using System.Windows;
using System.ComponentModel;
using System.Collections.ObjectModel;
namespace WpfTutorialSamples.DataBinding
{
public partial class ChangeNotificationSample : Window
{
private ObservableCollection<User> users = new ObservableCollection<User>();
public ChangeNotificationSample()
{
InitializeComponent();
users.Add(new User() { Name = "John Doe" });
users.Add(new User() { Name = "Jane Doe" });
lbUsers.ItemsSource = users;
}
private void btnAddUser_Click(object sender, RoutedEventArgs e)
{
users.Add(new User() { Name = "New user" });
}
private void btnChangeUser_Click(object sender, RoutedEventArgs e)
{
if(lbUsers.SelectedItem != null)
(lbUsers.SelectedItem as User).Name = "Random Name";
}
private void btnDeleteUser_Click(object sender, RoutedEventArgs e)
{
if(lbUsers.SelectedItem != null)
users.Remove(lbUsers.SelectedItem as User);
}
}
public class User : INotifyPropertyChanged
{
private string name;
public string Name {
get { return this.name; }
set
{
if(this.name != value)
{
this.name = value;
this.NotifyPropertyChanged("Name");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged(string propName)
{
if(this.PropertyChanged != null)
this.PropertyChanged(this, new PropertyChangedEventArgs(propName));
}
}
}
Resumo
Como você pode ver, implementar o INotifyPropertyChanged é bem fácil, mas cria um pouco de código extra nas suas classes e adiciona um pouco de lógica extra às suas propriedades. Este é o preço que você terá que pagar se quiser vincular suas próprias classes e ter as alterações refletidas na interface do usuário imediatamente. Obviamente, você só precisa chamar NotifyPropertyChanged nas propriedades de vinculação do setter - o restante pode permanecer como está.
O ObservableCollection, por outro lado, é muito fácil de lidar - ele simplesmente requer que você use esse tipo específico de lista nas situações em que você deseja que as mudanças na lista de fontes sejam refletidas em um destino obrigatório.