This article has been localized into Italian by the community.
Rispondere ai cambiamenti
Fino a questo momento, nel tutorial, abbiamo associato i bindings tra elementi dell'interfaccia grafica e classi esistenti, ma nelle applicazioni reali quello che farai sarà collegare i bindings ai tuoi oggetti. Scoprirai che sarà altrettanto facile farlo ma con qualche delusione: I cambiamenti non sono riflessi in modo automatico così come avveniva nell'esempio precedente. Di seguito nell'articolo vedrai che saranno necessari alcune modifiche aggiuntive per far si che ciò avvenga, fortunatamente WPF rende tutto abbastanza facile.
Risposta alle modifiche del data source
Ci sono 2 differenti scenari che puoi voler gestire quando si tratta di modifiche al data source: Modifiche ad una lista di items e modifiche alle proprietà collegate ad ognuno dei data object. Come gestirli può variare a seconda di cosa stai facendo o cosa stai cercando di realizzare, ma WPF ti aiuta con l'utilizzo di due interfacce: ObservableCollection e INotifyPropertyChanged
Il seguente esempio ti mostrerà perchè abbiamo bisogno di queste 2 cose:
<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; }
}
}
Prova ad eseguire il codice e vedi come anche se aggiungi qualcosa alla lista o cambi il nome di uno degli utenti, niente nella UI viene aggiornato. L'esempio e molto semplice, con una User class che prenderà il nome dell'utente, una ListBox che li mostra ed alcuni bottoni per manipolare la lista ed i suoi contenuti. L'ItemSource della ListBox è assegnato ad una lista di un paio di utenti che creiamo nel costruttore dell'oggetto Window. Il problema è che nessuno dei bottoni sembra lavorare. Vediamo come risolvere il problema in due semplici passi.
Riflettere i cambiamenti nel data source della lista
Il primo passo è ottenere che la UI risponda ai cambiamenti nella list source (ItemsSource), come quando aggiungiano o eliminiamo un utente. Ciò di cui abbiamo bisogno è una lista che notifichi le modifiche al suo contenuto e, fortunatamente, WPF ha un tipo di lista che farà questo. Il suo nome è ObservableCollection, e lo usi semplicemente come List<T>, con qualche differenza.
Nell'esempio finale, che trovi di seguito, abbiamo semplicemente sostituito List<User> con ObservableCollection<User> - tutto qui! Questo farà funzionare i bottoni Add e Delete, ma non il bottone "Change name" perchè il cambiamento avverrà sul data object collegato ma non nel source list - il secondo passo gestirà questo aspetto.
Riflettere i cambiamenti nei data objects
Il secondo passo consiste nell'implementare l'interfaccia INotifyPropertyChanged nella nostra User class. Facendo ciò, i nostri oggetti User saranno in grado di avvisare il livello UI dei cambiamenti alle sue proprietà. Questo è un pò più impegnativo rispetto alla modifica del tipo della List, ma è il modo più semplice per realizzare gli aggiornamenti automatici.
L'esempio finale e funzionante
Con le due modifiche descritte sopra, abbiamo ora un esempio che RIFLETTERA' i cambiamenti avvenuti nel datasource. E' qualcosa simile a questo:
<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));
}
}
}
Sommario
Come hai potuto vedere, implementare INotifyPropertyChanged è molto facile, ma occorre creare un pò di codice extra nelle nostre classi, e aggiunge un pò di logica in più alle tue proprietà. Questo è il prezzo che dovrai pagare se vuoi fare il bind alle tue classi ed avere le modifiche riflesse immediatamente nella UI. Ovviamente devi chiamare NotifyPropertyChanged solo per le proprietà di cui devi fare il bind - il resto può restare come è.
ObservableCollection dall'altro lato è semplicissimo da utilizzare - richiede semplicemente di usare uno specifico tipo di lista in quelle situazioni in cui vuoi che i cambiamenti alla lista sorgente si riflettano nella destinazione collegata.