Sei sulla pagina 1di 17

Accesso ai dati con Silverlight

Nella prima parte dell'articolo illustriamo alcuni concetti teorici necessari a comprendere
l'associazione ai dati in una Silverlight application. Nella seconda parte, attraverso un tutorial,
vedremo in pratica come collegare l'interfaccia utente ai dati provenienti da un database e attraverso
i vari livelli applicativi. Chi vuole mettere subito le mani sulla tastiera può passare direttamente al
tutorial e in un secondo momento riprendere questa prima parte.

L'associazione ai dati in una Silverlight Application

L'associazione tra controlli presenti nell'interfaccia utente e i dati contenuti in una collection, in un
documento Xml o in una Datatable avviene utilizzando l'oggetto Binding. Questo oggetto ci
consente di indicare da una parte il target cioè il destinatario dell'associazione (es. la proprietà Text
di una TextBox) e dall'altra la proprietà presente nell'oggetto che contiene i dati (es. la
RagioneSociale di un oggetto di tipo Cliente).

Page.xaml
[CODE]
<TextBox x:Name="txtRagioneSociale" Text="{Binding Path=RagioneSociale}" />
[/CODE]

Come si vede dall'esempio precedente è possibile dichiarare nel codice di markup l'oggetto Binding
direttamente nella proprietà Text di una TextBox e indicare il nome della proprietà presente
nell'oggetto che contiene i dati.

Le proprietà dell'oggetto Binding che ci interessano di più sono:


- Path: questa è la proprietà di Default e non è obbligatorio specificarla. Indica il nome della
proprietà che contiene il dato all'interno di una sorgente dati. L'esempio precedente potrebbe essere
scritto così "{Binding RagioneSociale}".
- Mode: questa properietà può assumere due possibili valori OneWay e TwoWay. Se si omette, il
valore di Default è OneWay. Se vogliamo che le modifiche apportate nel controllo vengano
propagate all'oggetto che contiene i dati è necessario specificare TwoWay

[CODE]
<TextBox x:Name="txtRagioneSociale" Text="{Binding RagioneSociale, Mode=TwoWay}" />
[/CODE]

- Source: questa proprietà è opzionale e consente di specificare direttamente nell'oggetto Binding la


sorgente dati. Ad esempio di può anche programmaticamente gestire l'associazione dati come nel
seguente esempio
Page.xaml.cs
[CODE]
Binding b = new Binding();
b.Source = ((Cliente)lbClienti.SelectedItem).RagioneSociale;
txtRagioneSociale.SetBinding(TextBox.TextProperty, b);
[/CODE]

Abbiamo visto la sintassi per utilizzare l'oggetto Binding e specificare le sue proprietà. Ora vediamo
come associare uno o più controlli ad un oggetto che contiene i dati.

Per associare uno o più controlli ad un singolo oggetto che contiene i dati, ad esempio un oggetto di
tipo Cliente contenente una serie di proprietà, possiamo utilizzare la classe DataContext. Essa è
esposta dai controlli Silverlight e serve per impostare la sorgente dati. Vediamo un esempio nel
quale la impostiamo programmaticamente. Supponiamo di avere una ListBox chiamata “lbClienti”
che contiene un elenco di oggetti di tipo Cliente e una TextBox chiamata “txtRagioneSociale”
potremo collegare alla proprietà DataContext della TextBox l'oggetto selezionato nella ListBox
così:

[CODE]
txtRagioneSociale.DataContext = lbClienti.SelectedItem as Cliente;
[/CODE]

L'associazione che abbiamo appena visto è tra un singolo controllo di tipo TextBox e un singolo
oggetto di tipo Cliente. Per evitare di dover scrivere una riga di codice per ogni controllo presente
nell'applicazione è meglio assegnare l'oggetto di tipo Cliente ad un Container che propagherà
automaticamente il suo DataContext a tutti i controlli in esso contenuti.
Esempio di container di tipo StackPanel il cui DataContext verrà propagato a tutti i suoi controlli
Child.

Page.xaml
[CODE]
<StackPanel x:Name="spDettagli">
<TextBlock>Ragione Sociale</TextBlock>
<TextBox x:Name="txtRagioneSociale" Text="{Binding RagioneSociale,
Mode=TwoWay}" />
<TextBlock>Citta</TextBlock>
<TextBox x:Name="txtCitta" Text="{Binding Citta, Mode=TwoWay}" />
</StackPanel>
[/CODE]

Page.xaml.cs
[CODE]
private void lbClienti_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
// La ListBox lbClienti contiene un elenco di oggetti di tipo Cliente
spDettagli.DataContext = lbClienti.SelectedItem as Cliente;
}
[/CODE]

Da tenere in considerazione anche che la classe che nel nostro esempio contiene i dati anagrafici,
deve implementare l'interfaccia INotifyPropertyChange se vogliamo che possa tenere memorizzate
le modifiche apportate ai dati. Questa interfaccia contiene un singolo evento PropertyChange. Per
poterlo utilizzare in una nostra classe il codice da scrivere è abbastanza semplice. Ecco un esempio
di classe Cliente con due proprietà e che implementa l'interfaccia INotifyPropertyChange:

[CODE]
class Cliente : INotifyPropertyChanged
{
private string _ragioneSociale;
public string RagioneSociale
{
get
{
return _ragioneSociale;
}
set
{
_ragioneSociale = value;
propertyChange("RagioneSociale");
}
}

private string _citta;

public string Citta


{
get
{
return _citta;
}
set
{
_citta = value;
propertyChange("Citta");
}
}

private void propertyChange(string _propertyName)


{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(_propertyName));
}
}

#region INotifyPropertyChanged Members

public event PropertyChangedEventHandler PropertyChanged;

#endregion
}
[/CODE]

Per collegare una collezione di oggetti ad un controllo in grado di visualizzare un elenco come ad
esempio una DataGrid o una ListBox è necessario utilizzare una collection che implementi
l'interfaccia IEnumerable come ad esempio List<t>. Se si vuole che le modifiche effettuate
dall'utente si propaghino alla nostra collection è necessario che la collection implementi anche
l'interfaccia INotifyCollectionChanged. Questa interfaccia consente di gestire gli eventi di modifica,
inserimento o eliminazione degli oggetti contenuti nella collection. Per comodità come collection
per i nostri oggetti si può utilizzare la collection generica ObservableCollection<t> presente nel
Framework e che implementa già IEnumerable e INotifyCollectionChanged.

Quindi, in conclusione, i singoli oggetti devono basarsi su una classe che implementi l'interfaccia
INotifyPropertyChanged mentre le collection devono implementare l'interfaccia IEnumerable e
INotifyCollectionChanged.

Inizio tutorial

L'obiettivo di questo tutorial è di visualizzare in una ListBox un elenco di clienti e visualizzare i


dettagli di ogni cliente selezionato. Oltre alla visualizzazione dell'elenco e dei dettagli vedremo
come implementare la modifica, eliminazione e inserimento dei dati stessi.

L'architettura della nostra applicazione rispecchierà il seguente schema

<Resource fileName='architettura.ppt' />

Dallo schema precedente vediamo come da una parte si trovano i dati e dall'altra la Silverlight
application la quale è ospitata in una normale pagina html.
Da notare a questo proposito che una applicazione Silverlight è visualizzata grazie ad un plug-in
disponibile per i browser più diffusi. Il plug-in viene ospitato all'interno di una pagina html
utilizzando il tag <object ...> contenente tutti i parametri necessari al corretto funzionamento.

Notiamo che nello schema precedente, tra i due estremi troviamo alcuni livelli applicativi necessari
per far comunicare Silverlight con il database oltre che utili per organizzare e rendere riutilizzabile
l'architettura della nostra applicazione. Silverlight è eseguito lato client, all'interno di un plug-in che
contiene una speciale versione del .Net framework. Non può accedere direttamente ai dati se questi
sono contenuti lato server ad esempio in un database. E' quindi necessario scrivere uno o più metodi
che interagiscono con la base dati ed esporre questi metodi attraverso un Web Service. In questo
articolo, per evitare di dover scrivere i singoli metodi utilizzando Ado.Net, ci serviremo della
tecnologia chiamata LinQ to Sql. Questa tecnologia presente dalla versione 3,5 in poi del .Net
Framework ci consente di creare con pochi click e senza scrivere una riga di codice tutto quanto è
necessario per accedere ai dati presenti in un database SqlServer. Nel caso dobbiate utilizzare un
database diverso da SqlServer allora potrete utilizzare direttamente Ado.Net oppure utilizzare
un'altra nuova tecnologia chiamata LinQ to Entity.

Creare il progetto Silverlight

Facciamo partire l'ambiente di sviluppo. Utilizzeremo per questo articolo Visual Web Developer
Express 2008 e il linguaggio C#. Sarà comunque disponibile il codice di esempio liberamente
scaricabile anche in Visual Basic.

Selezioniamo dal menu principale la voce File->New project

<Image fileName='SilverlightDB_01.jpg' />

Appaiono i vari template disponibili. Selezioniamo il template “Silverlight application” e


chiamiamo rispettivamente il progetto con il nome SilverlightApplication e la solution
SilverlightApplication_sol
<Image fileName='SilverlightDB_02.jpg' />
A questo punto, il wizard ci mostra una piccola finestra dove poter scegliere il tipo di applicazione
Silverlight.
Le scelte sono tra:
− creare due progetti di cui uno contenente la nostra applicazione Silverlight e uno separato che fa
riferimento e ospita la nostra applicazione

