Alessandro Del Sole's Blog

{ A programming space about Microsoft® .NET® }
posts - 1908, comments - 2047, trackbacks - 352

My Links

News

Your host

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

Cookie e Privacy

Disabilita cookie ShinyStat

Microsoft MVP

My MVP Profile

Microsoft Certified Professional

Microsoft Specialist

Xamarin Certified Mobile Developer

Il mio libro su VB 2015!

Pre-ordina il mio libro su VB 2015 Pre-ordina il mio libro "Visual Basic 2015 Unleashed". Clicca sulla copertina per informazioni!

Il mio libro su WPF 4.5.1!

Clicca sulla copertina per informazioni! E' uscito il mio libro "Programmare con WPF 4.5.1". Clicca sulla copertina per informazioni!

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

Le vostre visite

I'm a VB!

Guarda la mia intervista a Seattle

Follow me on Twitter!

Altri spazi

GitHub
I miei progetti open-source su GitHub

Article Categories

Archives

Post Categories

Image Galleries

Privacy Policy

Sviluppare Metro-style app per Windows 8 con Visual Basic - quarta parte

Dopo qualche giorno riprendiamo la serie di post introduttivi allo sviluppo Metro per Windows 8 con Visual Basic. Finalmente iniziamo a scrivere codice ed a questo proposito faccio le seguenti precisazioni:

  • l'app che iniziamo a creare oggi (e che sarà completata nel prossimo post) è un news reader, ossia aggrega e presenta feed RSS.
  • sebbene questa possa sembrare la più banale delle app, e in effetti può esserlo, è la palestra ideale per capire una serie di concetti come l'uso di XAML, il pattern Async, l'uso dei contract. Queste sono tutte specificità di Windows 8/Metro e vi renderete conto presto del fatto che scegliere di realizzare un'applicazione semplice o banale permette di concentrarsi sugli aspetti nuovi e tipici di Metro.

Il codice completo sarà disponibile per il download dal prossimo post. Parlando per immagini, oggi arriveremo a questo:

Decisamente essenziale, non c'è dubbio. La prossima volta arriveremo però a definire e utilizzare l'app bar:

Qui scopriremo anche che non dovremo impazzire a creare icone Metro, perché Visual Studio le genera per noi. Inoltre implementeremo lo Share contract integrato col sistema operativo:

Quindi news reader si, ma di classe  Prima di andare avanti, vi consiglio di aprire in un'altra istanza del browser il seguente documento MSDN, del quale andremo a prendere alcuni frammenti di codice, riadattati al nostro caso: Create your first Metro style app with C# or Visual Basic

Creazione del progetto

In Visual Studio 11 creiamo un nuovo progetto Metro con VB, scegliendo il template Blank Application. Questo genera un file XAML vuoto e senza dati di esempio. Così evitiamo confusioni e costruiremo il tutto a manina.

I dati

L'app si occuperà di scaricare e presentare informazioni provenienti dal feed ufficiale delle notizie su Visual Basic di MSDN. La prima cosa da fare è quindi creare una classe che chiameremo FeedItem, che rappresenta la singola notizia:

Public Class FeedItem
 
    Public Property Title As String = String.Empty
    Public Property Author As String = String.Empty
    Public Property Content As String = String.Empty
    Public Property PubDate As Date
    Public Property Link As Uri
End Class

Ci occorre poi una classe che rappresenti una collezione di notizie. Noi interrogheremo un solo feed, ma tale classe ci consente di avere più feed come peraltro dimostrato nella pagina MSDN linkata in precedenza. La classe si chiama FeedData ed è la seguente:

Imports Windows.Web.Syndication
 
Public Class FeedData
    Public Property Title As String = String.Empty
    Public Property Description As String = String.Empty
    Public Property PubDate As Date
    Public Property Link As Uri
 
    Private _items As New List(Of FeedItem)
 
    Public ReadOnly Property Items As List(Of FeedItem)
        Get
            Return _items
        End Get
    End Property
End Class

Prima considerazione: WinRT offre una strada già pronta per accedere ai feed RSS e Atom attraverso il namespace Windows.Web.Syndication. Ora è necessario creare una classe che si occupi di interrogare il feed e di esporre le notizie a codice chiamante. La chiamiamo FeedDataSource e questa è la prima parte:

Imports Windows.Web.Syndication
 
