Come accedere a SQL Mobile 3.5 da un'applicazione mobile

Dopo i primi post introduttivi sulla piattaforma Windows Mobile, necessari per approcciare questo argomento, in molti mi hanno chiesto di mostrare come creare nella pratica un'applicazione da zero. Oggi realizzeremo quindi un programma per Smartphone completo e funzionante basato su Compact Framework 3.5, Microsoft SQL Server Compact 3.5 e le classi legate al Pocket Outlook preistallato sul dispositivo, utilizzando la versione Professional di Visual Studio 2008.

Inizieremo con la teoria, esaminando gli oggetti del Compact Framework 3.5 che consentono l'accesso a SQL Mobile; vi darò poi un breve esempio di come creare le classi per accedere ad un database mobile vero (che potete scaricare dai link sotto riportati), quindi vi guiderò a realizzare un esempio passo passo testandolo sull'emulatore incluso in Visual Studio 2008 o, se avete letto il mio post sul collegamento bluetooth, anche su un dispositivo fisico. Se avete un minimo di basi tecniche sullo sviluppo .NET, e avete letto il mio post di introduzione alla piattaforma mobile, in meno di un'oretta sarete in grado di creare da soli la prossima applicazione. E allora, che cosa stiamo aspettando? Mettiamoci all'opera!

 

Iniziamo dalla definizione di DataSet, valida in generale con ADO.NET: tale oggetto rappresenta un insieme di viste -chiamate DataTable- associate al nome del DataSet. Per esempio, un DataSet potrebbe chiamarsi ANAGRAFICA e contenere una vista relativa all'anagrafica clienti (chiamata CLIENTI), una dei fornitori (FORNITORI) e una per i dipendenti (DIPENDENTI). Nell'esempio che segue avremo un dataset OrdersDataSet contentente una singola vista chiamata Inventory.

Nota: in questa esposizione, i caratteri evdienziati in blu rappresentano i nomi scelti dallo sviluppatore assegnati agli oggetti, mentre la parte in rosso è quella aggiunta da Visual Studio. In grassetto sono invece indicate le keyword della class library e del linguaggio C#.

Quando si crea un DataSet (NomeDataset) contenente una DataTable (NomeVista) utilizzando il Visual Studio 2008, viene generato in particolare un file NomeDataset.XSD che rappresenta il dataset in formato XML –che il designer mostra in formato grafico- e in più il file NomeDataset.Designer.cs che contiene, fra altro, la definizione delle seguenti quattro classi:

  • La classe NomeDataset, che rappresenta appunto il dataset
  • Le seguenti classi figlie di NomeDataset:
    • NomeVistaDataTable, che rappresenta la vista all’interno del dataset
    • NomeVistaRow, che rappresenta un record di NomeVistaDataTable
  • NomeVistaTableAdapter, che indica come la vista è riempita dalla sorgente dati. Quindi un TableAdapter contiene principalmente:
    • La stringa di connessione
    • I comandi per SELECT, INSERT, UPDATE, DELETE

Le due classi NomeVistaDataTable e NomeVistaRow sono fortemente tipizzate e rappresentano i dettagli della vista definita nel DataSet; possono quindi essere usate nel codice sfruttando le proprietà che si mappano 1:1 sui campi della vista corrispondente; in altre parole, per ogni campo della vista NomeVista esiste una corrispondente proprietà nell’oggetto NomeVistaRow.

Un’applicazione mobile che deve leggere i record di un datatable utilizza gli oggetti DataSet e TableAdapter spiegati sopra; in più, deve creare un oggetto BindingSource, che serve a eseguire le operazioni di spostamento, conteggio dei record, calcolo della posizione, recupero del record corrente etc. Tale oggetto contiene, fra gli altri, anche l’evento CurrentChanged che viene richiamato ogni volta che ci si sposta di record in record. La creazione dell’oggetto BindingSource e relativa configurazione con DataSet e TableAdapter può essere effettuata automaticamente dal Visual Studio se trascinate sulla form un campo (o un’intera tabella) prendendola dalla finestra “Data Sources”; by default, l’oggetto così creato si chiamerà NomeVistaBindingSource, di tipo BindingSource, che contiene anche l’evento NomeVistaBindingSource_CurrentChanged che viene richiamato ogni volta che un record viene mostrato sulla form.

