Alessandro Del Sole's Blog

{ A programming space about Microsoft® .NET® }
posts - 1716, comments - 2327, trackbacks - 356

My Links

News

Your host

This is me! Questo spazio è dedicato a Microsoft® .NET®, di cui sono molto appassionato :-)

These postings are provided 'AS IS' for entertainment purposes only with absolutely no warranty expressed or implied and confer no rights.
If you're not an Italian user, please visit my English blog

Microsoft MVP

My MVP Profile

My MVP Corner Page

Il mio libro su VB2012!

Il mio nuovo libro su Visual Basic 2012 E' uscito il mio libro "Visual Basic 2012 Unleashed". Clicca sulla copertina per informazioni!

Il mio libro su VS LightSwitch!

Il mio nuovo libro su Visual Studio LightSwitch E' uscito il mio libro "Visual Studio LightSwitch Unleashed". Clicca sulla copertina per info!

Visual Basic Tips & Tricks Team Member

Le vostre visite

I'm a VB!

Guarda la mia intervista a Seattle

Follow me on Twitter!


Guarda i miei webcast MSDN

Altri spazi

CodePlex download Scarica i miei progetti open-source su CodePlex!

Cerca nel blog



Seguimi su Twitter!

Article Categories

Archives

Post Categories

Image Galleries

.NET Framework

Back to basics

Blogroll

Help 1.x e 2.0

Microsoft & MSDN

Setup & Deployment

Visual Basic .NET e 2005

Creare un lettore di feed RSS per Windows Phone 7 con Visual Basic 2010

Oggi è stata finalmente pubblicata la versione definitiva degli strumenti di sviluppo per Windows Phone 7 e Visual Basic 2010 che, vi ricordo, possono essere utilizzati a tutti gli effetti per la pubblicazione delle app sul Market Place. Vediamo quindi come sfruttare questa nuova release per creare un esempio un pochino più complesso, ossia un lettore di feed RSS. Questo post non è esattamente introduttivo ai tool, per cui vi rimando alla documentazione MSDN. In questo post sfrutteremo il controllo Panorama e un po' di data-binding lato XAML, per facilitarci le cose.

Dopo aver avviato Visual Studio 2010, selezioniamo il template Windows Phone Panorama Application dalla cartella di progetti chiamata Silverlight For Windows Phone:

Il nome del template prende il nome dal controllo Panorama, che consente di sfogliare più contenuti con un touch, andando oltre le dimensioni fisiche del display, sia in un senso che nell'altro. Una volta creato il progetto, Visual Studio 2010 si presenta in un modo che già conoscete se avete provato i tool per C# o la CTP per Visual Basic:

 

Quindi è disponibile il designer sulla sinistra e il codice XAML sulla destra. Si può osservare che, per default, viene aggiunto un controllo Panorama che è composto da diversi elementi PanoramaItem, ciascuno dei quali rappresenta una pagina del controllo stesso e che viene attivata quando viene fatto il touch sul display. A design time questo controllo viene popolato da una serie di dati offerti da un'architettura basata su Model-View-ViewModel. Questo è evidenziabile curiosando in Solution Explorer, dove troviamo una cartella chiamata SampleData che espone i dati e una chiamata ViewModels, che espone le classi "ponte" tra UI e dati:

 Poiché sfruttare questo tipo di pattern richiede una conoscenza almeno generica del pattern stesso, in questo post non ne faremo utilizzo ma ci proponiamo di farlo successivamente come approfondimento.

Abbiamo quindi detto che vogliamo creare un lettore di feed RSS. Il sito dal quale consumeremo i feed è il Visual Basic Developer Center di Microsoft e la nostra applicazione sarà in grado di visualizzare i seguenti elementi:

  • ultime notizie
  • ultimi articoli tecnici
  • ultimi "How-do-I videos"
  • ultimi thread nel forum Visual Basic IDE

Per farlo sfrutteremo LINQ to XML e il download asincrono di contenuti. Dopo aver aggiunto un riferimento all'assembly System.Xml.Linq (che nelle applicazioni Silverlight nella loro generalità non è incluso di default), la prima cosa da fare è creare una classe che mappi un contenuto esposto da feed. Questa classe si chiamerà FeedItem e la aggiungiamo al progetto nel modo che sappiamo. Ecco il codice:

Public Class FeedItem
 
    'Titolo contenuto
    Public Property Title As String
    'Data di pubblicazione
    Public Property pubDate As String
    'Link al contenuto
    Public Property Link As Uri
 
End Class

Prima di dedicarci all'interfaccia grafica e al data-binding, scriviamo il codice che scarichi il contenuto dei feed. Come avviene nelle applicazioni Silverlight classiche, il download di informazioni dev'essere fatto in modalità asincrona e per questo ricorriamo alla classe WebClient. Quindi nel file di code-behind chiamato MainPage.xaml.vb, scriviamo il seguente metodo commentato nel codice:

    'Reads RSS contents from the given Uri and assigns the result to the specified PanoramaItem
    Private Sub ReadContentsRss(ByVal rssUri As UriByVal panoramaElement As PanoramaItem)
        'The WebClient class allows async downloading
        Dim wclient As New WebClient()
 
 
        'The statement lambda is a replacement for AddressOf
        AddHandler wclient.OpenReadCompleted, Sub(sender As Object, e As OpenReadCompletedEventArgs)
                                                  If e.Error IsNot Nothing Then
                                                      MessageBox.Show(e.Error.Message)
                                                      Exit Sub
                                                  End If
 
                                                  Dim str As IO.Stream = e.Result
                                                  Dim xdoc As XDocument = XDocument.Load(str)
 
                                                  ' take results from each item element in the XML feed
                                                  Dim rssFeedsItems = (From item In xdoc...<item>
                                                                       Let published = CStr(CDate(item.<pubDate>.Value).ToLocalTime)
                                                                       Let title = item.<title>.Value
                                                                       Let postLink = item.<link>.Value
                                                                       Select New FeedItem With {.Link = New Uri(postLink, UriKind.Absolute),
                                                                                                 .pubDate = published,
                                                                                                 .Title = title}).ToList
 
                                                  ' close
                                                  str.Close()
                                                  ' add results to listbox
                                                  panoramaElement.DataContext = rssFeedsItems
                                              End Sub
        wclient.OpenReadAsync(rssUri)
    End Sub

Si noti la "finezza" della statement lambda al posto di un AddressOf, che permette di avere il gestore di evento in un unico posto. Sostanzialmente viene invocato il metodo WebClient.OpenReadAsync; quando viene generato l'evento OpenReadCompleted, viene dapprima verificato che non ci siano errori, nel qual caso esce. Diversamente carica il feed in uno stream (il nome del feed xml è contenuto nella proprietà e.Result). La successiva query LINQ cicla tutti gli elementi di tipo item, ognuno dei quali rappresenta un blog post per capirci, e ne memorizza le informazioni in tante istanze della classe FeedItem quante sono gli elementi ottenuti.

