Nella prima parte ho fatto un po' un'introduzione di
come implementare una classe che implementi l'interfaccia
INotifyPropertyChanged, allo scopo di notificare all'engine di binding del FX
che deve aggiornare i controls corrispondenti. Questo nel caso in cui la classe
è bindata ad un controllo come la TextBox, per esempio, in grado di
mostrare un solo elemento alla volta. In questo secondo ed ultimo post vedremo
invece quello che bisogna fare se la nostra classe espone una property adatta ad
essere bindata ad un controllo lista, come ListBox, ComboBox, etc
.etc.
La classe HockeyPlayer espone una property
Faults:
private FaultsCollection faults;
public FaultsCollection Faults
{ get { return faults; } }
La classe FaultsCollection è una classe che eredita da
Collection<DateTime>.
using System;
using System.ComponentModel;
using System.Collections.ObjectModel;
namespace DataBindingSample
{
public class FaultsCollection : Collection<DateTime>
{ }
}
Se noi bindassimo la proprietà Faults con una
semplice ListBox, questo controllo verrebbe automaticamente
popolato con tutti gli elementi della Collection, nel caso specifico gli oggetti
DateTime. Il data-binding fallisce se noi, via codice, aggiungiamo un nuovo
elemento alla collection: in questo caso, la ListBox bindata perde la
sincronizzazione con il suo datasource. Per ovviare a questo inconveniente,
dobbiamo implementare l'interfaccia IBindingList all'interno della
nostra classe. Questa interfaccia prevede l'implementazione di un certo numero
di proprietà e di metodi, per i quali vi rimando alla pagina sul sito MSDN. Quello che mi interessa farvi
vedere in questo post è la gestione dell'evento ListChanged
che, in modo analogo a quanto fa il PropertyChanged, notifica
al FX che c'è stato un cambiamento all'interno della mia collection (aggiunta o
rimozione di nuovi elementi DateTime).
using System;
using System.ComponentModel;
using System.Collections.ObjectModel;
namespace DataBindingSample
{
public class FaultsCollection : Collection<DateTime>, IBindingList
{
// Evento pubblico ListChanged
public event ListChangedEventHandler ListChanged;
// Override di InsertItem e RemoveItem
protected override void InsertItem(int index, DateTime item)
{
base.InsertItem(index, item);
NotifyListChanged(ListChangedType.ItemAdded, "Faults");
}
protected override void RemoveItem(int index)
{
base.RemoveItem(index);
NotifyListChanged(ListChangedType.ItemDeleted, "Faults");
}
private void NotifyListChanged(ListChangedType what, String info)
{
if (ListChanged != null)
ListChanged(this, new ListChangedEventArgs(what, 0));
}
/*
Altri membri per aderire alla IBindingList
*/
}
}
Nell'override di InsertItem e
RemoveItem sollevo l'evento ListChanged che,
ancora una volta, viene gestito dal FX per refreshare il controllo
bindato. Adesso, tornando al post precedente, posso affermare con
sicurezza che, cliccando sul button1, tutti i controlli che
hanno come datasource il mio player vengono sincronizzati con
il contenuto attuale. La cosa interessante è che questo meccanismo non è stato
implementato nella UI (ad esempio, facendo il Clear sulla
property DataBindings di ogni controllo e ricreandole, cosa
orribile), ma nella logica dell'oggetto stesso.