Nessun risultato. Prova con un altro termine.
Guide
Notizie
Software
Tutorial

La gestione dei dati in Silverlight 2: Binding e dintorni

Stabilire le associazioni tra dati e controlli Silverlight
Stabilire le associazioni tra dati e controlli Silverlight
Link copiato negli appunti

L'associazione tra controlli dell'interfaccia utente e i dati (contenuti in una Collection, in un documento Xml o in una Datatable) avviene utilizzando l'oggetto Binding.

Grazie a questo oggetto possiamo specificare il "target", ovvero il destinatario dell'associazione (es. la proprietà Text di una TextBox), e la sorgente dei dati (es. la RagioneSociale di un oggetto di tipo Cliente).

Possiamo dichiarare l'oggetto Binding direttamente nel markup XAML, assegnando i dati alla proprietà desiderata.

Ecco l'esempio di una TextBox associata ad un campo RagioneSociale:

<TextBox x:Name="txtRagioneSociale" 
         Text="{Binding Path=RagioneSociale}" />

Le proprietà più interessanti dell'oggetto Binding sono:

Proprietà Descrizione
Path è un parametro di default e non è obbligatorio specificarlo. Indica il nome della proprietà che contiene il dato nella sorgente dati. L'esempio precedente può essere anche scritto: {Binding RagioneSociale}
Mode può assumere due possibili valori OneWay e TwoWay (di default OneWay).
Se vogliamo che le modifiche apportate nel controllo vengano propagate all'oggetto che contiene i dati è necessario specificare TwoWay:
{Binding RagioneSociale, Mode=TwoWay}
Source è opzionale e consente di specificare la sorgente dati, direttamente nell'oggetto Binding

Possiamo anche scegliere di gestire l'associazione dati nel code-behind:

Binding b = new Binding();
b.Source = ((Cliente)lbClienti.SelectedItem).RagioneSociale;
txtRagioneSociale.SetBinding(TextBox.TextProperty, b);

Associare controlli ed entità

Tipicamente, quando parliamo di Data Layer, intendiamo per "entità" un oggetto che contiene i dati, ad esempio un oggetto di tipo Cliente contenente una serie di proprietà.

Per associare uno o più controlli a questa entità, possiamo utilizzare la classe DataContext. Essa è esposta dai controlli Silverlight e serve per impostare la sorgente dati. Vediamo come impostarla via codice.

Supponiamo di avere una ListBox chiamata "lbClienti" che contiene un elenco di oggetti di tipo Cliente e una TextBox chiamata "txtRagioneSociale". Ecco come collegare al DataContext della TextBox l'oggetto selezionato nella ListBox:

txtRagioneSociale.DataContext = lbClienti.SelectedItem as Cliente;

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.

Ecco l'esempio di un container di tipo StackPanel il cui DataContext sarà propagato a tutti i suoi controlli Child.

Markup

<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>

Codice

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;            
}

È utile tenere in considerazione anche che la nostra entità (la classe Cliente con i dati), 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:

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
}

Collezioni di oggetti ed elenchi

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.

Perché 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 presente nel Framework e che implementa già IEnumerable e INotifyCollectionChanged.

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.

In questa seconda parte dell'articolo iniziamo un semplice tutorial: faremo visualizzare in una ListBox un elenco di clienti e i dettagli di ogni cliente selezionato. Inoltre vedremo come implementare la modifica, eliminazione e inserimento dei dati.

Figura 1. Schema dell'applicazione
Schema dell'applicazione

L'architettura dell'applicazione prevede da una parte la gestione dei dati e dall'altra l'applicazione Silverlight, ospitata in una pagina HTML, tramite plug-in.

Tra i due estremi troviamo alcuni livelli applicativi necessari a far comunicare il client con il database oltre che utili per organizzare e rendere riusabile l'architettura. L'applicazione Silverlight non può accedere direttamente ai dati se questi sono contenuti lato server, è quindi necessario scrivere uno o più metodi che interagiscono con la base dati ed esporre questi metodi attraverso un Web Service.

Per l'accesso ai dati ci serviamo di LinQ to Sql, che ci consente di realizzare con poco sforzo la gestione di un database SqlServer. Se volessimo utilizzare un database diverso da SqlServer ci potremmo servire di LinQ to Entity.