Fatto questo, possiamo caricare il contenuto dei feed al caricamento della pagina principale:

    Private Sub MainPage_Loaded(ByVal sender As ObjectByVal e As RoutedEventArgsHandles Me.Loaded
        'VB Highlights feed
        ReadContentsRss(New Uri("http://services.social.microsoft.com/feeds/feed/VB_featured_resources"),
                        ContentsItem)
        'How-do-I videos feed
        ReadContentsRss(New Uri("http://www.microsoft.com/feeds/msdn/en-us/vbasic/HDI-vbasic.xml"),
                        VideosItem)
        'Articles feed
        ReadContentsRss(New Uri("http://services.community.microsoft.com/feeds/feed/query/tag/article/eq/tag/visual%20basic/eq/and/locale/en-us/eq/and/namespace/F76D3EB9-73E1-4810-8C2D-0B42FEC930E7/eq/and"),
                        ArticlesItem)
        'VB IDE Forum feed
        ReadContentsRss(New Uri("http://social.msdn.microsoft.com/Forums/en-US/vbide/threads?outputAs=rss"),
                        ForumsItem)
    End Sub

Ora passiamo all'interfaccia, ossia al file MainPage.xaml. Per prima cosa modifichiamo il titolo del Panorama:

        <!--Panorama control-->
        <controls:Panorama Title="VB Dev Center">

Successivamente eliminiamo tutti gli elementi <controls:PanoramaItem/> proposti di default. Aggiungiamo il primo PanoramaItem personalizzato, che punta ai VB Highlights:

            <controls:PanoramaItem Header="VB Highlights" x:Name="ContentsItem"
                                  >
                <!--Double line list with text wrapping-->
                <ListBox Margin="0,0,-12,0" ItemsSource="{Binding}"
                         >
                    <!-- ListBox template for each item-->
                    <ListBox.ItemTemplate>
                        <DataTemplate>
                            <StackPanel Margin="0,0,0,17" Width="432"
                                        >
                                <TextBlock Tag="{Binding Path=Link}" 
                                           Text="{Binding Path=Title}"
                                           TextWrapping="Wrap"
                                           Style="{StaticResource PhoneTextLargeStyle}"
                                           MouseLeftButtonUp="TextBlock_MouseLeftButtonUp"
                                           >
                                </TextBlock>
                                <TextBlock Text="{Binding pubDate}" 
                                           TextWrapping="Wrap" Margin="12,2,12,0" 
                                           Style="{StaticResource PhoneTextSubtleStyle}"/>
                            </StackPanel>
                        </DataTemplate>
                    </ListBox.ItemTemplate>
                </ListBox>
            </controls:PanoramaItem>

Come funziona? Sfruttando gli stili di default, il primo TextBlock ottiene, grazie al data-binding, il titolo della notizia "bindandolo" alla proprietà Text mentre la proprietà Tag è in binding con il Link del post, cosa che ci servirà tra poco. Il secondo TextBlock visualizza la data di pubblicazione, sempre in data-binding. Ovviamente la collezione di notizie è stata assegnata a runtime tramite il metodo ReadContentRss mostrato in precedenza. Capito il meccanismo, aggiungiamo gli altri PanoramaItem che puntano alle altre risorse:

            <controls:PanoramaItem Header="'How do I' videos" x:Name="VideosItem"
                                  >
                <!--Double line list with text wrapping-->
                <ListBox Margin="0,0,-12,0" ItemsSource="{Binding}"
                         >
                    <ListBox.ItemTemplate>
                        <DataTemplate>
                            <StackPanel Margin="0,0,0,17" Width="432"
                                        >
                                <TextBlock Tag="{Binding Path=Link}" 
                                           Text="{Binding Path=Title}"
                                           TextWrapping="Wrap"
                                           Style="{StaticResource PhoneTextLargeStyle}"
                                           MouseLeftButtonUp="TextBlock_MouseLeftButtonUp"
                                           >
                                </TextBlock>
                                <TextBlock Text="{Binding pubDate}" 
                                           TextWrapping="Wrap" Margin="12,2,12,0" 
                                           Style="{StaticResource PhoneTextSubtleStyle}"/>
                            </StackPanel>
                        </DataTemplate>
                    </ListBox.ItemTemplate>
                </ListBox>
            </controls:PanoramaItem>
            <controls:PanoramaItem Header="Articles" x:Name="ArticlesItem"
                                  >
                <!--Double line list with text wrapping-->
                <ListBox Margin="0,0,-12,0" ItemsSource="{Binding}"
                         >
                    <ListBox.ItemTemplate>
                        <DataTemplate>
                            <StackPanel Margin="0,0,0,17" Width="432"
                                        >
                                <TextBlock Tag="{Binding Path=Link}" 
                                           Text="{Binding Path=Title}"
                                           TextWrapping="Wrap"
                                           Style="{StaticResource PhoneTextLargeStyle}"
                                           MouseLeftButtonUp="TextBlock_MouseLeftButtonUp"
                                           >
                                </TextBlock>
                                <TextBlock Text="{Binding pubDate}" 
                                           TextWrapping="Wrap" Margin="12,2,12,0" 
                                           Style="{StaticResource PhoneTextSubtleStyle}"/>
                            </StackPanel>
                        </DataTemplate>
                    </ListBox.ItemTemplate>
                </ListBox>
            </controls:PanoramaItem>
            <controls:PanoramaItem Header="VB IDE Forum" x:Name="ForumsItem"
                                  >
                <!--Double line list with text wrapping-->
                <ListBox Margin="0,0,-12,0" ItemsSource="{Binding}"
                         >
                    <ListBox.ItemTemplate>
                        <DataTemplate>
                            <StackPanel Margin="0,0,0,17" Width="432"
                                        >
                                <TextBlock Tag="{Binding Path=Link}" 
                                           Text="{Binding Path=Title}"
                                           TextWrapping="Wrap"
                                           Style="{StaticResource PhoneTextLargeStyle}"
                                           MouseLeftButtonUp="TextBlock_MouseLeftButtonUp"
                                           >
                                </TextBlock>
                                <TextBlock Text="{Binding pubDate}"
                                           TextWrapping="Wrap" Margin="12,2,12,0" 
                                           Style="{StaticResource PhoneTextSubtleStyle}"/>
                            </StackPanel>
                        </DataTemplate>
                    </ListBox.ItemTemplate>
                </ListBox>
            </controls:PanoramaItem>

Ora ipotizziamo che il nostro utente debba avere la possibilità di visualizzare un'anteprima di ciascun contenuto all'interno di un controllo WebBrowser. Poiché noi abbiamo utilizzato i TextBlock, il touch su di essi scatenerà l'evento MouseLeftButtonUp che può essere gestito come segue:

    Private Sub TextBlock_MouseLeftButtonUp(ByVal sender As System.Object,
                                            ByVal e As System.Windows.Input.MouseButtonEventArgs)
        Dim senderButton = CType(sender, TextBlock)
        NavigationService.Navigate(New Uri("/BrowserPage.xaml?Content=" + senderButton.Tag.ToString,
                                           UriKind.Relative))
    End Sub

Il codice converte il Sender in TextBlock, dopodiché costruisce una query string che invoca una pagina BrowserPage.xaml, che creeremo tra pochi attimi, alla quale viene aggiunto il contenuto della proprietà Tag del TextBlock che, come ricorderete, contiene il link alla notizia. Il consueto metodo NavigationService.Navigate va ad aprire la pagina sfruttando il framework di navigazione. Ora aggiungiamo, tramite Project|Add New Item, una nuova pagina di tipo Windows Phone Portrait Page. Nello XAML modifichiamo la proprietà SupportedOrientations in questo modo:

SupportedOrientations="PortraitOrLandscape"

Fatto questo, modifichiamo un po' di titoli e aggiungiamo un controllo WebBrowser trascinandolo dalla toolbox:

        <!--TitlePanel contains the name of the application and page title-->
        <StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="12,17,0,28">
            <TextBlock x:Name="PageTitle" Text="Anteprima" Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle1Style}"/>
        </StackPanel>
 
        <!--ContentPanel - place additional content here-->
        <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
            <phone:WebBrowser Name="Web1" />
        </Grid>

