Il libro sul data binding sta dando i suoi piccolo
frutti. Ho migliorato parte del codice del mio sw di fatturazione, con una
piccola (e banale) chicca a cui sinceramente prima non avevo pensato. Questa
chicca, a chi dovesse interessare, è spiegata a pag.114 di Data Binding with Windows Forms
2.0. Così, chi ce l'ha, può dargli
un'occhiata...vero, Marco?
Com'era il mio codice prima...
Dunque, vedrò di essere il
più breve possibile. In ogni Windows Form del mio sw, faccio vivere
un'istanza di un oggetto, dipendentemente dalla form stessa. Sulla form degli
articoli, c'è un oggetto currentEntity di tipo Articolo. Sulla form delle fatture, c'è un oggetto
currentEntity di tipo Fattura. E così via:
ovviamente, Articolo e Fattura
sono entrambi classi definite nel mio domain model.
Ho avuto un piccolo problema, che aveva risolto in un modo poco elegante, che
ho sistemato questa mattina. L'utente apre la WF dei clienti - supponiamo -
è la vede completamente vuota. Il binding non è attivo, tutte le TextBox sono
vuote. Attraverso vari meccanismi (dataset tipizzati, data provider,
DataGridView, etc.), l'utente sceglie un cliente (magari per
visualizzarlo) cliccando sul pulsante Sfoglia. Il codice
che gestisce il click è il seguente:
public override void PulsanteSfoglia(object sender, EventArgs e)
{
using (FormSfoglia frm = new FormSfoglia())
{
frm.DataSetToView = provider.GetDataSetBrowse();
if (frm.ShowDialog() == DialogResult.OK)
{
// aggiorno l'oggetto con quello selezionato dall'utente
// sulla WF di sfoglia
refreshEntity((Cliente)provider.GetInstance("Codice", frm.SelectedValue));
errNotifica.Clear();
// entro in modalità Modify
EntityState = FormEntityStateEnum.Modify;
this.txtCodice.Focus();
}
}
}
La form di dialogo ritorna tramite la proprietà
SelectedValue una stringa che contiene il codice del cliente
che voglio visualizzarlo. Notare l'uso del metodo privato refreshEntity: a cosa serve? Questo è quello che volevo
eliminare. La refreshEntity non fa altro che riassegnare una ad una le proprietà
dell'oggetto Cliente:
private void refreshEntity(Cliente entity)
{
currentEntity.CAP = entity.CAP;
currentEntity.Città = entity.Città;
currentEntity.Codice = entity.Codice;
currentEntity.Denominazione = entity.Denominazione;
currentEntity.FareBusta = entity.FareBusta;
currentEntity.ID = entity.ID;
currentEntity.Pagamenti = entity.Pagamenti;
currentEntity.PartitaIva = entity.PartitaIva;
currentEntity.Password = entity.Password;
currentEntity.Provincia = entity.Provincia;
currentEntity.Sigla = entity.Sigla;
currentEntity.Username = entity.Username;
currentEntity.Via = entity.Via;
}
Nel blocco di codice qui sopra, l'oggetto entity è il
nuovo cliente che voglio vedere sulla form, mentre
currentEntity è il cliente attualmente visualizzato.
Questo codice è necessario per scatenare l'aggiornamento
dei controlli bindati, che altrimenti non avviene. Difatti, il
semplice assegnamento currentEntity = entity è un semplice assegnamento di un
riferimento, e non passa attraverso le NotifyPropertyChanged definite nel
business object Cliente. Anche se è necessario, non mi
piace per nulla: lungo, noioso, soggetto ad errori, mi devo
ricordare di aggiornarlo se cambio qualcosa nel mio business object. Insomma,
c'erano tutti i buoni propositi per fare le cose in modo diverso e più
performante. Come? La risposta è arrivata ieri sera.
Il componente BindingSource
La magia sta nel componente
BindingSource, che va semplicemente trascinato sulla Windows Forms.
Non storcete il naso, non ci sono wizard di mezzo. Pensate a
questo componente come a una sorta di proxy. Nella logica del
binding, si frappone fra i controlli bindati e la datasource sottostante.
Possiamo controllare - attraverso una serie di eventi - l'aggiornamento dei
controlli e del datasource, intercettando il flusso dei dati da e verso una
certa direzione. Ma la cosa interessante è che settare
una nuova datasource provoca automaticamente l'aggiornamento di tutti i
controlli bindati a tale datasource. Mi spiego meglio:
#region SetupDataBindings()
private void SetupDataBindings()
{
if (!activeBinding)
{
bndSrc.DataSource = currentEntity;
txtCodice.DataBindings.Add("Text", bndSrc, "Codice");
txtRagioneSociale.DataBindings.Add("Text", bndSrc, "Denominazione");
txtVia.DataBindings.Add("Text", bndSrc, "Via");
txtCAP.DataBindings.Add("Text", bndSrc, "CAP");
txtCittà.DataBindings.Add("Text", bndSrc, "Città");
txtProv.DataBindings.Add("Text", bndSrc, "Provincia");
txtPartitaIva.DataBindings.Add("Text", bndSrc, "PartitaIva");
txtTipoPagamento.DataBindings.Add("Text", bndSrc, "Pagamenti");
txtSigla.DataBindings.Add("Text", bndSrc, "Sigla");
txtUsername.DataBindings.Add("Text", bndSrc, "Username");
txtPassword.DataBindings.Add("Text", bndSrc, "Password");
activeBinding = true;
}
}
#endregion
Mentre prima bindavo direttamente datasource e controlli, adesso passo
attraverso il BindingSource bndSrc. Questo è il primo punto. Il secondo
punto, che sbroglia la matassa, è che adesso la brutta refreshEntity non mi
serve più. Quando l'utente sceglie un nuovo cliente, mi basta semplicemente
reimpostare il datasource del BindingSource e tutti i controlli vengono
aggiornati.
// ...codice, codice, codice
// aggiorno l'oggetto con quello selezionato dall'utente
// sulla WF di sfoglia
currentEntity = (Cliente)provider.GetInstance("Codice", frm.SelectedValue);
bndSrc.DataSource = currentEntity;
// ...codice, codice, codice
Questo è un codice molto più pulito, snello ed efficiente. Che dire, evviva
il data binding una volta di più!!!