' FeedDataSource
' Holds a collection of blog feeds (FeedData), and contains methods needed to
' retreive the feeds.
Public Class FeedDataSource
    Private _Feeds As New ObservableCollection(Of FeedData)()
    Public ReadOnly Property Feeds() As ObservableCollection(Of FeedData)
        Get
            Return Me._Feeds
        End Get
    End Property
 
    Public Async Function GetFeedsAsync() As Task
    Dim feed1 As Task(Of FeedData) =
        GetFeedAsync("http://services.social.microsoft.com/feeds/feed/VB_featured_resources")
 
 
        Me.Feeds.Add(Await feed1)
    End Function

La collection di feed è di banale interpretazione. E' importante capire che usiamo una collection perché potremmo avere più feed, sebbene qui ne interroghiamo uno solo. Diciamo che ci piace guardare avanti :-)

Ciò che dobbiamo invece considerare è la definizione del metodo GetFeedsAsync. Tale metodo legge in modo asincrono il contenuto del feed e lo aggiunge alla collezione. La sua definizione contiene la parola chiave Async e restituisce un oggetto Task, due cose che vanno a braccetto. L'uso di Async è necessario per dire che l'operazione è asincrona, che è normale in Windows 8. Poiché l'operazione è asincrona e non sequenziale, si restituisce un oggetto Task, che già conoscete se avete letto almeno qualcosa della Task Parallel Library. Il suffisso Async è ncessario per convenzione nelle definizioni dei metodi asincroni.

L'uso della parola chiave Async è necessario anche per un altro motivo: il metodo legge il contenuto dei feed attraverso un altro metodo asincrono chiamato GetFeedAsync. Eccone il codice, che completa la definizione della classe:

    Private Async Function GetFeedAsync(feedUriString As String) As Task(Of FeedData)
Dim Client As New SyndicationClient
Dim FeedUri As New Uri(feedUriString)
 
        Try
            Dim Feed As SyndicationFeed = Await Client.RetrieveFeedAsync(FeedUri)
 
' This code is executed after RetrieveFeedAsync returns the SyndicationFeed.
' Process the feed and copy the data we want into our FeedData and FeedItem classes.
Dim FeedData As New FeedData
            FeedData.Link = FeedUri
            FeedData.Title = Feed.Title.Text
 
            If Feed.Subtitle.Text IsNot Nothing Then
                FeedData.Description = Feed.Subtitle.Text
            End If
' Use the date of the latest post as the last updated date.
            FeedData.PubDate = Feed.Items(0).PublishedDate.DateTime
            For Each Item As SyndicationItem In Feed.Items
Dim FeedItem As New FeedItem
                FeedItem.Title = Item.Title.Text
                FeedItem.PubDate = Item.PublishedDate.DateTime
                If Item.Authors.Any Then FeedItem.Author = Item.Authors(0).Name
 
                FeedItem.Content = Item.Summary.Text
                FeedItem.Link = Item.Links(0).Uri
 
                FeedData.Items.Add(FeedItem)
            Next
            Return FeedData
 
        Catch Ex As Exception
            Return Nothing
        End Try
    End Function
End Class

La classe SyndicationClient fornisce un'infrastruttura nativa per leggere feed attraverso il suo metodo Client.RetrieveFeedAsync, il cui risultato è di tipo SyndicationFeed. Come si può facilmente intuire, quest'ultima classe rappresenta un feed e le sue notizie. Il metodo in questione è preceduto da Await, che vuol dire: esegui l'operazione in modo asincrono ed attendi il suo completamento per assegnare il risultato alla variabile.

Per il resto, si fa uso della classe SyndicationItem che rappresenta una singola notizia nel feed (ossia nella collection di notizie esposte dal SyndicationFeed). Ciclare tale collection permette poi di creare tante istanze della classe FeedItem quante sono le notizie. L'uso di tale classe è veramente intuitivo e non mi dilungo troppo su questa, descritta comunque nella documentazione MSDN.

Seguendo l'esempio della documentazione stessa, nel file App.xaml.vb (ve lo ricordate? lo stesso di WPF, Silverlight, Windows Phone....) esponiamo la sorgente dati istanziata:

Public Shared DataSource As FeedDataSource
 
Public Sub New()
 
    ' This call is required by the designer.
    InitializeComponent()
 
    ' Add any initialization after the InitializeComponent() call.
    DataSource = New FeedDataSource
End Sub

Passiamo ora ad implementare altre parti dell'infrastruttura.

Converters