A livello di code-behind, dobbiamo ricavare la query string passata dalla pagina principale, ottenere il link e passarlo al WebBrowser. Ecco come si fa:

    Private Sub BrowserPage_Loaded(ByVal sender As Object,
                                   ByVal e As System.Windows.RoutedEventArgsHandles MyBase.Loaded
        Try
            Dim contentSource As String = ""
            NavigationContext.QueryString.TryGetValue("Content", contentSource)
            Web1.Navigate(New Uri(contentSource))
 
        Catch ex As Exception
            MessageBox.Show(ex.Message)
        End Try
    End Sub

La classe NavigationContext espone il metodo QueryString.TryGetValue che permette di leggere il parametro della stringa e il suo valore; quest'ultimo viene convertito in Uri e passato al WebBrowser. Se ora proviamo ad avviare l'applicazione otterremo il seguente risultato:

Se poi proviamo ad aprire uno dei contenuti caricati, questo verrà visualizzato nella pagina dell'anteprima:

Download del codice

Il progetto sorgente illustrato nell'articolo si trova qui.

Considerazioni finali

Nello sviluppo per Windows Phone 7 non dimenticate di leggere le linee guida per lo sviluppo di app e concetti importanti come il tombstoning. Ci sono poi chiaramente molti miglioramenti da fare all'applicazione di esempio, come l'utilizzo della barra dei pulsanti all'interno della quale si possono implementare pulsanti per fare il refresh manuale dei contenuti. Questo e molto altro lo trovate nella pagina Microsoft degli esempi dedicati a Windows Phone 7 con VB e C#.

Enjoy this RTW!

Alessandro

Print | posted on lunedì 29 novembre 2010 20:40 |

Feedback

No comments posted yet.

Post Comment

Title  
Name  
Email
Url
Comment   
Please add 2 and 6 and type the answer here:

Powered by:
Powered By Subtext Powered By ASP.NET