Magazine

DATABINDING CON CUSTOM COLLECTION

Vincenzo Foggia

26/11/2006

L'articolo mostra come implementare il databinding per le custum collection e costruire un componente per lavorare a design time.

0%100%
per esprimere un voto è necessario registrarsi al sito

Framework 2.0,Generics

Source.zip (46,00 Kb)

 

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.

Commenti
  • 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
Nome

Sito web
Commento


indietro