Per il nostro esempio utilizziamo Visual Web Developer Express 2008, creiamo un nuovo progetto Silverlight utilizzando il template che prevede la creazione di una applicazione Web e di un progetto silverlight separati.

Figura 2. Creare l'applicazione Silverlight
Creare l'applicazione Silverlight

Nota: Se non troviamo il template, probabilmente non abbiamo ancora scaricato i Silverlight Tools per Visual Studio.

I prossimi passi saranno:

  1. Creazione del database e della tabella Cliente
  2. Impostare la logica di accesso ai dati con LinQ to Sql
  3. Realizzare un Web Service che si interfacci con i dati
  4. Creare il layout dell'applicazione
  5. Il Databind: associare i controlli con i dati e gestire le operazioni CRUD

Creare il database

Aggiungiamo al progetto Web un nuovo DB SqlServer Express cliccando con il tasto destro sul progetto SilverlightApplication.Web e cliccando su Aggiungi > Nuovo elemento.

Figura 3. Aggiungere un nuovo elemento
Aggiungere un nuovo elemento

Scegliamo Database SQL e chiamiamo il nuovo database "NegozioDB.mdf"

Figura 4. Creare un database
Creare un database

Rispondiamo "Si" per spostare il file nella cartella AppData.

Una volta creato il database dobbiamo creare la tabella Clienti. Da Esplora Database (linguetta vicina a "Esplora Soluzioni" o Visualizza > Esplora Database) espandiamo la voce NegozioDB, clicchiamo con il tasto destro sulla cartella Tabelle e selezioniamo la voce Aggiungi nuova tabella.

Figura 5. Nuova tabella
Nuova tabella

Aggiungiamo le seguenti colonne:

Nome colonna Tipo di dati Ammetti Null
IDCliente int no
RagioneSociale nvarchar(150) no
Citta nvarchar(50) no
Attivo bit no

Impostiamo la colonna "IDCliente" come Primary Key

Figura 6. Chiave primaria
Chiave primaria

Perché questa chiave sia generata automaticamente dal sistema, impostiamo la proprietà Identità (IsIdentity) a . Salviamo la tabella con il nome "Cliente".

Figura 7. Impostare il campo come identità
Impostare il campo come identità

Ora aggiungiamo qualche record alla tabella. Clicchiamo con il tasto destro su Client, in Esplora Database e selezioniamo Mostra dati tabella.

Accesso ai dati con LinQ to Sql

Come abbiamo detto, il database risiede fisicamente sul server mentre l'applicazione Silverlight è eseguita sul client.

Per trasferire i dati dal database all'applicazione Silverlight utilizziamo delle entità. Una entità è rappresentata da una classe che contiene i dati prelevati dal database, ne memorizza i campi, e li espone sotto forma di proprietà. L'entità non contiene logica di accesso al database e svolge una funzione di trasporto.

Per gestire i dati come oggetti e garantirne la persistenza sul database esistono da tempo dei progetti detti ORM, tra cui il notissimo NHibernate.

Dalla versione 3.5 del .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. In questo articolo utilizzeremo LinQ to Sql per creare un modello a oggetti e un motore di persistenza con pochi click.

Per creare il modello a oggetti possiamo utilizzare l'apposito designer: aggiungiamo un elemento al progetto SilverlightApplication.Web, selezioniamo il template Classi LinQ to Sql e diamogli il nome NegozioClasses.dbml.

Figura 8. Creare una classe LinQ
Creare una classe LinQ

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

Figura 9. Creazione dell'entità dalla tabella
Creazione dell'entità dalla tabella

Questa entità dovrà trasportare i dati in entrambi i sensi tra server e client, perciò dobbiamo renderla serializzabile. Clicchiamo su una zona libera del designer e cambiamo la proprietà Serialization Mode in Unidirectional.

Figura 10. Serializzazione
Serializzazione

Il nostro un modello a oggetti e il relativo motore di persistenza sono pronti. Nel prosieguo del tutorial vedremo come utilizzare la classe Cliente e la classe DataContext (NegozioDBClasseDataContext per la precisione).

Iniziamo questa terza parte dell'articolo creando il servizio Web che espone metodi per leggere e scrivere dati.

Creare il Web Service

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 Service servono per far parlare tra loro le applicazioni e tipicamente non vengono usati in modo diretto dall'utente.