− creare un unico progetto contenente la nostra applicazione Silverlight con una semplice pagina
html che ospiterà la nostra applicazione (visualizzerà cioè il plug-in di Silverlight che caricherà
l'applicazione compilata) utile per i test.

Scegliamo la prima opzione e lasciamo quindi le impostazioni di default.

<Image fileName='SilverlightDB_03.jpg' />

In questo modo verranno creati due progetti. Uno che contiene l'applicazione Silverlight vera e
propria e un secondo progetto web che avrà al suo interno una pagina aspx già configurata per
contenere il plug-in con la nostra SilverlightApplication.

Questi sono i passaggi del tutorial:

− Creazione di un database SqlServer Express e una tabella Cliente

− La logica di accesso ai dati con LinQ to Sql: il Data Access Layer

− Il Web Service che espone metodi per leggere e scrivere dati

− Creare il layout dell'applicazione Silverlight

− Il Databind: associare i controlli con i dati e gestire le operazioni Create, Read, Update,
Delete (CRUD)

Creazione di un database SqlServer Express

Utilizzeremo un database SqlServer Express creato appositamente per questo articolo. Lo


aggiungeremo al progetto Web.
Procediamo. Clicchiamo con il tasto destro sul progetto SilverlightApplication.Web e selezioniamo
la voce “Add → New Item”

<Image fileName='SilverlightDB_04.jpg' />

Selezioniamo il template “Sql Database” e chiamiamo il nuovo database con il nome


“NegozioDB.mdf”

<Image fileName='SilverlightDB_05.jpg' />

Rispondiamo “Si” alla domanda se vogliamo spostare nella cartella “AppData” il nuovo database.
Ora che abbiamo creato il database dobbiamo creare la tabella Clienti. Selezioniamo la finestra
“Database Explorer” che ci consente di visualizzare e modificare il contenuto del database
NegozioDB appena creato. Per farlo, clicchiamo sulla linguetta di fianco a “Solution Explorer”
oppure se non la troviamo, selezioniamo nel menu principale la voce View → Database Explorer

<Image fileName='SilverlightDB_06.jpg' />

Una volta visualizzata la finestra “Database Explorer” espandiamo la voce NegozioDB, clicchiamo
con il tasto destro sulla cartella “Tables” e selezioniamo la voce “Add New Table”.

<Image fileName='SilverlightDB_07.jpg' />

Aggiungiamo le seguenti colonne deselezionando “Allow null”. Attenzione ai tipi di dato previsti
dalle colonne (int, nvarchar, bit).

<Image fileName='SilverlightDB_08.jpg' />

Impostiamo la colonna “IDCliente” come Primary Key

<Image fileName='SilverlightDB_09.jpg' />

visto che questa chiave è utile che sia generata automaticamente dal sistema, impostiamo l'opzione
“Identity” a “True”.

<Image fileName='SilverlightDB_10.jpg' />

Ora non ci resta che salvare la tabella. Selezioniamo il pulsante di salvataggio e diamo il nome
“Cliente”. Perchè è meglio scegliere il nome al singolare? Semplicemente perchè nel prosieguo di
questo tutorial utilizzeremo LinQ to Sql per creare un Data Access Layer e vedremo come la
generazione automatica dei nomi delle entità, una sola nel nostro esempio, è più corretta se le
tabelle di riferimento nel database sono al singolare.

<Image fileName='SilverlightDB_11.jpg' />

Ora dobbiamo aggiungere qualche record alla tabella. Clicchiamo con il tasto destro sulla tabella
visualizzata nella finestra “Server Explorer” e selezioniamo la voce “Show Table Data”

<Image fileName='SilverlightDB_12.jpg' />

Aggiungiamo qualche record riempiendo tutte le colonne tranne IDCliente che è generata
automaticamente

<Image fileName='SilverlightDB_13.jpg' />

Bene. Chiudiamo la finestra di inserimento e procediamo al prossimo step.


La logica di accesso ai dati con LinQ to Sql: il Data Access Layer

Come premessa al discorso relativo ai dati va sottolineato come il database sia fisicamente sul
server mentre l'applicazione Silverlight sia eseguita sul client. Per trasportare i dati dal database
all'applicazione Silverlight utilizzeremo delle entità. Una entità è rappresentata normalmente da una
classe che è capace di contenere i dati prelevati dal database e memorizzarli in campi che poi
vengono esposti con delle semplici proprietà. Non contiene logica di accesso al database e l'entità
svolge normalmente una funzione di trasporto. Alcuni programmatori scelgono la strada di scrivere
da soli il codice necessario per creare le entità. Altri programmatori scelgono di farsi aiutare da
strumenti che automaticamente creano le entità partendo dallo schema di un database.

Questi strumenti spesso creano sia le entità che corrispondono al modello a oggetti (Domain Model)
della nostra applicazione e sia la la logica necessaria per gestire la persistenza dei dati dandoci poi
la possibilità di interrogare e gestire il domain model. Esistono molti strumenti anche molto diffusi
di questo tipo: i cosidetti ORM Object-relational mapping. Uno dei più diffusi è NHibernate.

Dalla versione 3.5 del Microsoft .Net Framework sono stati inseriti una serie di nuove funzionalità
partendo da LinQ fino ad arrivare ad un vero e proprio ORM chiamato LinQ to Entity. LinQ sta per
Language Integrated Query e consiste in una serie di estensioni ai linguaggi del framework C# e
Visual Basic che consentono di interrogare e gestire modelli a oggetti usando una sintassi familiare
ai programmatori e uniforme indipendentemente dal tipo di data source e fortemente tipizzata.

Sono uscite numerose versioni di LinQ ognuna specializzata come ad esempio LinQ to Xml, LinQ
to Dataset, LinQ to Sql. In questo articolo utilizzeremo LinQ to Sql e vedremo come questa nuova
tecnologia ci consenta di creare con pochi click un modello a oggetti e un motore di persistenza
perfettamente funzionanti. A differenza di LinQ to Entity che è un vero e proprio ORM ed è
indipendente dal database utilizzato, LinQ to Sql ha un legame più stretto con lo schema del
database e può essere utilizzato solo con SqlServer.

Il modello a oggetti creato utilizzando LinQ to Sql lo possiamo disegnare facilmente con un
apposito designer. Aggiungiamo un nuovo Item al progetto SilverlightApplication.Web

<Image fileName='SilverlightDB_14.jpg' />

Selezioniamo il template di nome “LinQ to Sql Classes” e diamogli il nome NegozioClasses.dbml

<Image fileName='SilverlightDB_15.jpg' />

Ora, visualizziamo la finestra del Database Explorer e trasciniamo la tabella sul designer di LinQ to
Sql

<Image fileName='SilverlightDB_16.jpg' />

A questo punto, sapendo che i dati dovranno trasportare i dati in entrambi i sensi dal server al client
su cui girerà la nostra SilverlightApplication, dobbiamo preoccuparci di modificare una opzione che
rende serializzabili le entità. Clicchiamo su una zona libera del designer e cambiamo l'opzione
“Serialization Mode” in “Unidirectional”.
<Image fileName='SilverlightDB_17.jpg' />

Con queste poche semplici operazioni abbiamo creato un modello a oggetti e un relativo motore di
persistenza. Nel prosieguo del tutorial vedremo come sarà semplice utilizzare la classe Cliente e la
classe DataContext (NegozioDBClasseDataContext per la precisione). Quest'ultimo oggetto
rappresenta il motore di persistenza e ci mette a disposizione tutte le funzioni per gestire i dati
memorizzati nel database come le normali operazioni CRUD Create, Read, Update, Delete.

Il Web Service che espone metodi per leggere e scrivere dati

Un Web Service è simile ad un normale metodo. La sua utilità è il fatto che è richiamabile
attraverso internet perché si basa sui protocolli standard (es. http, soap, xml). I Web Services
servono per far parlare tra loro le applicazioni e non sono solitamente usati direttamente dall'utente.

Ai fini del nostro tutorial, creeremo un Web Service dentro l'applicazione web che esporrà alla
nostra SilverlightApplication alcuni metodi per leggere e scrivere i dati nel database. I metodi che
inseriremo nel servizio sono: GetClienti, SaveCliente, DeleteCliente.

Clicchiamo con il tasto destro sulla applicazione web di nome SilverlightApplication.Web e


aggiungiamo un nuovo item. Selezioniamo il template “Silverlight-enabled WCF Service” e
chiamiamo il Web Service con il nome “NegozioService.svc”

<Image fileName='SilverlightDB_18.jpg' />

All'interno della classe “NegozioService”, dichiariamo una variabile “db” di tipo


“NegozioClassesDataContext” e inizializziamola nel costruttore della classe

[CODE]
public class NegozioService
{
NegozioClassesDataContext db;

public NegozioService()
{
db = new NegozioClassesDataContext();
}
}
[/CODE]

Questa variabile rappresenta il nostro motore di persistenza creato automaticamente dalla tecnologia
LinQ to Sql nello step precedente e ci sarà utile all'interno dei metodi che ora scriveremo per gestire
facilmente il database.

Metodo GetClienti()
Il primo metodo che scriviamo è GetClienti che avrà come risultato una collection di oggetti di tipo
cliente: List<Cliente>. Sostituite il codice del metodo DoWork con il seguente frammento di codice

[CODE]
[OperationContract]
public List<Cliente> GetClienti()
{
return db.Clientes.ToList();
}
[/CODE]

Metodo SaveCliente(Cliente _entity)


L'entità “Cliente” che stiamo utilizzando è stata creata automaticamente quando abbiamo creato il
modello a oggetti con LinQ to Sql nello step precedente. All'interno del metodo “SaveCliente”
dobbiamo capire se si tratta di una nuova entità oppure se si tratta di modificarne una esistente e per
fare questo utilizziamo il valore presente nella proprietà “IDCliente”. Se è inferiore a 1 è una entità
che dobbiamo inserire altrimenti modificare. Queste operazioni le eseguiamo servendoci del
DataContext creato da LinQ to Sql.
Aggiungiamo ora il metodo SaveCliente che accetta come parametro una entità di tipo Cliente
[CODE]
[OperationContract]
public Cliente SaveCliente(Cliente _entity)
{
if (_entity == null)
{
throw new NullReferenceException("Cliente is null");
}
if (_entity.IDCliente < 1)
{
// Insert
db.Clientes.InsertOnSubmit(_entity);
}
else
{
// Update
Cliente original = (from c in db.Clientes
where c.IDCliente == _entity.IDCliente
select c).Single();
if (original == null)
{
throw new ArgumentException("Cliente not found");
}
original.RagioneSociale = _entity.RagioneSociale;
original.Citta = _entity.Citta;
original.Attivo = _entity.Attivo;
}
db.SubmitChanges();
return _entity;
}
[/CODE]

Come si può vedere, per rendere persistenti le operazioni di modifica dei dati è necessario chiamare
il metodo “SubmitChanges” sul DataContext. La funzione ritorna l'entità inserita o modificata per
dare la possibilità al client di modificare i dati in memoria.

Metodo DeleteCliente(Cliente _entity)


Per eliminare una entità, prima controlliamo che esista utilizzando una query LinQ. Se esiste
procediamo alla sua eliminazione chiamando il metodo “DeleteOnSubmit” a cui passeremo l'entità
da eliminare. Scriviamo il seguente metodo DeleteOnSubmit che ritorna l'entità eliminata per
consentire al client di modificare i suoi dati in memoria
[CODE]
[OperationContract]
public Cliente DeleteCliente(Cliente _entity)
{
var query = (from c in db.Clientes
where c.IDCliente == _entity.IDCliente
select c).SingleOrDefault();
if (query != null)
{
db.Clientes.DeleteOnSubmit(query);
db.SubmitChanges();
return query;
}
else
{
throw new ArgumentException("Cliente not found");
}
}
[/CODE]

Creare il layout dell'applicazione Silverlight


In questo step aggiungiamo i controlli che sono necessari per visualizzare l'elenco dei clienti e i
dettagli di ogni cliente selezionato. Inseriremo anche i vari pulsanti necessari per effettuare le
operazioni di modifica sui dati.
Questo non è un tutorial su come creare il layout di una applicazione Silverlight quindi daremo per
scontate alcune cose riguardo in particolare al linguaggio XAML utilizzato da Silverlight e non
andremo troppo per il sottile per quanto riguarda la bellezza dell'interfaccia che andremo a creare.
Nel progetto SilverlightApplication, facciamo doppio click sul file Page.xaml.
Notiamo che la pagina contiene una Grid, che è l'elemento di default creato quando si crea una
nuova pagina. Aggiungiamo due colonne alla Grid in modo da mettere a sinistra la ListBox e a
destra i controlli per visualizzare i dettagli.

<Image fileName='SilverlightDB_19.jpg' />

Ora, per separare la parte destra dalla parte sinistra, aggiungiamo un controllo “GridSplitter”.
Questo controllo lo aggiungiamo prendendolo dalla toolbox, in modo da farci aggiungere
automaticamente il namespace necessario per il suo utilizzo.

Se nella toolbox non ci sono controlli, possiamo visualizzarli in questo modo:


- Visualizziamo la toolbox cliccando sopra il relativo tab presente sulla sinistra della finestra di
Visual Studio (se non c'e' il tab selezioniamo dal menu principale “View” → “Toolbox”)
- Clicchiamo con il tasto destro sulla toolbox vuota e selezioniamo “Choose items”
- Selezioniamo il tab “Silverlight components” e dall'elenco selezioniamo “Grid Splitter”
- Ok al termine della selezione
Per aggiungere il controllo GridSplitter nel punto giusto, posizioniamo il cursore subito dopo la
definizione delle colonne e facciamo doppio click sul controllo GridSplitter presente nella toolbox.
Successivamente, per evidenziarlo meglio, aggiungiamo un valore (es. “Gainsboro”) alla proprietà
“Background”

<Image fileName='SilverlightDB_20.jpg' />

Aggiungiamo ora una ListBox posizionando il cursore subito dopo il precedente controllo e
inserendo il seguente codice Xaml

[CODE]
<ListBox x:Name="lbClienti" Grid.Column="0" Margin="15" />
[/CODE]

Impostando l'attributo Grid.Column=”0” della ListBox facciamo in modo che venga visualizzata
nella prima colonna a sinistra dello splitter.

Ora aggiungiamo i controlli per visualizzare i dettagli.


Utilizzeremo come container dei controlli uno StackPanel. Utilizziamo come label dei controlli di
tipo TextBlock, per visualizzare i dati delle TextBox e un CheckBox, per le operazioni di modifica
dei normali Button.
Aggiungiamo quindi il seguente codice Xaml subito dopo il controllo ListBox lbClienti

[CODE]
<StackPanel x:Name="spDettagli" Grid.Column="1" Margin="15">
<TextBlock>Ragione Sociale</TextBlock>
<TextBox x:Name="txtRagioneSociale" />
<TextBlock>Citta</TextBlock>
<TextBox x:Name="txtCitta" />
<TextBlock>Attivo</TextBlock>
<CheckBox x:Name="chkAttivo" />
<Button x:Name="btnSalva" Content="Salva" Margin="2" />
<Button x:Name="btnNuovo" Content="Nuovo" Margin="2" />
<Button x:Name="btnElimina" Content="Elimina" Margin="2" />
</StackPanel>
[/CODE]

<Image fileName='SilverlightDB_21.jpg' />

Il Databind: associare i controlli con i dati

Siamo arrivati al collegamento tra i controlli che sono nella pagine della nostra
SilverlightApplication, lato client, e i dati che ci verranno restituiti dal Web Service che abbiamo
appena creato.
Per poterci collegare dalla SilverlightApplication al Web Service di nome NegozioService,
dobbiamo aggiungere una Service Reference al progetto.
Clicchiamo con il tasto destro sulla cartella “Service Reference” presente nel progetto
SilverlightApplication e selezioniamo “Add Service Reference”
<Image fileName='SilverlightDB_22.jpg' />

Nella finestra che ci compare subito dopo dobbiamo individuare il servizio. E' facile farlo quando il
Web Service è presente nella stessa solution perchè abbiamo a disposizione un comodo pulsante
“Discover” che se selezionato ci visualizza tutti i servizi presenti negli altri progetti.

<Image fileName='SilverlightDB_23.jpg' />

Selezioniamo l'unico servizio visualizzato e diamogli il nome “NegozioServiceReference”

<Image fileName='SilverlightDB_24.jpg' />

Dopo aver confermato con “Ok”, Visual Studio ci crea una speciale classe definita anche proxy che
ci permette di interrogare il Web Service come se fosse un oggetto locale.
Se non è già aperto facciamo doppio click sul file Page.xaml e premiamo il pulsante F7 per
visualizzare la pagina di codice (code behind).
Inseriamo preventivamente in alto prima della dichiarazione della classe uno using

[CODE]
using SilverlightApplication.NegozioServiceReference;
[/CODE]

Dichiariamo all'interno della classe Page, una variabile di tipo


System.Collection.ObjectModel.ObservableCollection chiamata “elencoClienti”. Questa variabile
ci sarà utile per memorizzare l'elenco di tutti i clienti ed essendo di tipo ObservableCollection
notificherà automaticamente ai controlli Silverlight tutti gli eventi di modifica degli elementi che
contiene.

[CODE]
System.Collections.ObjectModel.ObservableCollection<Cliente> elencoClienti;
[/CODE]

Dichiariamo ora una variabile di tipo “NegozioServiceClient” (il proxy creato automaticamente) e
chiamiamola “serviceClient”

<Image fileName='SilverlightDB_25.jpg' />

La variabile serviceClient è dichiarata ma non ancora inizializzata. Dove la possiamo inizializzare?


Potremmo farlo nel costruttore, ma visto che esiste un comodo evento che viene sempre scatenato
nella pagina Silverlight al termine del caricamento iniziale, impariamo ad utilizzarlo scrivendo il
seguente gestore di evento (event handler). Da tenere presente che VisualStudio ci aiuta a scrivere i
gestori di evento se dopo aver digitato i segni += premiamo consecutivamente per due volte il tasto
Tab
[CODE]
public Page()
{
InitializeComponent();
Loaded += new RoutedEventHandler(Page_Loaded);
}
[/CODE]

e nel metodo “Page_Loaded” possiamo procedere a inizializzare il nostro oggetto “serviceClient”

[CODE]
void Page_Loaded(object sender, RoutedEventArgs e)
{
serviceClient = new NegozioServiceClient();
}
[/CODE]

L'oggetto serviceClient che abbiamo inizializzato è in grado di richiamare il Web Service remoto e
farci restituire l'elenco dei clienti. Dobbiamo quindi ricevere la collection con questo elenco di
entità di tipo Cliente e associarla alla ListBox.
La prima riga di codice da scrivere associa un gestore di evento che verrà richiamato al termine del
caricamento della collection con l'elenco dei clienti. La seconda riga “inizia” la chiamata vera e
propria. Questo pattern asincrono è reso abbastanza semplice perchè per ogni metodo presente nel
proxy contiene sia l'evento XXXCompleted a cui registrare un gestore e sia il metodo XXXAsync()
che serve per far partire la chiamata al servizio.

[CODE]
void Page_Loaded(object sender, RoutedEventArgs e)
{
serviceClient = new NegozioServiceClient();
serviceClient.GetClientiCompleted += new
EventHandler<GetClientiCompletedEventArgs>(serviceClient_GetClientiCompleted);
serviceClient.GetClientiAsync();
}
[/CODE]

Dopo tutto questo parlare, la collection con dentro l'elenco di Clienti dovè?
Naturalmente ce la ritroveremo nel metodo “serviceClient_GetClientiCompleted” che è il gestore
di evento che verrà eseguito al termine del caricamento della collection.
Andiamo quindi a scrivere in questo metodo il collegamento tra la collection e il controllo ListBox

[CODE]
void serviceClient_GetClientiCompleted(object sender, GetClientiCompletedEventArgs e)
{
elencoClienti = e.Result;
lbClienti.ItemsSource = elencoClienti;
}
[/CODE]

Controlli come la ListBox o la DataGrid hanno lo scopo di visualizzare un elenco di informazioni


che possono essere contenute in una collection di elementi, come nel nostro tutorial, oppure in un
documento Xml oppure in una Datatable di Ado.Net. Questi controlli espongono la proprietà
“ItemsSource” a cui collegare direttamente la fonte dati.

Ora dobbiamo indicare la proprietà “RagioneSociale” presente nella classe Cliente come il membro
da visualizzare nella ListBox utilizzando la proprietà “DisplayMemberPath”. Spostiamoci nel file
Page.xaml e modifichiamo il tag della ListBox lbClienti in questo modo
[CODE]
<ListBox x:Name="lbClienti" Grid.Column="0" Margin="15"
DisplayMemberPath="RagioneSociale" />
[/CODE]

Eseguiamo l'applicazione premendo il pulsante F5 o l'apposita icona nella toolbox in alto e vediamo
all'opera quanto finora fatto. Premiamo Ok se viene richiesta la modifica del web.config.
La prima esecuzione può risultare lenta ma dopo qualche secondo vedrete apparire l'elenco dei
clienti nella ListBox.

<Image fileName='SilverlightDB_26.jpg' />

Ora dobbiamo associare ai controlli presenti nello StackPanel i dati contenuti nel cliente selezionato
con la ListBox lbClienti. Per farlo, dobbiamo dichiarare nel codice Xaml il nome del gestore che si
occuperà dell'evento “SelectionChanged” della ListBox. Facciamo la seguente modifica al codice
che definisce la ListBox nel file Page.xaml

<Image fileName='SilverlightDB_27.jpg' />

Quando scriviamo direttamente il codice Xaml, l'intellisense di Visual Studio ci aiuta. E, nel caso
della definizione degli eventi e dell'associazione tra un evento e un gestore evento, ci permette di
creare velocemente il metodo confermando i suggerimenti automatici.

Cosa dobbiamo fare nel metodo “lbClienti_SelectionChanged” che abbiamo appena creato?
Dobbiamo associare l'elemento selezionato nella ListBox alla proprietà DataContext dello
StackPanel. Certo, avremmo potuto associarlo ad ogni singolo controllo dentro lo StackPanel ma è
molto più semplice associarlo ad un Container sapendo che così la proprietà verrà propagata
automaticamente ai controlli in esso contenuti. Spostiamoci nella pagina del codice ed ecco il
codice con cui effettuare l'associazione
[CODE]
private void lbClienti_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
spDettagli.DataContext = lbClienti.SelectedItem;
}
[/CODE]