Anche in Metro esiste il concetto dei value converters, da richiamare da XAML. Ed esattamente come nelle altre tecnologie, come WPF/Silverlight/Phone, i converters si creano attraverso l'implementazione dell'interfaccia IValueConverter. Il che significa che non avremo difficoltà!  

Con particolare riguardo al nostro caso, ci serve un value converter per formattare le date provenienti dal feed. Prendiamo ancora spunto dalla documentazione per implementare una classe chiamata DateConverter, definita come segue e che potrà avere molteplici utilizzi:

Imports Windows.Globalization.DateTimeFormatting
 
Public Class DateConverter
    Implements IValueConverter
 
    Public Function Convert(ByVal value As ObjectByVal targetType As TypeByVal parameter As ObjectByVal culture As String) _
        As Object Implements IValueConverter.Convert
        If value Is Nothing Then
            Throw New ArgumentNullException("value""Value cannot be null.")
        End If
        If Not GetType(DateTime).Equals(value.GetType()) Then
            Throw New ArgumentException("Value must be of type DateTime.""value")
        End If
 
        Dim dt As DateTime = DirectCast(value, DateTime)
 
        If parameter Is Nothing Then
            ' Date "7/27/2011 9:30:59 AM" returns "7/27/2011"
            Return DateTimeFormatter.ShortDate.Format(dt)
 
        ElseIf DirectCast(parameter, String) = "day" Then
            ' Date "7/27/2011 9:30:59 AM" returns "27"
            Dim dateFormatter As DateTimeFormatter = New DateTimeFormatter("{day.integer(2)}")
            Return dateFormatter.Format(dt)
 
        ElseIf DirectCast(parameter, String) = "month" Then
            ' Date "7/27/2011 9:30:59 AM" returns "JUL"
            Dim dateFormatter As DateTimeFormatter = New DateTimeFormatter("{month.abbreviated(3)}")
            Return dateFormatter.Format(dt).ToUpper()
 
        ElseIf DirectCast(parameter, String) = "year" Then
            ' Date "7/27/2011 9:30:59 AM" returns "2011"
            Dim dateFormatter As DateTimeFormatter = New DateTimeFormatter("{year.full}")
            Return dateFormatter.Format(dt)
 
        Else
            ' Requested format is unknown. Return in the original format.
            Return dt.ToString()
 
        End If
    End Function
 
    Public Function ConvertBack(ByVal value As ObjectByVal targetType As TypeByVal parameter As ObjectByVal culture As String) _
        As Object Implements Windows.UI.Xaml.Data.IValueConverter.ConvertBack
        Dim StrValue As String = DirectCast(value, String)
        Dim Result As Date
        If DateTime.TryParse(StrValue, Result) Then
            Return Result
        End If
        Return DependencyProperty.UnsetValue
    End Function
End Class

La lettura dei commenti aiuta nella comprensione, si noti più che altro come il namespace Windows.Globalization.DateTimeFormatting consenta di formattare date in modo piuttosto agevole.

L'interfaccia, ovvero la leggenda del Santo XAML

E' giunta l'ora di passare alla definizione della UI e forse sarà questo il punto in cui Windows 8 e Metro vi spaventeranno meno e sarà forse questo il momento in cui benedirete il giorno in cui avete deciso di investire su WPF o Silverlight. Passiamo al designer facendo doppio click sul file BlankPage.xaml in Solution Explorer. Noterete subito un oggetto Page, certamente familiare. Ci interessa definire e organizzare all'interno del layout costituito da una Grid radice:

  • visualizzazione del testo di benvenuto
  • titolo del feed
  • un elenco delle notizie
  • un'anteprima del contenuto

