Silverlight 3: Navigation Framework
a cura di Antonio Catucci (requisiti: conoscenza di Silverlight 2)

Introduzione
L’attuale beta di Silverlight 3 porta con sé diverse importanti novità. Una delle più interessanti, che manca nell’attuale versione 2.0, è il supporto per la navigazione tra pagine nella stessa applicazione, noto come Navigation Framework.

Questo Framework mette a disposizione tutto il meccanismo di base per consentire la realizzazione e la gestione di applicazioni Silverlight basate su più UserControl caricabili dinamicamente e, cosa molto importante, con il pieno supporto dell’history, che permette di spostarsi avanti e indietro nell’applicazione, proprio come accade nelle applicazioni web. Non solo: la navigazione si riflette sull’url dell’applicazione aggiungendo il path della pagina corrente facilitando, così, l’indicizzazione nei motori di ricerca dei contenuti dell’applicazione .

Come funziona
L’idea alla base di questo meccanismo è avere un controllo Frame all’interno del quale caricare i controlli dell’applicazione. Il tutto si basa su due nuovi controlli:

Tutto il meccanismo di navigazione è gestito, invece, da queste classi:

Tutte queste classi sono racchiuse in un assembly specifico chiamato System.Windows.Controls.Navigation.dll, che è quindi necessario includere nel file XAP finale da distribuire all’utente.

Ma vediamo un esempio pratico su come realizzare applicazioni multi-pagina.

Un esempio
Prima di cominciare bisogna avere installato sul pc di sviluppo il recente Silverlight Tools for Visual Studio 2008 SP1, che potete utilizzare anche per le versioni Express. Questo pacchetto contiene tutto l’occorrente per sviluppare applicazioni per Silverlight 3 Beta, ovvero il runtime, l’SDK, i template per Visual Studio. Trattandosi di una beta vale la solita raccomandazione di installare il tutto su un pc dedicato (o meglio su una macchina virtuale) soprattutto considerando che dopo l’installazione non è più possibile sviluppare applicazioni per Silverlight 2.

Per il nostro esempio usiamo Microsoft Visual Web Developer 2008.
Creiamo un nuovo progetto Silverlight basato sul template Silverlight Application che chiameremo SampleNavigation:

Come si vede dalla figura precedente, con Silverlight 3 si ha a disposizione anche un nuovo template, Silverlight Navigation Application, che crea l’infrastruttura di base per creare un’applicazione del genere. Noi non useremo questo template, in quanto comprende cose che non ci interessano in questo articolo, ma potete sempre darci un’occhiata.

Notiamo che, a differenza di quanto avviene con Silverlight 2, il controllo di default si chiama MainPage.xaml e non più Page.xaml.

La prima cosa da fare è aggiungere il riferimento all’assembly System.Windows.Controls.Navigation.dll e aggiungere il namespace nello xaml del MainPage:

<UserControl x:Class="SampleNavigation.MainPage"   
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"     
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
  xmlns:navigation="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Navigation"
  HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
  <Grid x:Name="LayoutRoot" Background="Gray">

  </Grid>
</UserControl>

Successivamente creiamo le pagine (Page) che comporranno la nostra applicazione. Per comodità creiamo una cartella Pages nella quale inseriremo le nostre pagine aggiungendo un nuovo item di tipo Silverlight Page (anche questo è un nuovo template di Silverlight 3) con il nome Home.xaml:

Lo xaml della pagina è il seguente:

<navigation:Page x:Class="SampleNavigation.Home" 
   xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
   xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
   xmlns:navigation="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Navigation"
   Title="Home Page">
  <Grid x:Name="LayoutRoot" Background="White">

  </Grid>
</navigation:Page>

Visual Studio aggiunge automaticamente il riferimento al namespace System.Windows.Controls.Navigation con il prefisso navigation. Ripetiamo questa operazione aggiungendo altre due pagine chiamate Page1.xaml e Page2.xaml.

Ora non ci resta che aggiungere il Frame che conterrà le pagine caricate dinamicamente nel controllo MainPage.xaml. Il codice xaml è il seguente:

          <navigation:Frame x:Name="NavFrame" Background="White"
                          VerticalAlignment="Stretch"
                          HorizontalAlignment="Stretch"
                          Padding="20"/>

Nell’esempio allegato all'articolo il Frame è posizionato in una griglia con due colonne dove nella colonna di sinistra avremo il menu per la navigazione mentre nella seconda il Frame.

Per simulare la navigazione creiamo 3 button:

        <Button Height="35" Content="Home"></Button>
        <Button Height="35" Content="Page 1"></Button>
        <Button Height="35" Content="Page 2"></Button>

Per rendere la gestione della navigazione più generica possibile sfruttiamo la proprietà Tag del Button per specificare la pagina da associare a ciascun pulsante così da utilizzare un unico gestore d’evento per il click:

            <Button Height="35" Tag="/Pages/Home.xaml" 
                    Click="NavigationButton_Click"  Content="Home"></Button>
            <Button Height="35" Tag="/Pages/Page1.xaml" 
                    Click="NavigationButton_Click"  Content="Page 1"></Button>
            <Button Height="35" Tag="/Pages/Page2.xaml" 
                    Click="NavigationButton_Click"  Content="Page 2"></Button>