Come vedete, l'item selezionata nella ListBox viene associata alla proprietà DataContext dello
StackPanel e quindi automaticamente propagata al DataContext di tutti i controlli. Un item
selezionato nella ListBox a cosa corrisponde? Ad un oggetto di tipo Cliente. Come sappiamo, la
classe Cliente contiene alcune proprietà come “RagioneSociale” “Citta” “Attivo”. Come facciamo a
stabilire che la proprietà Text della TextBox txtRagioneSociale deve visualizzare la proprietà
“RagioneSociale” dell'oggetto Cliente selezionato?
Con la seguente sintassi:

[CODE]
<TextBox x:Name="txtRagioneSociale" Text="{Binding RagioneSociale}" />
[/CODE]

Come vedete, la proprietà che possiamo definire target di nome “Text” contiene il collegamento alla
sua fonte dati che in questo caso è il nome di una proprietà dell'oggetto impostato nel DataContext.
Procediamo allo stesso modo con il resto dei controlli prestando attenzione al controllo CheckBox
la cui proprietà target è “IsChecked”
Ecco il codice Xaml come deve apparire

<Image fileName='SilverlightDB_28.jpg' />

Eseguiamo l'applicazione premendo F5 o l'apposita icona nella toolbox in alto e notiamo come la
selezione di un item sulla ListBox provochi correttamente l'associazione con i controlli nello
StackPanel. Proviamo a modificare il testo di uno dei clienti selezionati. Cambiando selezione e
ritornando su di esso possiamo notare che le modifiche vengono perse. Per evitare questo e quindi
per riuscire a mantenere negli oggetti associati le modifiche fatte attraverso i controlli dobbiamo
cambiare valore all'attributo “Mode” del Binding inserendo il valore “TwoWay” nel seguente modo