Esempio

Prendiamo un database di tipo Microsoft SQL Server Compact 3.5 chiamato Orders.sdf, che contiene la tabella Inventory. Creiamo un data source (che chiamiamo OrdersDataSet) di tipo Microsoft SQL Server Compact 3.5, leghiamolo a Orders.sdf e includiamo Inventory nella configurazione del DataSource. Se eseguiamo questa operazione utilizzando il menu Data – Add New Data Source... , il VS 2008 crea automaticamente le seguenti classi nel file OrdersDataSet.Designer.cs:

  • classe OrdersDataSet
    • classe OrdersDataSet.InventoryDataTable
    • classe OrdersDataSet.InventoryRow
  • classe InventoryBindingSource
  • classe InventoryTableAdapter

La proprietà Current di InventoryBindingSource mantiene un riferimento ad un oggetto DataRowView che corrisponde alla riga mostrata; in pratica, rappresenta un record di una tabella e come tale può essere utilizzato attraverso le sue proprietà per essere cancellato, modificato, letto etc.

Ma in particolare la sua proprietà Row è un oggetto di tipo OrdersDataSet.InventoryRow.

Ciò significa che posso dichiarare un oggetto di tipo OrdersDataSet.InventoryRow e poi assegnargli InventoryBindingSource .Current.Row; poiché però Current è di tipo Object, devo fare due cast:

  • DataRowView rowView = (DataRowView) InventoryBindingSource.Current;
  • OrdersDataSet.InventoryRow row = (OrdersDataSet.InventoryRow) rowView.Row;

A dimostrazione del fatto che le classi create dal designer sono fortemente tipizzate, si noti che il campo ImageFileName (di tipo stringa) della tabella Inventory è rappresentato da row . ImageFileName, e per esempio può essere utilizzato per creare e mostrare l’immagine in un oggetto di tipo PictureBox chiamato MyPictureBox che posizioniamo sulla form:

  • MyPictureBox.Image = new Bitmap(row.ImageFileName)

Step by Step

Lanciamo il Visual Studio 2008 Porfessional (le versioni Express non includono la parte di sviluppo mobile) e creiamo un nuovo progetto di tipo Smart Device chiamato OrderManager, basato sul Compact Framework 3.5, il C#, piattaforma Windows Mobile 5 per Smartphone e template per Device Application; in altre parole, stiamo creando un’applicazione finestra da far girare su uno Smartphone che monta il Compact Framework 3.5:image image

Scarichiamo il file di database Orders.sdf di tipo SQL Server Compact 3.5, che mettiamo in una cartella temporanea.

Dal menu Data, aggiungiamo un nuovo data source e leghiamolo al database scaricato al punto precedente creando una nuova connessione: imageimage image

Rispondete sì alla domanda che chiede di includerlo nel progetto:
image

Scegliete di includere la tabella Inventory -che è anche l'unica- e chiamate OrdersDataSet il DataSet così creato:
image

Ora prendiamo la form, dove è rappresentato in anteprima il device su cui verrà installata la vostra applicazione, e creiamo i menu Avanti e Uscita; non vi indico i passi dettagliati perché sono veramente banali per chi ha una minima esperienza di utilizzo del Visual Studio; per tutti i dettagli potete comunque consultare questo articolo:
image

Togliamoci subito il pensiero e implementiamo la risposta al menu di uscita; chiamatelo per esempio menuUscita poi fate doppio click su di esso e scrive questa singola istruzione:

private void menuUscita_Click(object sender, EventArgs e)
{
Application.Exit();
}

Ora dal menu Data scegliete Show Data Sources e poi selezionate Details dalla lista a discesa:
image

