Eccomi, direttamente dal mio giardino, pronto per andare
avanti e riprendere un attimo il discorso che avevamo lasciato in sospeso
mercoledì scorso. Nel mio ultimo post avevamo visto come salvare in un file
XML i compleanni che abbiamo inserito nella ListBox, ovvero:
- abbiamo aggiunto qualche voce al nostro MainMenu per
prevedere le funzioni di caricamento, di salvataggio e di importazione dei
nostri files XML
- abbiamo visto la creazione e l'utilizzo della function dichiarata
così:
private DataTable
getSaveTable(bool
WithData)
- abbiamo visto l'utilizzo di SaveFileDialog che consente
all'utente di scegliere dove salvare il file e con quale nome
- abbiamo visto, infine, il metodo WriteXML del
DataSet che scrive su disco il file XML (appunto) contenente
schema e dati della nostra classe
Operazione di caricamento da file
XML
Nel post di oggi vediamo di fare il percorso inverso,
ovvero di implementare il caricamento da file XML. Innanzitutto, facciamo così:
io ho aperto l'applicazione e ho inserito il mio compleanno, quello di
mio fratello e quello dei miei genitori. Ho salvato il file con il nome
CompleanniFamiglia.xml, sul Desktop. Quello che vogliamo fare
è, in breve: usare la classe OpenFileDialog per dare all'utente la possibilità
di scegliere quale file caricare, usare un DataSet e riempirlo con i dati
provenienti dal file ed infine popolare la ListBox con questi dati. Presto
fatto, qui sotto vi riporto il codice, che descriverò ancora più sotto.
private void mnuLoad_Click(object sender, System.EventArgs e)
{
// Creo un'istanza OpenFileDialog
OpenFileDialog dlgOpen = new OpenFileDialog();
// Imposto alcune property per facilitare l'usabilità
dlgOpen.CheckPathExists = true;
dlgOpen.AddExtension = true;
dlgOpen.Filter = "Files XML|*.xml";
if (dlgOpen.ShowDialog() == DialogResult.OK)
{
// creo un dataset
DataSet dsBirthdays = new DataSet();
// leggo i dati dal file XML salvato
dsBirthdays.ReadXml(dlgOpen.FileName);
this.lstResults.Items.Clear();
for(int i=0; i < dsBirthdays.Tables[0].Rows.Count; i++)
{
DataRow dr = dsBirthdays.Tables[0].Rows[i];
Age ageTemp = new Age(Convert.ToString(dr["Name"]), Convert.ToDateTime(dr["BirthDate"]));
this.lstResults.Items.Add(ageTemp);
}
// Dopo il caricamento, libero la memoria
dsBirthdays.Dispose();
}
dlgOpen.Dispose();
}
Dunque, vediamo i concetti. Istanziamo un oggetto
dlgOpen della classe OpenFileDialog, ne
impostiamo alcune property per aiutare l'utente e poi chiamiamo il metodo
ShowDialog(). Se l'utente seleziona un file questa classe ci
ritorna DialogResult.OK: in questo caso possiamo proseguire
senza troppi problemi.
Utilizziamo il DataSet dsBirthdays. Chiamo il metodo
ReadXML, passando come parametro dlgOpen.FileName. Se
l'operazione va a buon fine, dentro il mio DataSet ho appena caricato una
tabella con tutti i dati che mi servono. Svuoto la ListBox e comincio un ciclo
for...next per aggiungere tanti Item:
for(int i=0; i < dsBirthdays.Tables[0].Rows.Count; i++)
{
DataRow dr = dsBirthdays.Tables[0].Rows[i];
Age ageTemp = new Age(Convert.ToString(dr["Name"]), Convert.ToDateTime(dr["BirthDate"]));
this.lstResults.Items.Add(ageTemp);
}
Per pulizia di codice, dentro il ciclo istanzio un oggetto
DataRow, che è a conoscenza della struttura del DataSet: per
questo motivo posso chiamare i campi (Column) esattamente con il loro nome
reale, che gli era stato attribuito al momento del salvataggio.
Operazione di importazione da file
XML
Oh, è arrivato il momento per una piccola domanda.
Supponiamo di dare questa applicazione a due persone diverse: io inserisco i
compleanni di qualcuno dei miei amici, mio fratello inserisce quelli dei suoi
amici. Io ho raggiunto 30 compleanni, supponiamo, e mio fratello 18. Adesso
supponiamo di voler unire le due liste. Come possiamo fare? Vi sembra
complicato? Lo vediamo subito!
Continuiamo a supporre. Io salvo il mio file con il nome
CompleanniAmiciIgor.xml, mentre mio fratello lo salva come
CompleanniAmiciOmar.xml. L'obiettivo è quello di caricare uno
dei due files, e successivamente importare il secondo. Per questo motivo è stato
previsto il menu mnuImport, il cui handler completo di
codice è:
private void mnuImport_Click(object sender, System.EventArgs e)
{
// istanzio un dataset corrispondente agli items
// nella ListBox
DataSet dsScreen = new DataSet();
dsScreen.Tables.Add(this.getSaveTable(true));
// Creo un'istanza OpenFileDialog
OpenFileDialog dlgOpen = new OpenFileDialog();
// Imposto alcune property per facilitare l'usabilità
dlgOpen.CheckPathExists = true;
dlgOpen.AddExtension = true;
dlgOpen.Filter = "Files XML|*.xml";
if (dlgOpen.ShowDialog() == DialogResult.OK)
{
// Istanzio un secondo dataset con i dati letti
// dal file che 'utente ha scelto
DataSet dsFile = new DataSet();
dsFile.ReadXml(dlgOpen.FileName);
// Unisco i due dataset (metodo Merge)
// dsScreen.Merge(dsFile);
// o meglio, unisco solo la Tables(0)
dsScreen.Merge(dsFile.Tables[0]);
// Popolo la ListBox con il dataset dsScreen
// che contiene i dati uniti e mergiati assieme
this.lstResults.Items.Clear();
for(int i=0; i < dsScreen.Tables[0].Rows.Count; i++)
{
DataRow dr = dsScreen.Tables[0].Rows[i];
Age ageTemp = new Age(Convert.ToString(dr["Name"]), Convert.ToDateTime(dr["BirthDate"]));
this.lstResults.Items.Add(ageTemp);
}
}
}
Vediamo di vedere il codice che, qualcuno se ne accorgerà, in parte
riprende il discorso appena fatto sul caricamento. Innanzitutto ho bisogno di
due DataSet: dsScreen e dsFile. Il primo
contiene i dati contenuti nella ListBox, il secondo contiene i dati letti dal
file XML. Di fatto, il primo lo posso istanziare e popolare fin da subito, ed in
effetti è quello che ho fatto. Chiamo il metodo .Add sulla
collection Tables, passando come parametro la famigerata
getSaveTable. Il primo DataSet è pronto.
Per istanziare il secondo (che è quello che devo unire), devo usare ancora la
classe OpenFileDialog come abbiamo fatto prima. L'utente
sceglie il file che vuole importare prelevandolo da disco: in questo modo,
istanzio dsFile chiamando il suo metodo
ReadXML. Il secondo DataSet è pronto.
Adesso non mi resta da fare che accodare le DataRow di uno con le DataRow
dell'altro, giusto? Sì, è giusto, ma il framework ci dà una mano. La classe
DataSet espone un metodo Merge che unisce un DataSet con un
altro, ed in effetti è proprio quello di cui abbiamo bisogno.
dsScreen.Merge(dsFile.Tables[0]);
A questo punto, il DataSet dsScreen contiene i dati uniti,
per cui possiamo semplicemente fare il ciclo for...next visto prima e ripopolare
la ListBox daccapo.
Dal punto di vista pratico, io apro l'applicazione Age,
carico il file CompleanniAmiciIgor.xml: se abbiamo fatto tutto
bene, la ListBox si popola con i compleanni che avevo inserito. Poi importo:
seleziono il file CompleanniAmiciOmar.xml e come per magia la
ListBox contiene i compleanni di tutti e due.
Conclusioni
Sui DataSet
ovviamente c'è ancora molto da dire (e chissà quante cose ancora non conosco
neppure io, non fatemi pensare!!): magari parlando del databinding o di qualche
altro tema che verrà fuori, avrò modo di sviscerare qualcosa che oggi non ho
preso in considerazione.
Piccola
annotazione: se state leggendo, ricordatevi che io sto studiando
per diventare MCAD, non sono certo un guru, per cui, se lo ritenete opportuno,
commentati, correggetemi, criticatemi.........