<Image fileName='SilverlightDB_29.jpg' />


Se ora riproviamo ad eseguire l'applicazione e a cambiare i dati vedremo come i cambiamenti non
solo vengono mantenuti ma si propagano anche alla collection collegata con la ListBox.

Questo perché la classe Cliente che ci è stata creata automaticamente da LinQ to Sql implementa
l'interfacci INotifyPropertyChanged ed anche la collection di oggetti di tipo Cliente che viene
restituita dal Web Service è di tipo “System.Collections.ObjectModel.ObservableCollection” e
quindi è in grado di gestire le modifiche agli elementi contenuti notificandole ai controlli.

Aggiungiamo adesso i gestori di evento ai pulsanti

<Image fileName='SilverlightDB_30.jpg' />

Iniziamo con il definire il contenuto del metodo btnNuovo_Click

[CODE]
private void btnNuovo_Click(object sender, RoutedEventArgs e)
{
Cliente nuovo = new Cliente();
nuovo.RagioneSociale = "Inserisci dati";
nuovo.Citta = string.Empty;
spDettagli.DataContext = nuovo;
}
[/CODE]

Come possiamo vedere, dopo aver creato una nuova istanza di tipo Cliente che valorizziamo con dei
dati di default associamo l'oggetto appena creato, e non ancora contenuto nel database, alla
proprietà DataContext dello StackPanel. In questo modo indipendentemente da quello che è stato
prima selezionato nella ListBox, i dati nei controlli contenuti nello StackPanel cambiano e vengono
collegati al nuovo oggetto. Eseguiamo l'applicazione e controlliamo questo comportamento