Tale scelta farà in modo che trascinando la tabella Inventory sulla form, verranno creati tante TextBox quanti sono i (quattro) campi della tabella; si noti che oltre alle TextBox, nella parte inferiore della form vengono creati (e automaticamente configurati) anche gli oggetti che servono a estrarre i dati, ovvero il DataSet (ordersDataSet),  il BindingSource (inventoryBindingSource) e il TableAdapter (inventoryTableAdapter), come avevo anticipato nella sezione Esempio di questo post:
image

Ora rispondiamo alla pressione del tasto Avanti: fate doppio click sul menu e scrivete questa istruzione che incrementa il puntatore di uno, sfruttando anche l'operatore modulo (%) per consentire di ritornare al primo record se si preme Avanti quando ci si trova sull'ultimo record:

private void menuAvanti_Click(object sender, EventArgs e)
{
inventoryBindingSource.Position = (inventoryBindingSource.Position + 1)
% inventoryBindingSource.Count;
}

Vogliamo prenderci un po' di soddisfazione e provare l'applicazione? Premete F5 e selezionate l'emulatore per Windows Mobile 5; potrebbe volerci anche qualche minuto se la vostra immagine è pulita perché nel caso il Visual Studio deve installare prima il Compact Framework 3.5:
image

Se tutto va come deve, ecco comparire la vostra applicazione; verificate il funzionamento premendo il tasto Avanti e, alla fine, il tasto di uscita:
image

Che cosa possiamo aggiungere adesso? Beh, io direi che sarebbe meglio vedere le immagini che rappresentano i prodotti, piuttosto che leggerne solamente il nome del file, voi che ne dite? Allora, avanti...

Chiudete l'applicazione utilizzando il menu di uscita, ma lasciate attivo l'emulatore: risparmieremo così il tempo necessario a installare nuovamente il compact framework.

La prossima operazione consiste nel copiare nel dispositivo le immagini da mostrare; esse infatti non risiedono nel database; per accedere al "disco" del dispositivo si può procedere in vari modi, io vi suggerisco di stabilire una connessione via ActiveSynch verificando che le impostazioni del Windows Mobile Device Center prevedano l'ascolto sulla porta DMA (che è quella attraverso cui comunica l'emulatore):
image

A questo punto, in Visual Studio lanciate il Device Emulator Manager:
image

Localizzate l'immagine dell'emulatore che avete ancora in esecuzione in background e dal menu contestuale scegliete Cradle; tale operazione simula il collegamento fisico del dispositivo al PC e fa partire l'ActiveSync:image 

Dall'interno del Mobile Device Center scegliete "Browse the content of your device":
image

Ora con una semplice operazione di Drag&Drop copiate la cartella images dentro la root del dispositivo.

Bene: le immagini JPG ci sono. Ora dobbiamo solamente mostrarle ;-).

Riprendiamo la form, cancelliamo tutte le label tranne Price ed eliminiamo pure la TextBox che mostra il nome di file; infine trasciniamo nella form dalla barra degli strumenti un oggetto di tipo PictureBox (che chiamiamo myPicture) e impostiamone la proprietà SizeMode a StretchImage:
image

Vi ricordate quando all'inizio dicevo che l'oggetto BindingSource ha un metodo CurrentChanged che viene richiamato ogni volta che ci si sposta di record in record? E' arrivato il momento di usarlo: fate doppio click su InventoryBindingSource nella parte bassa della finestra Form1.cs ed inserite queste quattro istruzioni:

DataRowView rowView = (DataRowView)inventoryBindingSource.Current;

OrdersDataSet.InventoryRow row =
(OrdersDataSet.InventoryRow)rowView.Row;

Bitmap myBitmap = new Bitmap(row.ImageFilename);

myPicture.Image = myBitmap;

Come avevo anticipato sopra nella sezione Esempio, la proprietà Current di InventoryBindingSource mantiene un riferimento ad un oggetto DataRowView che corrisponde alla riga mostrata. Tale oggetto può essere utilizzato attraverso le sue proprietà per essere cancellato, modificato, letto etc. Ma in particolare la sua proprietà Row è un oggetto di tipo OrdersDataSet.InventoryRow. Ciò significa che posso dichiarare un oggetto di tipo OrdersDataSet.InventoryRow e poi assegnargli InventoryBindingSource .Current.Row; poiché però Current è di tipo Object, ho dobuto effettuare due cast.

