BindingSource in Visual Studio 2005

E’ un pò di tempo che sto studiando il nuovo modello di gestione dei dataset tipizzati e del databinding in Visual Studio 2005. La prima cosa interessante nel modello è che finalmente gli adpater che gestiscono il trasferimento dei dati dal database nel dataset e viceversa sono stati inserire all’interno del dataset stesso (anche se in realtà non è proprio così, cioè sono classi che stanno all’interno dello stesso namespace ma sono fuori dalla definizione del dataset) e non come avveniva in VS 2003 dove venivano generati esternamente ogni volta che per esempio si faceda drag & drop (pratica sconfessata da Roberto Brunetti :-))di una tabella di SQL Server su una form.

Dopo un pò di analisi del modello, mi sono reso conto che questo mi permette di sviluppare nel modo più RAD possibile ma mantenendo quella sorta di astrazione che deve avere l’interfaccia grafica dai dati sottostanti. Il designer del dataset tipizzato permette di gestire in un unico punto tutta la struttura del dataset e di gestire le query (anche più di una) che servono per gestire i dati (query, store procedure).

Il dataset tra l’altro viene generato usando il paradigma della partial class ed è quindi estendibile senza dover toccare il codice generato da VS (fantastico !!!!).

Allora mi sono detto, perchè non creare una vista (DataView) nel databaset tipizzato, renderla pubblica e fare databinding con la vista invece che con la tabella principale.

Scrivo la mia funzioncina , la rendo pubblica e poi utilizzo l’oggeto BindingSource per collegare la mia vista ad una datagridview. Il BindingSource è uno dei componenti più interessanti che sono stati aggiunti al nuovo framework (ricordo di una versione sviluppata da Francesco Balena per un evento ). In pratica permette di disaccopiare la sorgente dati di un controllo dalla sorgente reale (mi sa che non mi sono spiegato bene J). In questo modo io posso cambiare come mi pare la sorgente reale senza che il binding venga perso (un problema con la versione 1.1). Ottimo direi allora scrivo:

MyBindingSource.DataSource = MioDataset;

MyBindingSource.DataMember = “MyPersonalView”;

Cioè imposto come sorgente dati il mio dataset tipizzato e come membro la mia vista che è esposta come proprietà pubblica.

Lancio VS in quale si incazza come una biscia dicendomi che MyPersonalView non esiste in MioDataset. Ecco lo sapevo, non sono capace.

Allora comincio ad indagare per capire perchè non funziona e tramite reflector vado ad indagare per capire cosa succede quando viene associato un oggetto alla proprietà DataMember del BindingSource.

Ecco il codice :

 public void set_DataMember(string value)
{
      if (value == null)
      {
            value = string.Empty;
      }

      if (!this.dataMember.Equals(value))
      {
            this.dataMember = value;
            this.ResetList();
            this.OnDataMemberChanged(EventArgs.Empty);
      }
}

 

Come si vede, viene impostata la proprietà privata dataMember e viene chiamata ResetList:

 private void ResetList()
 {
      if (this.initializing)
      {
            this.needToSetList = true;
      }
      else
      {
            this.needToSetList = false;
            object obj1 = (this.dataSource is Type) ? BindingSource.GetListFromType(this.dataSource as Type) : this.dataSource;
            object obj2 = ListBindingHelper.GetList(obj1, this.dataMember);

            this.listExtractedFromEnumerable = false;
            if (!(obj2 is IList))
            {
                  if (obj2 is IEnumerable)
                  {
                        obj2 = BindingSource.GetListFromEnumerable(obj2 as IEnumerable);
                        this.listExtractedFromEnumerable = true;
                  }
                  else if (obj2 != null)
                  {
                        obj2 = BindingSource.WrapObjectInBindingList(obj2);
                  }
               else
                {
                        obj2 = BindingSource.CreateBindingList(ListBindingHelper.GetListItemType(this.dataSource, this.dataMember));
                  }
               }
               this.SetList(obj2 as IList, true, true);
               }

}

 

A questo punto viene controllato se il datasource è un Type oppure no. Nel nostro caso non è un Type e quindi viene chiamata ListBindingHelper.GetList(obj1, this.dataMember);

All’interno di GetList troviamo questo codice :

        object obj1;
      dataSource = ListBindingHelper.GetList(dataSource);
      if (((dataSource == null) || (dataSource is Type)) || string.IsNullOrEmpty(dataMember))
      {
            return dataSource;
      }
       PropertyDescriptorCollection collection1 = ListBindingHelper.GetListItemProperties(dataSource);
      PropertyDescriptor descriptor1 = collection1.Find(dataMember, true);
 Cioè si crea una collezione di proprietà tramite il metodo ListBindingHelper.GetListItemProperties(dataSource) e poi si cerca con il metodo find di trovare la proprietà passata.
 E qui si capisce perchè io non capisco nulla !!!!! 
 public static PropertyDescriptorCollection GetListItemProperties(object list)
{
      if (list == null)
      {
            return new PropertyDescriptorCollection(null);
      }
       if (list is Type)
      {
            return ListBindingHelper.GetListItemPropertiesByType(list as Type);
      }
       object obj1 = ListBindingHelper.GetList(list);
      if (obj1 is ITypedList)
      {
            return (obj1 as ITypedList).GetItemProperties(null);
      }
       if (obj1 is IEnumerable)
      {
            return ListBindingHelper.GetListItemPropertiesByEnumerable(obj1 as IEnumerable);
      }
      return TypeDescriptor.GetProperties(obj1);
}
  Viene fatto un controllo sulla sorgente dati per capire di che tipo è: vi dico subito che io pensavo che alla fine venisse chiamata questa funzione (l’ultima) TypeDescriptor.GetProperties(obj1) che tramite reflection va a navigare tra tutte le proprietà pubbliche di un oggeto (dove per altro veniva trovata la mia). 
Ecco che mi sono perso un passaggio fondamentale J  questa è la riga imporrtante dataSource = ListBindingHelper.GetList(dataSource) che nel nostro caso trasformerà il datasource da DataSet a DataViewManager
 
 public static object GetList(object list)
{
      if (list is IListSource)
      {
            return (list as IListSource).GetList();
      }
      return list;
} 
 Dato che il DataSet implementa IlistSource, GetList restituisce un oggetto di tipo DataViewManager e quindi nel codice precedente non verrà chiamata TypeDescriptor.GetProperties(obj1) ma return (obj1 as ITypedList).GetItemProperties(null) 
perchè DataViewManager implementa ITypedList. 
 Questo in pratica vuol dire che i membri di un dataset non vengono presi per reflection (per fortuna J) ma vengono esposte tutte le tabelle presenti nella collection Tables.
Per fare quello che volevo fare io ho dovuto cambiare il codice in questo modo 
 

MyBindingSource.DataSource = MioDataset.MyView;

Come si dice, alle volte si impara di più navigando nel codice che cercando su intenet J