<Image fileName='SilverlightDB_31.jpg' />


Ora implementiamo il codice del metodo btnSalva_Click. In questo metodo dobbiamo chiamare in
maniera asicrona il metodo SalvaClienteAsync presente nel Web Service. A questo metodo
dobbiamo passare l'oggetto correntemente associato al DataContext dello StackPanel e possiamo
registrarci per ricevere il segnale della fine dell'operazione lato server in modo da modificare
l'elenco dei clienti che teniamo memorizzato nella variabile “elencoClienti”.
[CODE]
private void btnSalva_Click(object sender, RoutedEventArgs e)
{
serviceClient.SaveClienteCompleted +=
new
EventHandler<SaveClienteCompletedEventArgs>(serviceClient_SaveClienteCompleted);
serviceClient.SaveClienteAsync(spDettagli.DataContext as Cliente);
}

void serviceClient_SaveClienteCompleted(object sender, SaveClienteCompletedEventArgs e)


{
if (e.Error != null)
{
// C'e' stato un errore visualizziamo il messaggio
MessageBox.Show(e.Error.Message);
}
else
{
// Se si tratta di un nuovo elemento aggiungiamolo all'elenco
if (elencoClienti.FirstOrDefault(c => c.IDCliente == e.Result.IDCliente) == null)
{
elencoClienti.Add(e.Result);
}
}
}
[/CODE]