Che altro possiamo fare per complicarci la vita e rendere questa applicazione ancora più interessante? Che ne dite, potremmo aggiungere una funzione che invia la bitmap per mail utilizzando Pocket Outlook dello Smartphone... Avanti allora.

Inannzi tutto, aggiungiamo i riferimenti agli assembly che gestiscono il Contact Picker, che è l'oggetto del Compact Framework che fornisce un facile accesso alla lista dei contatti; prendete il menu contestuale della cartella References, selezionate Microsoft.WindowsMobile.Forms e Microsoft.WindowsMobile.PocketOutlook e premete OK:image

A proposito: se state facendo il deployment sull'emulatore, allora non vi sono contatti definiti, createne quindi un paio di prova. Spero non ci sia bisogno di istruzioni per questo... ;-)

Adesso aggiungete la dichiarazione Using all'inizio del file Form1.cs:

using Microsoft.WindowsMobile.Forms;
using Microsoft.WindowsMobile.PocketOutlook;

Ora aggiungiamo una voce "Invia" al Menu di destra, facciamo doppio click su di essa e inseriamo le istruzioni per recuperare le informazioni da inserire nel messaggio:

  • prima creiamo un'istanza dell'oggetto ChooseContactDialog che chiamiamo contactPicker:
       ChooseContactDialog contactPicker = new ChooseContactDialog();
  • poi facciamo comparire la finestra di richiesta di selezione contatto e verifichiamo se ne è stato selezionato uno:
       if (contactPicker.ShowDialog() == DialogResult.OK) { }
  • adesso, all'interno delle parentesi graffe al punto precedente, scriviamo le istruzioni per creare il messaggio e spedirlo con allegata la bitmap; a questo scopo dichiariamo una variabile chiamata rowView di tipo DataRowView e le assegnamo il valore della proprietà Current di inventoryBindingSource; notate che i passi sono molto simili a quelli utilizzati in precedenza per creare la bitmap:
       DataRowView rowView = (DataRowView)inventoryBindingSource.Current;
  • Infine creiamo la variabile selectedProduct di tipo OrdersDataSet.InventoryRow e assegnamole il valore di rowView:
       OrdersDataSet.InventoryRow row = (OrdersDataSet.InventoryRow)rowView.Row;

Adesso che abbiamo recuperato le informazioni da spedire, creiamo il messaggio di posta; a questo scopo dichiariamo un oggetto message di tipo EmailMessage e gli associamo i classici campi oggetto, corpo, destinatario e allegato:

EmailMessage message = new EmailMessage();
message.Subject = "The picture you requested";
message.BodyText = "Attached is the picture we discussed";

Recipient client =
new Recipient(contactPicker.SelectedContact.Email1Address);
message.To.Add(client);

Attachment attachedImage = new Attachment(row.ImageFilename);
message.Attachments.Add(attachedImage);

Da ultimo, spediamo il messaggio con l'istruzione Send; questo metodo richiede il nome dell'account di email da cui spedire, quindi se state usando l'emulatore via ActiveSync scrivete ActiveSync come nell'istruzione che segue:

message.Send("ActiveSync");

 

Abbiamo terminato: verificate di avere almeno un contatto con indirizzo di e-mail valido ed eseguite l'applicazione. Per testarla uscite dall'applicazione, lanciate Pocket Outlook e verificate che il messaggio si trovi in posta in uscita:
image 

That's all folks. Qui trovate i sorgenti completi.

Mi scuso per il post un po' lungo, ma spero vi siate divertiti e lo abbiate trovato abbastanza chiaro. Lasciate pure i vostri commenti qui sotto, risponderò a tutti. Grazie dell'attenzione, vi auguro buon lavoro. Alla prossima.