Usare il data-binding può veramente togliere gran parte del
lavoro di scrittura del codice. In questi giorni lo sto esplorando ben bene, e
devo dire che, sebbene ci siano molti modi diversi e sicuramente più ottimizzati
per realizzare WF che prelevino i dati da un database, è veramente divertente
e piacevole vedere cosa si può fare in così poco tempo. Si è sul serio
molto più produttivi.
Non mi interessa parlare di DataSource,
DataMember ed affini. In parte l'ho già fatto nel mio post precedente
, in parte vorrei invece approfondire un discorso che, ancora una
volta, mi ha colpito positivamente. Ovvero, parlare della classe
Binding, che è una sorte di "ponte" che si trova a metà
strada tra il nostro Control e la sorgente
dati sottostante. Usando la classe Binding, infatti, possiamo
intervenire nel momento in cui .NET fa il binding vero e proprio, e giocare come
vogliamo. Vediamo un po' più nel dettaglio cosa intendo.
Qual'è lo scopo della classe Binding? A cosa
serve?
Abbiamo detto che la classe Binding è il motore nascosto che
fa muovere tutto il data-binding delle nostre WF. Quando chiamiamo il metodo
Add della property DataBindings di un
qualsiasi controllo, in realtà creiamo implicitamente un'istanza della classe
Binding. Possiamo poi recuperarla accedendo alla collection
DataBindings del relativo controllo, come ad esempio qui sotto,
tratto da un'applicazione che sto creando proprio in questi giorni:
// recupero Binding accedendo alla collection DataBindings
Binding bnd = this.txtDenominazione.DataBindings["Text"];
La classe Binding espone, tra le altre cose, di due eventi
importanti ed utili: Format e
Parse. Il Format si scatena ogni volta che
un'informazione viene formattata su un controllo. Il Parse si scatena ogni volta
che un'informazione viene editata sul controllo ed aggiornata nella sorgente
dati. E' sufficiente aggiungere un delegate per questi due eventi per manipolare
a piacimento quello che "va e che viene, da e verso la sorgente dati". Ad
esempio, banalmente:
Binding bnd = this.txtDenominazione.DataBindings["Text"];
bnd.Format += new ConvertEventHandler(formatta);
.
...
private void formatta(object sender, ConvertEventArgs e)
{ e.Value = e.Value.ToString().ToUpper(); }
Se dalla sorgente dati leggo la stringa "via giovanni pascoli, 14",
nella mia TextBox apparirà "VIA GIOVANNI PASCOLI", proprio perchè l'evento
Format si frappone. In questo caso, ho messo un semplice
ToUpper() per convertire tutto maiuscolo: avremmo
potuto fare qualsiasi altra cosa, nei limiti del framework (!). Stessa cosa
all'inverso, possiamo gestire l'evento Parse per restituire alla sorgente dati
l'informazione come vogliamo che venga scritta nel database.
Utilizzo avanzato del
data-binding
Usando questa tecnica, ho fatto una cosa più che
divertente.
Ho creato una piccola tabella
ForBind su SQL Server con un solo campo
DimFont di tipo int. Ho creato
record a caso con i valori: 24, 16, 36, 6, etc. Ho cominciato un progetto WF
nuovo, ho usato la toolbox per creare velocemente
SqlConnection, SqlDataAdapter e un
DataSet per caricare il contenuto della
tabella ForBind.
Poi, ho creato (via codice, questa volta), un binding su una TextBox della
mia WF tra la property Font e il campo DimFont. Ovviamente, compilando il
progetto ed eseguendolo, ho avuto un exception. E'
importante ricordare che se faccio il binding su una property di tipo
xyz (int, string, Font, size e così via), la sorgente dati deve in qualche modo ritornare un oggetto dello
stesso tipo. Tenendo a mente questo, ho scritto un piccolo
handler dell'evento Format in questo modo:
private void formattaFont(object sender, ConvertEventArgs e)
{
int dim = (int) e.Value;
e.Value = new Font("Tahoma", dim);
}
L'evento, come abbiamo visto, viene sollevato prima che la
sorgente dati comunichi con il Control. Intercettiamo la chiamata, restituiamo
un oggetto Font. Adesso, se eseguo il progetto, la TextBox usa un Font piuttosto
che un altro in base alla posizione del record su cui sono posizionato
all'interno della sorgente dati (ho aggiunto due Button per sfogliare in back e
in forward passando per il BindingManagerBase).
Con la property Font magari ha poco senso: immaginatevi però
che bello "bindare" il BackColor, o la Size o altre property che all'occasione
posso rivelarsi utili