Ora implementiamo il metodo btnElimina_Click in maniera simile

[CODE]
private void btnElimina_Click(object sender, RoutedEventArgs e)
{
serviceClient.DeleteClienteCompleted +=
new
EventHandler<DeleteClienteCompletedEventArgs>(serviceClient_DeleteClienteCompleted);
serviceClient.DeleteClienteAsync(spDettagli.DataContext as Cliente);
}

void serviceClient_DeleteClienteCompleted(object sender, DeleteClienteCompletedEventArgs


e)
{
if (e.Error != null)
MessageBox.Show(e.Error.Message);
else
{
// Cancelliamo il datacontext dello StackPanel e rimuoviamo l'elemento dall'elenco
spDettagli.DataContext = null;
elencoClienti.Remove(elencoClienti.FirstOrDefault(c => c.IDCliente ==
e.Result.IDCliente));
}
}
[/CODE]
Conclusioni

Quanto detto in questo articolo ha come obiettivo quello di prendere familiarità con il databind di
Silverlight. Quanto appreso può poi essere facilmente riutilizzato anche in progetti che utilizzano il
fratello maggiore di Silverlight che è Window Presentation Foundation. Ci sono modi alternativi per
fare le stesse cose qui illustrate.
In taluni casi potrebbe essere auspicabile una maggiore separazione tra il codice Xaml e il codice di
programmazione spostando ad es. il collegamento con gli eventi e le informazioni di Databinding
nel codice in modo da rendere più facilmente modificabile l'interfaccia utente anche da non
programmatori che possono utilizzare Microsoft Expression Blend.
Ve detto poi che i controlli Silverlight consentono di gestire facilmente la validazione dei dati
inseriti sia utilizzando dei Converter custom o presenti nel Framework associati o anche facendo
affidamento sulle proprietà degli oggetti stessi che possono generare eccezioni. In entrambi i casi,
gli oggetti espongono dei comodi eventi per controllare gli errori di validazione ed evidenziare ad
esempio i dati.
E' altresì utile approfondire l'utilizzo di User Controls per un migliore riutilizzo del codice e dei
DataTemplate per strutturare meglio l'associazione dati con i controlli.

Potrebbero piacerti anche