Il binding di WPF è molto reattivo grazie a tutta la struttura di DependencyObjects e DependencyProperties a cui si appoggia. Per questo motivo lavorando con WPF molto spesso non ci dobbiamo preoccupare di fare un refresh dell'interfaccia utente per visualizzare i dati aggiornati.
Può capitare tuttavia di dover mettere in binding alcune sorgenti dati che non utilizzano DependencyProperties e che nella loro struttura non implementano l'interfaccia INotifyPropertyChanged . In questi rari casi accade che l'interfaccia utente non è aggiornata con i dati perchè non è in grado di capire che i dati sono stati modificati in qualche modo. Dobbiamo essere quindi noi a forzare il refresh.
Vediamo quali strumetni abbiamo a disposizione. Viene sicuramente in nostro aiuto la classe statica BindingOperations, la quale ci permette di interrogare la struttura ad oggetti dell'interfaccia utente e capire se le proprietà degli oggetti stessi sono in binding oppure no.
BindingOperations ci restituisce oggetti di tipo BindingExpression che sono di fatto il collegamento tra il target e la sorgente dati.
Possiamo utilizzare il metodo UpdateTarget per forzare la lettura dei dati ed un refresh dell'interfaccia utente.
Come facciamo tuttavia da codice a utilizzare questo metodo senza vincolare la grafica a delle strutture fisse di binding ?
Mi spiego meglio: Se utilizziamo questo metodo, dobbiamo conoscere esattamente quali sono i campi bindati per poter sollevare il binding e quindi vincoliamo lo sviluppo della parte grafica impedendo la modifica di template e di datatemplate.
Ci viene in aiuto un'altra classe molto importante: VisualTreeHelper.
Questa classe ci permette di navigare all'interno della VisualTree, ovvero all'interno degli oggetti renderizzati nell'interfaccia utente. Con un po' di reflection , e con l'aiuto di BidingOperations possiamo estrarre dalla VisualTree tutte le BindingExpressions ed invocare il metodo UpdateTarget.
Ecco il codice :
[code language="c#"]
private static void InvalidateBinding(DependencyObject objectbase)
{
// Per ogni oggetto all'interno del ramo di visualtree
for (int x = 0; x < VisualTreeHelper.GetChildrenCount(objectbase); x++)
{
// Leggo l'oggetto nodo
DependencyObject obj = VisualTreeHelper.GetChild(ww, x) as DependencyObject;
// Se è un DependencyObject
if (obj != null)
{
// Cerco tutti i fields di
foreach (FieldInfo finfo in obj.GetType().GetFields())
{
// Se il field trovato è una DependencyProperty allora può essere sottoposta a binding
if (finfo.FieldType.Equals(typeof(DependencyProperty)))
{
// Verifico se è sottoposta a binding
DependencyProperty dpvalue = (DependencyProperty)finfo.GetValue(obj);
if (BindingOperations.IsDataBound(obj, dpvalue))
{
// Estraggo la BindingExpression
BindingExpression expr = BindingOperations.GetBindingExpression(obj, dpvalue);
// Sollevo il metodo di aggiornamento del binding.
expr.UpdateTarget();
}
// Se il contenuto della DependencyProperty è un DependencyObject allora devo controllare anche tutte DP dell'oggetto contenuto nella DP corrente
DependencyObject subdpobject = obj.GetValue(dpvalue) as DependencyObject;
if (subdpobject != null)
if (!(subdpobject is Visual))
{
foreach (FieldInfo subfinfo in subdpobject.GetType().GetFields())
{
DependencyProperty subdpvalue = (DependencyProperty)subfinfo.GetValue(subdpobject);
if (BindingOperations.IsDataBound(subdpobject, subdpvalue))
{
BindingExpression subexpr = BindingOperations.GetBindingExpression(subdpobject, subdpvalue);
subexpr.UpdateTarget();
}
}
}
}
}
// Rendo la funzione ricorsiva in modo da esplorare tutta la VisualTree
InvalidateBinding(obj);
}
}
}
[/code]