Ai fini del nostro tutorial, creiamo un Web Service dentro l'applicazione Web, che esporrà alla nostra applicazione Silverlight i metodi per leggere e scrivere nel database: GetClienti, SaveCliente, DeleteCliente.

Clicchiamo con il tasto destro sulla applicazione Web (SilverlightApplication.Web), aggiungiamo un nuovo elemento Servizio WCF Abilitato per Silverlight e chiamiamo il Web Service NegozioService.svc

Figura 11. Creare un Servizio WCF
Creare un Servizio WCF

All'interno della classe NegozioService, dichiariamo una variabile db di tipo NegozioClassesDataContext e inizializziamola nel costruttore della classe

public class NegozioService
{
  NegozioClassesDataContext db;

  public NegozioService()
  {
    db = new NegozioClassesDataContext();
  }
}

Questa variabile rappresenta il nostro motore di persistenza creato automaticamente da LinQ to Sql e ci sarà utile per gestire il database.

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

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

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

[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;
}

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.

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 metodo DeleteOnSubmit che ritorna l'entità eliminata per consentire al client di modificare i suoi dati in memoria:

[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"); }
}

Creare il layout

In questo step aggiungiamo i controlli necessari per visualizzare l'elenco dei clienti e il dettaglio di ogni cliente selezionato. Inseriamo anche i pulsanti necessari per effettuare le operazioni di modifica sui dati.

Non ci soffermiamo troppo sulla creazione del layout di una applicazione Silverlight, cerchiamo qui di creare il minimo indispensabile al funzionamento dell'applicazione.

Dal progetto SilverlightApplication, apriamo il file Page.xaml. 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.

Per separare le due parti, aggiungiamo un controllo GridSplitter. Questo controllo lo aggiungiamo prendendolo dalla toolbox, in modo da farci aggiungere automaticamente il namespace necessario per il suo utilizzo.

Per inserire la barra nel punto giusto, posizioniamo il cursore subito dopo la definizione delle colonne e facciamo doppio click sul controllo GridSplitter presente nella toolbox. Per evidenziare meglio la divisione, aggiungiamo un valore (es. "Gainsboro") alla proprietà Background.

Figura 12. Grid Splitter
Grid Splitter

Aggiungiamo ora una ListBox inserendo il seguente codice XAML

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

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

<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>
Figura 13. Interfaccia
Interfaccia

Il Databind: associare i controlli con i dati

Siamo arrivati finalmente al collegamento tra i controlli che sono nell'applicazione Silverlight e i dati restituiti dal Web Service. Per collegare la SilverlightApplication al nostro NegozioService, dobbiamo aggiungere una Riferimento al servizio nel progetto.

Clicchiamo con il tasto destro sulla cartella Riferimenti presente nel progetto SilverlightApplication e selezioniamo "Aggiungi riferimento al servizio".

Figura 14. Interfaccia
Interfaccia

Nella finestra che appare possiamo individuare il servizio. È facile farlo quando il Web Service è presente nella stessa solution perchè abbiamo a disposizione il pulsante Individua che, se selezionato, ci mostra i servizi presenti negli altri progetti.

Selezioniamo il servizio visualizzato e diamogli il nome NegozioServiceReference.

Figura 15. Individuare il servizio Web
Individuare il servizio Web

Dopo aver confermato con Ok, viene creata una classe proxy che ci permette di interrogare il Web Service come se fosse un oggetto locale.

Torniamo al file Page.xaml e premiamo F7 per visualizzare la pagina di codice (code behind).
Qui dichiariamo di voler utilizzare il servizio attraverso la classe proxy. Inseriamo quindi una clausola using:

using SilverlightApplication.NegozioServiceReference;

All'interno della classe Page, dichiariamo una variabile di tipo 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.

System.Collections.ObjectModel.ObservableCollection<Cliente> elencoClienti;

Dichiariamo ora una variabile di tipo NegozioServiceClient (il proxy creato automaticamente) e chiamiamola serviceClient. Questa variabile è dichiarata ma non ancora inizializzata. Potremmo farlo nel costruttore, ma esiste un evento che viene sempre scatenato nella pagina Silverlight al termine del caricamento iniziale, impariamo ad utilizzarlo e inizializziamo il proxy nel metodo gestore dell'evento.

