DATABINDING CON CUSTOM COLLECTION
Con l’introduzione dei Generics nel Framework 2.0 si è acceso il dibattito tra il confronto tra DataSet e Custom Collection.
Tra i vantaggi del Dataset si possono registrare:
- Semplicità d’utilizzo
- Gestione dello stato dei dati (cancellazioni,modifiche,aggiunte)
- Facilità di filtro e ordinamento (tramite l’oggetto DataView)
- Dataset tipizzati per un accesso Type-Safe
- DataBinding con i controlli
Di contro una struttura complessa come il Dataset e la loro struttura “generica”, richiede un notevole dispendio di risorse e un dazio in termini di performance, soprattutto con grosse quantità di dati.
L’introduzione dei Generics nella nuova versione del framework migliora la possibilità di costruire custom collection senza dover necessariamente passare per il tipo object come avveniva nelle versioni precedenti. Per approfondire che cosa sono i generics e il loro utilizzo vi rimando a questo articolo: http://msdn2.microsoft.com/it-it/library/512aeb7t(VS.80).aspx
C’è da tenere presente che l’integrazione tra il Dataset e il Visual Studio è di notevole efficacia e permette di gestire design time anche le strutture più complesse con la presenza di DataRelation e campi calcolati, con un notevole risparmio di tempo.
In questo articolo mostreremo come costruire una semplice custom collection e come implementare il databinding con i controlli del framework e permettere di lavorare con la nostra collezione anche a design time con la medesima semplicità del Dataset.
INotifyPropertyChanged
Iniziamo col costruire la nostra classe Book, che rappresenta l'entità base della nostra collection.
public class Book
{
public Book(){}
private int idBook = 0;
public int IdBook
{
get{return idBook;}
set{idBook = value;}
}
private string autore;
public string Autore
{
get{return autore;}
set{autore = value;}
}
private string titolo;
public string Titolo
{
get{return titolo;}
set{titolo = value;}
}
}
La nostra classe Book è pronta e ci permette di memorizzare tutto quello che ci serve. Il limite di questa struttura è che non abbiamo tenuto conto della necessità di collegarla ad un controllo. Se ad esempio provassimo ad aggiornare l’oggetto book non ci sarebbe il refresh automatico dei controlli a cui il nostro oggetto è collegato. La soluzione a questo problema è l’implementazione dell’interfaccia INotifyPropertyChanged che permette di notificare al Framework quando una proprietà è stata modificata. In pratica all’interno di ogni costrutto set dobbiamo richiamare un evento di tipo PropertyChangedEventHandler: questo evento verrà ridefinito in automatico dal Framework nel momento in cui ci sarà il collegamento con un controllo e al momento della notifica verranno aggiornati i controlli che sono in binding con la nostra classe.
Oltre a questo specifichiamo con l’attributo DisplayName il testo da mostrare in fase di collegamento, omettendo questo attributo il framework mostrerà il nome della nostra property.Ecco la nostra classe con le opportune modifiche(solo per la prima property):
public class Book : INotifyPropertyChanged
{
public Book(){}
private int idBook = 0;
[DisplayName("Codice")]
public int IdBook
{
get{return idBook;}
set
{
idBook = value;
//notifica il cambiamento del id
NotifyPropertyChanged("IdBook");
}
}
public event PropertyChangedEventHandler PropertyChanged; protected void NotifyPropertyChanged(string info) { if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(info)); }
}
BindingList<Book>
A questo punto costruiamo la nostra collezione tipizzata di Book, al fine di renderla collegabile ad una DataGridView ereditiamo dalla classe generica BindingList<Book>, che a sua volta eredita da Colllection<Book>.La classe BindingList,introdotta con la nuova versione del framework, implementa in maniera generica tutte le funzionalità dell'interfaccia IBindingList, è infatti quest’ultima che permette di rendere “parlante” la nostra collezione in fase di binding gestendo il refresh dei controlli.Non sempre la classe BindingList rappresenta la scelta migliore, solitamente per implementare molte funzionalità presenti nel Dataset è necessario definire direttamente l'interfaccia IBindingList, cosa non possibile in questo articolo per motivi di spazio.
Nella nostra collection implementiamo l’interfaccia ITypedList: quest’ultima fornisce lo schema degli oggetti associabili, è fondamentale nel momento in cui gli elementi associabili differiscono dalle proprietà pubbliche dell'oggetto verso cui effettuare l'associazione.Il valore di ritorno del metodo GetItemProperties è l'insieme di proprietà con cui effettuare il collegamento.Possiamo quindi modificare l'ordine con cui effettuare il binding ed eventualmente omettere delle property che non devono essere visualizzate all'utente.
Membri dell’interfaccia ITypedList:
string GetListName(PropertyDescriptor[] listAccessors);
PropertyDescriptorCollection GetItemProperties(PropertyDescriptor[] listAccessors);
Di seguito la classe BookCollection:
public class BookCollection : BindingList<Book>,ITypedList
{
public BookCollection(){}
PropertyDescriptorCollection ITypedList.GetItemProperties(PropertyDescriptor[] listAccessors)
{
PropertyDescriptorCollection propsCollection = TypeDescriptor.GetProperties(typeof(Book));
//Modifico l'ordine
PropertyDescriptorCollection result = propsCollection.Sort(new string[] { "IdBook", "Titolo", "Autore" });
return (result);
}
string ITypedList.GetListName(PropertyDescriptor[] listAccessors)
{
return this.GetType().Name; }
}
A questo punto la nostra collection è pronta ed è in grado di interagire con i controlli, ma il nostro obiettivo è quello di permettere un utilizzo di quest’ultima anche a Design time utilizzando la medesima integrazione del Dataset con il Visual Studio 2005 che permette un notevole risparmio di tempo.
La soluzione è nel definire un ulteriore classe che eredita da System.ComponentModel.Component in modo da poter visualizzare il nostro componente all’interno della cassella degli strumenti del visual studio insieme ai componenti standard del framework.Includiamo all'interno del componente la nostra collezione ed implementatiamo l’interfaccia IListSource che permette di fornire a un oggetto le funzionalità per restituire un elenco che possa essere associato a un'origine dati.Vista la semplicità di tale classe ometto il codice.
CONCLUSIONE
La nostra sorgente dati è pronta, possiamo utilizzare il nostro componente semplicemente trascinandolo all’interno del nostro form con il Drag&Drop e modificare il contenuto della collection anche a Design time, oltre al fatto che tutte le modifiche all’interno della nostra sorgente dati vengono automaticamente riportate anche a livello di interfaccia utente.
-
Ma nel caso di voler poi persistere i dati in un db, conviene ancora usare una simile soluzione, oppure è preferibile utilizzare un dataset, magari fortemente tipizzato?
Grazie,
Diego Parolin
di
Diego Parolin
-
11/12/2006 3.06.43
-
Ovviamente l'esempio qui descritto non è idoneo alla persistenza dei dati sul db, ma può essere un ottimo punto di partenza.Ti consiglio di dare un occhiata a questo http://www.codeplex.com/RafCollection , una collezione tipizzata che affronta in maniera completa tutte le problematiche descritte in questo articolo, aggiungendo moltissime funzionalità tra cui la persistenza dei dati.
di
Vincenzo Foggia
-
12/12/2006 1.18.28
indietro