Il codice per l’evento NavigationButton_Click è il seguente:

  Private Sub NavigationButton_Click(ByVal sender As System.Object, _
                                     ByVal e As System.Windows.RoutedEventArgs)
    ' Recupera la url di destinazione dalla proprietà Tag del Sender
    Dim TargetUrl As String = DirectCast(sender, Button).Tag.ToString
    NavFrame.Navigate(New Uri(TargetUrl, UriKind.Relative))
  End Sub

Come si vede il tutto si riduce a chiamare il metodo Navigate della classe Frame passando come parametro l’Url relativo della pagina da caricare, che, nel nostro esempio, viene recuperata dalla proprietà Tag del button che ha scatenato l’evento (Sender).

Da notare che di default l’oggetto Frame non visualizza alcunché, ma è possibile impostare una pagina iniziale impostando la proprietà Source. Noi impostiamo la pagina iniziale su Home.xaml:

        <navigation:Frame x:Name="NavFrame"  Background="White"
                          VerticalAlignment="Stretch"
                          HorizontalAlignment="Stretch"
                          Source="/Pages/Home.xaml"
                          Padding="20"/>

Eseguendo l’applicazione il risultato è questo:

Cambiando pagina è possibile notare come l’url del browser cambi, visualizzando alla fine il path relativo della pagina caricata separato da #. Oltre alla url è possibile specificare anche il titolo della finestra del browser impostando la proprietà Title della Page :

Infine si può facilmente verificare anche il supporto all’history, semplicemente tornando indietro con il tasto 'back' del browser.

Passaggio di parametri
Come accennato all’inizio dell’articolo, quando si carica una pagina è possibile passare dei parametri in un modo del tutto analogo a quanto avviene con le pagine web, cioè in querystring, in questo modo:

<url>?p1=valore1&p2=valore2&...pn=valoreN

I parametri vengono recuperati nella pagina attraverso la proprietà QueryString (di tipo Dictionary(Of String, String)) dell’oggetto NavigationContext esposto dall’omonima proprietà dell’oggetto Page.

Nel nostro esempio simuleremo il passaggio di un ipotetico parametro 'id' alla pagina Page1.xaml dalla pagina Home.xaml.

Aggiungiamo, dunque, nella Home una TextBox per inserire il valore del parametro da passare alla Page1.xaml ed un HyperlinkButton per raggiungere fisicamente la pagina:

            <TextBlock FontSize="80">Home</TextBlock>
            <StackPanel Orientation="Horizontal">
                <TextBlock Text="Parameter:" VerticalAlignment="Center"></TextBlock>
                <TextBox x:Name="txtParam" Width="50"></TextBox>
               <HyperlinkButton x:Name="hlGoTo" Content="go to Page1" 
                                Click="hlGoTo_Click"></HyperlinkButton>
            </StackPanel>

Il codice dell’evento hlGoTo_Click è il seguente:

  Private Sub hlGoTo_Click(ByVal sender As System.Object, _
                           ByVal e As System.Windows.RoutedEventArgs)
    ' Costruisce la url di destinazione accodando il parametro ID ed il relativo valore
    Dim url As String = String.Format("/Pages/Page1.xaml?id={0}", txtParam.Text)
    Me.NavigationService.Navigate(New Uri(url, UriKind.Relative))
  End Sub

Come si può notare, è possibile navigare tra le pagine anche all’interno di una pagina stessa facendo riferimento alla proprietà NavigationService.

Analogamente, nella Page1.xaml aggiungiamo un TextBlock per visualizzare il parametro ricevuto:

      <StackPanel>
        <TextBlock FontSize="80">Page 1</TextBlock>
        <StackPanel Orientation="Horizontal">
          <TextBlock Text="QueryString: "/>
          <TextBlock x:Name="lblParams"></TextBlock>
        </StackPanel>
      </StackPanel>

Il codice relativo è il seguente:

  'Executes when the user navigates to this page.
  Protected Overrides Sub OnNavigatedTo(ByVal e As System.Windows.Navigation.NavigationEventArgs)
    Dim QueryString As IDictionary(Of String, String) = Me.NavigationContext.QueryString

    Dim param As String

    ' Se esiste il parametro ID...
    If QueryString.ContainsKey("id") Then
      ' ...lo visualizzo
      param = QueryString("id")
    Else
      ' altrimenti notifico che non esiste
      param = "Empty"
    End If
    lblParams.Text = param
  End Sub

L’operazione viene fatta nel metodo OnNavigatedTo, che viene eseguito quando si arriva alla pagina attraverso il metodo Navigate. Molto semplicemente, si usa la chiave del parametro prima per verificarne l'esistenza nell’insieme QueryString, poi per ottenerne il valore da assegnare alla label lblParams.

Questo è un esempio di esecuzione:

Come per la pagina, anche i valori in querystring sono visualizzati nella url accodandoli come si vede nella figura seguente:

Inutile dire che questo non può che far piacere ai motori di ricerca.

Conclusioni
Come abbiamo visto, creare applicazioni multi-pagina in Silverlight 3 diventa molto più semplice, grazie ai nuovi controlli Page e Frame ed alle classi NavigationService e NavigationContext, importanti rispettivamente per navigare tra le pagine e per inviare e recuperare eventuali parametri passati in QueryString.

Il codice a corredo di questo articolo è scaricabile dall'area Download.
Per ulteriori informazioni potete contattarmi al mio indirizzo visitare il mio blog.