Nota: Visual Studio ci aiuta a scrivere i gestori di evento, basta premere consecutivamente per due volte il tasto Tab dopo aver digitato l'operatore +=

public partial class Page : UserControl
{
  NegozioServiceClient serviceClient;
  ObservableCollection elencoClienti;
  
  public Page()
  {
    InitializeComponent();
    Loaded += new RoutedEventHandler(Page_Loaded);
  }
}

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

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

Ora l'oggetto serviceClient è in grado di richiamare il Web Service e farci restituire l'elenco dei clienti. Dobbiamo quindi ricevere la collection con questo elenco di entità di tipo Cliente e associarla alla ListBox.

Quello che faccaimo ora è assegnare un gestore di evento da richiamare al termine del caricamento dell'elenco dei clienti. Poi lanciamo la chiamata vera e propria.

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

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.

Infine la collection con l'elenco dei Clienti sarà ritornata nel metodo serviceClient_GetClientiCompleted, che gestisce l'evento del caricamento completato. È qui che possiamo stabilire il collegamento tra la collection e il controllo ListBox.

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

Controlli come ListBox o 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 o 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

<ListBox x:Name="lbClienti" Grid.Column="0" Margin="15"
         DisplayMemberPath="RagioneSociale" />

Lanciamo l'applicazione premendo il pulsante F5 e vediamo all'opera quanto fatto. Premiamo Ok se viene richiesta la modifica del web.config.

La prima esecuzione può risultare lenta ma dopo qualche secondo vedremo apparire l'elenco dei clienti nella ListBox.

Figura 16. Prima esecuzione
Prima esecuzione

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.

<ListBox x:Name="lbClienti" Grid.Column="0" Margin="15"
         DisplayMemberPath="RagioneSociale"
         SelectionChanged="lbClienti_SelectionChanged" />

Quando scriviamo direttamente il codice XAML, l'intellisense di Visual Studio ci aiuta. 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.

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 aggiungiamo:

private void lbClienti_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
  spDettagli.DataContext = lbClienti.SelectedItem;
}

L'elemento selezionato nella ListBox viene associato alla proprietà DataContext dello StackPanel e quindi automaticamente propagato al DataContext di tutti i controlli.

Ogni elemento nella ListBox corrisponde ad un oggetto di tipo Cliente, la classe Cliente contiene proprietà come RagioneSociale, Citta, Attivo. Per stabilire che la proprietà Text della txtRagioneSociale deve visualizzare la proprietà RagioneSociale dell'oggetto Cliente selezionato utilizziamo finalmente il Binding!

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

La proprietà Text (detta proprietà "target"), 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.

<TextBox x:Name="txtRagioneSociale" Text="{Binding RagioneSociale}" />
...
<TextBox x:Name="txtCitta" Text="{Binding Citta}" />
...
<CheckBox x:Name="chkAttivo" IsChecked="{Binding Attivo}" />

Eseguiamo l'applicazione e verifichiamo che la selezione di un elemento sulla ListBox provochi l'associazione con i controlli nello StackPanel.

Proviamo a modificare il testo di uno dei clienti selezionati. Cambiando selezione e ritornando su di esso vediamo che le modifiche vanno perse. Per mantenere negli oggetti associati le modifiche fatte attraverso i controlli dobbiamo cambiare valore all'attributo Mode del Binding inserendo il valore TwoWay.

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

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

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

Aggiungiamo i gestori di evento ai pulsanti

Figura 16. Aggiungere i gestori ai pulsanti
Aggiungere i gestori ai pulsanti

e iniziamo a definire il comportamento del bottone Nuovo:

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

Dopo aver creato una nuova istanza di Cliente, valorizzata con 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.

Ora implementiamo il codice del metodo btnSalva_Click. Qui 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.

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'è 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);
    }
  }
}

Ora implementiamo il metodo btnElimina_Click in maniera simile

private void btnElimina_Click(object sender, RoutedEventArgs e)
{
  serviceClient.DeleteClienteCompleted += new EventHandler(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));
  }
}      
Riccardo Di Nuzzo è Microsoft Certified Trainer (MCSD, MCPD EA, MCDBA, MCTS). Svolge attività di trainer Microsoft presso Overnet Education. Il suo sito Web personale www.dinuzzo.it.

Ti consigliamo anche