Ancora una volta ci viene incontro il codice della documentazione che, se avete confidenza con XAML e data-binding, vi risulterà davvero semplice (da mettere all'interno della Grid radice):

        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition Height="140" />
                <RowDefinition Height="*" />
            </Grid.RowDefinitions>
 
            <!-- Title -->
            <TextBlock x:Name="TitleText" Text="{Binding Title}" 
                   VerticalAlignment="Center" FontSize="48" Margin="56,0,0,0"/>
 
            <!-- Content -->
            <Grid Grid.Row="1">
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="2*" MinWidth="320" />
                    <ColumnDefinition Width="3*" />
                </Grid.ColumnDefinitions>
 
                <!-- Left column -->
                <!-- The default value of Grid.Column is 0, so we do not need to set it   
                 to make the ListView show up in the first column. -->
                <ListView x:Name="ItemListView"  SelectionChanged="ItemListView_SelectionChanged"
          ItemsSource="{Binding Items}"
          Margin="60,0,0,10">
                    <ListView.ItemTemplate>
                        <DataTemplate>
                            <StackPanel>
                                <TextBlock Text="{Binding Title}"  
                           FontSize="24" Margin="5,0,0,0" TextWrapping="Wrap" />
                                <TextBlock Text="{Binding Author}" 
                           FontSize="16" Margin="15,0,0,0"/>
                                <TextBlock Text="{Binding Path=PubDate, Converter={StaticResource dateConverter}}" 
                           FontSize="16" Margin="15,0,0,0"/>
                            </StackPanel>
                        </DataTemplate>
                    </ListView.ItemTemplate>
                </ListView>
 
 
                <!-- Right column -->
                <!-- We use a Grid here instead of a StackPanel so that the WebView sizes correctly. -->
                <Grid DataContext="{Binding ElementName=ItemListView, Path=SelectedItem}"
                  Grid.Column="1" Margin="25,0,0,0">
                    <Grid.RowDefinitions>
                        <RowDefinition Height="Auto" />
                        <RowDefinition Height="*" />
                    </Grid.RowDefinitions>
                    <TextBlock x:Name="PostTitleText" Text="{Binding Title}" FontSize="24"/>
                    <WebView x:Name="ContentView" Grid.Row="1" Margin="0,5,20,20"/>
                </Grid>
            </Grid>
        </Grid>

Non mi posso chiaramente soffermare su come si definisce il layout in XAML o su controlli come TextBlock e StackPanel. Focalizziamo l'attenzione sul fatto che in WinRT si usa una ListView anziché ListBox, ma il concetto dei template rimane invariato. Si noti l'utilizzo del converter definito poc'anzi. Nella seconda Grid, invece, si noti l'oggetto WebView che viene utilizzato per presentare l'anteprima del contenuto.

Esattamente come altrove, bisogna dichiarare le risorse per poterle utilizzare e questo è il caso del nostro converter. Quindi nel file App.xaml aggiungiamo un ResourceDictionary che contenga la definizione della risorsa:

        <ResourceDictionary>
            <local:DateConverter x:Key="dateConverter" />
        </ResourceDictionary>

E' interessante sottolineare che non c'è necessità di definire il namespace local, poiché Visual Studio 11 lo fa per noi nella definizione dell'elemento Application. Si passa poi al code-behind in cui dobbiamo specificare cosa avviene quando la pagina viene raggiunta. Questo si fa con l'override del metodo OnNavigatedTo, passato in forma asincrona:

    Protected Overrides Async Sub OnNavigatedTo (e As Navigation.NavigationEventArgs)
    Dim _feedDataSource As FeedDataSource = App.DataSource
        If _feedDataSource.Feeds.Any = False Then
            Await _feedDataSource.GetFeedsAsync
        End If
        Me.DataContext = (_feedDataSource.Feeds).First
    End Sub

Il codice recupera l'istanza della sorgente dati e, nel caso questa non contenga elementi, invoca il suo metodo GetFeedsAsync per il caricamento. Infine assegna al contesto dati della pagina il primo dei feed (che in questo caso è anche l'unico).

Infine, gestiamo l'evento SelectionChanged per la ListView:

   Private Sub ItemListView_SelectionChanged(sender As Object, e As SelectionChangedEventArgs)
        Dim _feedItem As FeedItem = e.AddedItems(0)
        If _feedItem IsNot Nothing Then
            ' Navigate the WebView to the blog post content HTML string.
            contentView.NavigateToString(_feedItem.Content)
        End If
    End Sub

Questo recupera l'elemento selezionato e passa il suo contenuto all'oggetto WebView attraverso il metodo NavigateToString.

Test

Se ora provate ad avviare l'app otterrete il risultato mostrato nella prima figura di questo post. Complimenti, avete appena creato la vostra prima Metro app con Visual Basic 11!

Ricapitolando, in questo post abbiamo capito come lavorare in modo asincrono, come definire l'interfaccia tramite XAML, come utilizzare alcuni oggetti offerti da WinRT. Nel prossimo post implementeremo caratteristiche specifiche di Windows 8 e daremo un'occhiata al discorso della rotazione del device.

Alessandro

Print | posted on lunedì 23 aprile 2012 23:03 | Filed Under [ Visual Basic UWP e Windows Store Apps Visual Studio 2012 ]

Powered by:
Powered By Subtext Powered By ASP.NET