Alessandro Del Sole's Blog

{ A programming space about Microsoft® .NET® }
posts - 1504, comments - 3233, 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 VS LightSwitch!

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

Il mio libro su VB2010!

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

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

mercoledì 16 maggio 2012

Registrazione della mia sessione su WPF 4.5 ai Community Days 2012

Tramite altri canali social, tempo fa, avevo segnalato la disponibilità della registrazione della sessione che avevo tenuto ai Community Days 2012 di Milano, dal titolo What's new in WPF 4.5.

Poiché ho ricevuto contatti in merito alla sessione stessa, sono andato a verificare ed effettivamente non avevo bloggato circa la disponibilità della sessione.

La potete trovare a quest'indirizzo del portale dei Community Days, dal quale potete anche scaricare le slide e la demo (che andrebbe aggiornata alla Beta di Visual Studio 11).

La registrazione è anche scaricabile in vari formati video se accedete alla sua pagina su Vimeo.

Per facilitarvi la visione, qui c'è il video in embed: 

Alessandro

posted @ mercoledì 16 maggio 2012 19.11 | Feedback (0) | Filed Under [ Windows Presentation Foundation Community and friends Visual Studio 11 ]

martedì 15 maggio 2012

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

Dopo una lunga pausa riprendiamo la serie di post dedicati allo sviluppo di app in stile Metro per Windows 8, con il nostro amato Visual Basic.

C'eravamo salutati la volta scorsa dopo aver completato il nostro news reader, dotato di app bar. Questa volta facciamo considerazioni sul layout e sulle rotazioni.

Layout e adattabilità

Sganciamoci per un momento dall'ambiente desktop classico a cui siamo abituati e immaginiamo di avere in mano un tablet. Come sapete, questo può essere ruotato. Ciò implica che il layout della nostra app deve essere in grado di adattarsi alla rotazione. Non solo: in Windows 8 esiste il concetto di Snap che permette di tenere in vita un'app Metro a fianco di quelle desktop. La seguente figura mostra una rappresentazione di un'app in snap:

Lo snap è disponibile solo per risoluzioni da 1366 x 768 in su, ma va comunque tenuto in considerazione. Ecco quindi che in questo post stravolgeremo praticamente tutta la UI del nostro news reader, facendo delle cose necessarie.

Vi ricordo che il codice fino alla quinta parte di questa serie è disponibile a questo indirizzo e vi consiglio di scaricarlo in modo da avere una base da cui partire.

Altra cosa che vi consiglio vivamente è avere sempre al vostro fianco la documentazione MSDN per creare la prima app Metro. Li ci sono cose che useremo in questo post, cose che useremo parzialmente e cose che lì sono fatte in modo più approfondito, per cui vi servirà in futuro.

Stati Visuali

Per cambiare layout a seconda dell'orientamento del device e dello snap si usa un oggetto già noto in WPF e Silverlight, il Visual State Manager. Partendo dalla documentazione sopra linkata, possiamo ricavare un po' di codice adatto allo scopo. Per prima cosa modifichiamo l'oggetto Page in LayoutAwarePage, che si adatta a questi discorsi. In primo luogo aggiungiamo un namespace common a livello di pagina:

xmlns:common="using:adsVBMetroReader.Common"

Quindi modifichiamo l'oggetto Page in common:LayoutAwarePage. All'interno della Grid radice vanno poi aggiunti gli stati visuali, in particolare la gestione degli stati Landscape, Portrait e Snapped. E' importante ricordare che l'oggetto LayoutAwarePage non fa parte di WinRT ma è disponibile tra i file auto-generati dal template di progetto ed offre di default una serie di membri che supportano la navigazione in pagine e che è sufficiente collegare in binding un button al membro GoBack per attivare la navigazione alla pagina precedente. In questo particolarissimo esempio non abbiamo navigazione, quindi negli stati visuali viene reso "innocuo" lo XAML relativo a questo pulsante. In questo preciso momento accompagnatevi nella lettura con la documentazione poichè riporta le motivazioni relative alle altre parti rese innocue. Ecco lo XAML per gli stati visuali:

        <VisualStateManager.VisualStateGroups>
 
            <!-- Visual states reflect the application's view state -->
            <VisualStateGroup>
                <VisualState x:Name="FullScreenLandscape"/>
 
                <!-- Filled uses a simpler list format in a narrower column -->
                <VisualState x:Name="Filled">
                    <Storyboard>
                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="primaryColumn" Storyboard.TargetProperty="Width">
                            <DiscreteObjectKeyFrame KeyTime="0" Value="420"/>
                        </ObjectAnimationUsingKeyFrames>
                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="itemListView" Storyboard.TargetProperty="ItemTemplate">
                            <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource NarrowListItemTemplate}"/>
                        </ObjectAnimationUsingKeyFrames>
                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="itemDetail" Storyboard.TargetProperty="Padding">
                            <DiscreteObjectKeyFrame KeyTime="0" Value="60,0,66,0"/>
                        </ObjectAnimationUsingKeyFrames>
                    </Storyboard>
                </VisualState>
 
                <!--
                    The page respects the narrower 100-pixel margin convention for portrait, and the page
                    initially hides details to show only the list of items
                -->
                <VisualState x:Name="FullScreenPortrait">
                    <Storyboard>
                        <!--<ObjectAnimationUsingKeyFrames Storyboard.TargetName="backButton" Storyboard.TargetProperty="Style">
                            <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource PortraitBackButtonStyle}"/>
                        </ObjectAnimationUsingKeyFrames>-->
 
                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="itemDetail" Storyboard.TargetProperty="Visibility">
                            <DiscreteObjectKeyFrame KeyTime="0" Value="Collapsed"/>
                        </ObjectAnimationUsingKeyFrames>
                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="itemListView" Storyboard.TargetProperty="Margin">
                            <DiscreteObjectKeyFrame KeyTime="0" Value="100,0,90,60"/>
                        </ObjectAnimationUsingKeyFrames>
                    </Storyboard>
                </VisualState>
 
                <!--
                    When an item is selected in portrait the details display requires more extensive changes:
                     * Hide the master list and the column is was in
                     * Move item details down a row to make room for the title
                     * Move the title directly above the details
                     * Adjust margins and padding for details
                 -->
                <VisualState x:Name="FullScreenPortrait_Detail">
                    <Storyboard>
                        <!--<ObjectAnimationUsingKeyFrames Storyboard.TargetName="backButton" Storyboard.TargetProperty="Style">
                            <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource PortraitBackButtonStyle}"/>
                        </ObjectAnimationUsingKeyFrames>-->
 
                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="primaryColumn" Storyboard.TargetProperty="Width">
                            <DiscreteObjectKeyFrame KeyTime="0" Value="0"/>
                        </ObjectAnimationUsingKeyFrames>
                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="itemListScrollViewer" Storyboard.TargetProperty="Visibility">
                            <DiscreteObjectKeyFrame KeyTime="0" Value="Collapsed"/>
                        </ObjectAnimationUsingKeyFrames>
                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="itemDetail" Storyboard.TargetProperty="(Grid.Row)">
                            <DiscreteObjectKeyFrame KeyTime="0" Value="1"/>
                        </ObjectAnimationUsingKeyFrames>
                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="itemDetail" Storyboard.TargetProperty="(Grid.RowSpan)">
                            <DiscreteObjectKeyFrame KeyTime="0" Value="1"/>
                        </ObjectAnimationUsingKeyFrames>
                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="titlePanel" Storyboard.TargetProperty="(Grid.Column)">
                            <DiscreteObjectKeyFrame KeyTime="0" Value="1"/>
                        </ObjectAnimationUsingKeyFrames>
                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="itemDetailGrid" Storyboard.TargetProperty="Margin">
                            <DiscreteObjectKeyFrame KeyTime="0" Value="0,0,0,60"/>
                        </ObjectAnimationUsingKeyFrames>
                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="itemDetail" Storyboard.TargetProperty="Padding">
                            <DiscreteObjectKeyFrame KeyTime="0" Value="100,0,90,0"/>
                        </ObjectAnimationUsingKeyFrames>
                    </Storyboard>
                </VisualState>
 
                <!--
                    The back button and title have different styles when snapped, and the page
                    initially hides details to show only the list of items
                -->
                <VisualState x:Name="Snapped">
                    <Storyboard>
                        <!--<ObjectAnimationUsingKeyFrames Storyboard.TargetName="backButton" Storyboard.TargetProperty="Style">
                            <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource SnappedBackButtonStyle}"/>
                        </ObjectAnimationUsingKeyFrames>-->
                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="pageTitle" Storyboard.TargetProperty="Style">
                            <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource SnappedPageHeaderTextStyle}"/>
                        </ObjectAnimationUsingKeyFrames>
 
                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="primaryColumn" Storyboard.TargetProperty="Width">
                            <DiscreteObjectKeyFrame KeyTime="0" Value="320"/>
                        </ObjectAnimationUsingKeyFrames>
                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="itemDetail" Storyboard.TargetProperty="Visibility">
                            <DiscreteObjectKeyFrame KeyTime="0" Value="Collapsed"/>
                        </ObjectAnimationUsingKeyFrames>
                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="itemListView" Storyboard.TargetProperty="ItemTemplate">
                            <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource NarrowListItemTemplate}"/>
                        </ObjectAnimationUsingKeyFrames>
                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="itemListView" Storyboard.TargetProperty="Margin">
                            <DiscreteObjectKeyFrame KeyTime="0" Value="20,0,0,0"/>
                        </ObjectAnimationUsingKeyFrames>
                    </Storyboard>
                </VisualState>
 
                <!--
                    When snapped and an item is selected the details display requires more extensive changes:
                     * Hide the master list and the column is was in
                     * Move item details down a row to make room for the title
                     * Move the title directly above the details
                     * Adjust margins and padding for details
                     * Use a different font for title and subtitle
                     * Adjust margins below subtitle
                 -->
                <VisualState x:Name="Snapped_Detail">
                    <Storyboard>
                        <!--<ObjectAnimationUsingKeyFrames Storyboard.TargetName="backButton" Storyboard.TargetProperty="Style">
                            <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource SnappedBackButtonStyle}"/>
                        </ObjectAnimationUsingKeyFrames>-->
                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="pageTitle" Storyboard.TargetProperty="Style">
                            <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource SnappedPageHeaderTextStyle}"/>
                        </ObjectAnimationUsingKeyFrames>
 
                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="primaryColumn" Storyboard.TargetProperty="Width">
                            <DiscreteObjectKeyFrame KeyTime="0" Value="0"/>
                        </ObjectAnimationUsingKeyFrames>
                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="itemListScrollViewer" Storyboard.TargetProperty="Visibility">
                            <DiscreteObjectKeyFrame KeyTime="0" Value="Collapsed"/>
                        </ObjectAnimationUsingKeyFrames>
                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="itemDetail" Storyboard.TargetProperty="(Grid.Row)">
                            <DiscreteObjectKeyFrame KeyTime="0" Value="1"/>
                        </ObjectAnimationUsingKeyFrames>
                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="itemDetail" Storyboard.TargetProperty="(Grid.RowSpan)">
                            <DiscreteObjectKeyFrame KeyTime="0" Value="1"/>
                        </ObjectAnimationUsingKeyFrames>
                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="titlePanel" Storyboard.TargetProperty="(Grid.Column)">
                            <DiscreteObjectKeyFrame KeyTime="0" Value="1"/>
                        </ObjectAnimationUsingKeyFrames>
                        <!--<ObjectAnimationUsingKeyFrames Storyboard.TargetName="itemDetailTitlePanel" Storyboard.TargetProperty="(Grid.Row)">
                            <DiscreteObjectKeyFrame KeyTime="0" Value="0"/>
                        </ObjectAnimationUsingKeyFrames>
                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="itemDetailTitlePanel" Storyboard.TargetProperty="(Grid.Column)">
                            <DiscreteObjectKeyFrame KeyTime="0" Value="0"/>
                        </ObjectAnimationUsingKeyFrames>-->
                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="itemDetail" Storyboard.TargetProperty="Padding">
                            <DiscreteObjectKeyFrame KeyTime="0" Value="20,0,20,0"/>
                        </ObjectAnimationUsingKeyFrames>
                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="itemDetailGrid" Storyboard.TargetProperty="Margin">
                            <DiscreteObjectKeyFrame KeyTime="0" Value="0,0,0,60"/>
                        </ObjectAnimationUsingKeyFrames>
                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="itemTitle" Storyboard.TargetProperty="Style">
                            <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource TitleTextStyle}"/>
                        </ObjectAnimationUsingKeyFrames>
                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="itemTitle" Storyboard.TargetProperty="Margin">
                            <DiscreteObjectKeyFrame KeyTime="0" Value="0"/>
                        </ObjectAnimationUsingKeyFrames>
                        <!--<ObjectAnimationUsingKeyFrames Storyboard.TargetName="itemSubtitle" Storyboard.TargetProperty="Style">
                            <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource CaptionTextStyle}"/>
                        </ObjectAnimationUsingKeyFrames>-->
                    </Storyboard>
                </VisualState>
            </VisualStateGroup>
        </VisualStateManager.VisualStateGroups>

Tra i vari stati notate come sia presente quello che si occupa di gestire lo snap. In questo esempio l'esecuzione in snap è semplificata perché l'app non implementa una struttura a navigazione, per cui verrà mostrato un elenco di post e basta. Nell'esempio fornito con la documentazione, più completo e complesso, l'app risponde al click sull'elemento mostrando il contenuto e attivando un pulsante back per tornare all'elenco post, sempre in modalità snap. In questi stati visuali si fa riferimento a due template per i controlli, uno chiamato DefaultListItemTemplate e uno NarrowListItemTemplate, ognuno dei quali da utilizzare a seconda dello stato visuale corrente. Eccoli, da inserire nelle risorse:

        <SolidColorBrush x:Key="BlockBackgroundBrush" Color="#FF557EB9"/>
        <!-- Used in Filled and Snapped views -->
        <DataTemplate x:Key="NarrowListItemTemplate">
            <Grid Height="80">
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="Auto"/>
                    <ColumnDefinition Width="*"/>
                </Grid.ColumnDefinitions>
                <Border Background="{StaticResource BlockBackgroundBrush}" Width="80" Height="80"/>
                <ContentControl Template="{StaticResource DateBlockTemplate}" Margin="-12,-12,0,0"/>
                <StackPanel Grid.Column="1" HorizontalAlignment="Left" Margin="12,8,0,0">
                    <TextBlock Text="{Binding Title}" MaxHeight="56" Foreground="White" TextWrapping="Wrap"/>
                    <TextBlock Text="{Binding Author}" FontSize="12" />
                </StackPanel>
            </Grid>
        </DataTemplate>
 
        <DataTemplate x:Key="DefaultListItemTemplate">
            <Grid HorizontalAlignment="Stretch" Width="Auto" Height="110" Margin="10,10,10,0">
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="Auto"/>
                    <ColumnDefinition Width="*"/>
                </Grid.ColumnDefinitions>
                <!-- Green date block -->
                <Border Background="{StaticResource BlockBackgroundBrush}" Width="110" Height="110" />
                <ContentControl Template="{StaticResource DateBlockTemplate}" />
                <StackPanel Grid.Column="1"  HorizontalAlignment="Left" Margin="12,8,0,0">
                    <TextBlock Text="{Binding Title}" FontSize="26.667" TextWrapping="Wrap"
                           MaxHeight="72" Foreground="White" />
                    <TextBlock Text="{Binding Author}" FontSize="18.667" />
                </StackPanel>
            </Grid>
        </DataTemplate>

Si fa riferimento ad un template chiamato DateBlockTemplate, che consente di formattare la data di pubblicazione del post in un modo simile a quello che avete visto nella figura precedente, che poi è ripreso dalla documentazione con qualche modifica. Tale template va in App.xaml per renderlo potenzialmente riutilizzabile:

        <ControlTemplate x:Key="DateBlockTemplate">
            <Canvas Height="86" Width="86"  Margin="8,8,0,8" HorizontalAlignment="Left" VerticalAlignment="Top">
                <TextBlock TextTrimming="WordEllipsis" TextWrapping="NoWrap" 
                     Width="Auto" Height="Auto" Margin="8,0,4,0" FontSize="32" FontWeight="Bold">
                          <TextBlock.Text>
                              <Binding Path="PubDate" Converter="{StaticResource dateConverter}" ConverterParameter="month"  />
                          </TextBlock.Text>
                </TextBlock>
 
                <TextBlock TextTrimming="WordEllipsis" TextWrapping="Wrap" 
                     Width="40" Height="Auto" Margin="8,0,0,0" FontSize="34" FontWeight="Bold" Canvas.Top="36">
                        <TextBlock.Text>
                            <Binding Path="PubDate" Converter="{StaticResource dateConverter}" ConverterParameter="day"  />
                        </TextBlock.Text>
                </TextBlock>
                <Line Stroke="White" StrokeThickness="2" X1="54" Y1="46" X2="54" Y2="80" />
 
                <TextBlock TextWrapping="Wrap" 
                     Width="20" Height="Auto" FontSize="{StaticResource ContentFontSize}" Canvas.Top="42" Canvas.Left="60">
                        <TextBlock.Text>
                            <Binding Path="PubDate" Converter="{StaticResource dateConverter}" ConverterParameter="year"  />
                        </TextBlock.Text>
                </TextBlock>
            </Canvas>
        </ControlTemplate>

A questo punto dobbiamo fare due cose: modificare la UI affinché sia in grado di essere più gradevole e adattabile agli stati e fare qualche considerazione sulla gestione degli stati a runtime.

Come ti stravolgo la User Interface

Uno dei punti cardine nella documentazione sopra citata è un paragrafo chiamato Creating a consistent look with styles. Useremo il codice mostrato lì per fare molte modifiche. Sostanzialmente definiremo una nuova UI per la pagina principale in questo modo:

  • testo per il nome del contenuto visualizzato
  • uno ScrollViewer con ListView, che mostra l'elenco dei post secondo i due template precedentemente definiti
  • uno ScrollViewer con controllo WebView per la visualizzazione dell'anteprima
  • lasceremo invariata l'app bar e le sue funzionalità

Devo, per forza di cose, dare per scontato che abbiate dimestichezza con XAML. Il codice non è complicato, lo è riassumere codice lungo e dettagliate spiegazioni. Lo XAML da inserire all'interno della Grid radice è il seguente:

        <!--
        This grid acts as a root panel for the page that defines two rows:
        * Row 0 contains the back button and page title
        * Row 1 contains the rest of the page layout
    -->
        <Grid Background="{StaticResource ApplicationPageBackgroundBrush}">
            <Grid.RowDefinitions>
                <RowDefinition Height="140"/>
                <RowDefinition Height="*"/>
            </Grid.RowDefinitions>
            <Grid.ColumnDefinitions>
                <ColumnDefinition x:Name="primaryColumn" Width="610"/>
                <ColumnDefinition Width="*"/>
            </Grid.ColumnDefinitions>
 
            <!-- Back button and page title -->
            <Grid x:Name="titlePanel" Grid.ColumnSpan="2">
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="Auto"/>
                    <ColumnDefinition Width="*"/>
                </Grid.ColumnDefinitions>
 
                <TextBlock x:Name="pageTitle" Grid.Column="1" Text="{Binding Feed.Title}" Style="{StaticResource GridTitleTextStyle}"/>
            </Grid>
 
            <!-- Vertical scrolling item list -->
            <ScrollViewer
            x:Name="itemListScrollViewer"
            AutomationProperties.AutomationId="ItemListScrollViewer"
            Grid.Row="1"
            Margin="-10,-10,0,0"
            Style="{StaticResource VerticalScrollViewerStyle}">
 
                <ListView
                x:Name="itemListView"
                AutomationProperties.AutomationId="ItemsListView"
                AutomationProperties.Name="Items"
                Margin="120,0,0,60"
                ItemsSource="{Binding Source={StaticResource itemsViewSource}}"
                SelectionChanged="ItemListView_SelectionChanged"
                ItemTemplate="{StaticResource DefaultListItemTemplate}"/>
 
            </ScrollViewer>
 
            <ScrollViewer
            x:Name="itemDetail"
            AutomationProperties.AutomationId="ItemDetailScrollViewer"
            Grid.Column="1"
            Grid.Row="1"
            Padding="70,0,120,0"
            DataContext="{Binding SelectedItem, ElementName=itemListView}"
            Style="{StaticResource VerticalScrollViewerStyle}">
 
                <Grid x:Name="itemDetailGrid">
                    <Grid.RowDefinitions>
                        <RowDefinition Height="Auto"/>
                        <RowDefinition Height="*"/>
                    </Grid.RowDefinitions>
 
                    <TextBlock x:Name="itemTitle" Text="{Binding Title}" 
                           Style="{StaticResource SubheaderTextStyle}">
                      <TextBlock.Transitions>
                        <TransitionCollection>
                            <ContentThemeTransition />
                        </TransitionCollection>
                      </TextBlock.Transitions>
 
                    </TextBlock>
                    <Border x:Name="contentViewBorder" BorderBrush="Gray" BorderThickness="2" 
                        Grid.Row="1" Margin="0,15,0,20">
                        <Grid>
                            <WebView x:Name="contentView" />
                            <Rectangle x:Name="contentViewRect" />
                        </Grid>
                    </Border>
                </Grid>
            </ScrollViewer>

Apparirà ora più chiaro come le varie fasi degli stati visuali influenzino controlli specifici, che ora vediamo nel codice con il loro nome. Si noti come alcuni controlli siano in binding con le proprietà di interesse della classe FeedItem la cui istanza costituisce la fonte dati della pagina. Infine si noti come il template assegnato in via prioritaria sia quello chiamato DefaultListItemTemplate. Questo cambierà a seconda dello stato visuale. Il controllo e la gestione degli stati è poi responsabilità del runtime.

Gli stati visuali a runtime

La classe LayoutAwarePage implementa di default tutto ciò che è necessario per gestire gli stati visuali a runtime, oltre all'infrastruttura per la navigazione. Nel nostro semplice esempio siamo fortunati perché non abbiamo necessità di fare altro, ma se la nostra app è composta da più pagine è necessario fare l'override di alcuni metodi e gestire il comportamento degli stati in fase di navigazione.

La documentazione offre tutto ciò che è necessario sapere per implementare correttamente queste caratteristiche.

Test

Potete ora divertirvi a testare l'app ruotando il simulatore, soprattutto capire il discorso dello snap. Il codice aggiornato è disponibile a questo indirizzo.

Alessandro

posted @ martedì 15 maggio 2012 21.14 | Feedback (0) | Filed Under [ Visual Basic Windows 8 & Metro Visual Studio 11 ]

martedì 8 maggio 2012

Passa a Visual Studio Premium con MSDN e risparmia il 15%

La maggior parte dei visitatori che passano dall'home page di Visual Basic Tips & Tricks hanno sicuramente notato, già da qualche giorno, la presenza di una promozione offerta da Microsoft che riguarda la possibilità di risparmiare il 15% sull'acquisto di un abbonamento MSDN con Visual Studio Premium e che permette di stare al sicuro da aumenti futuri del prezzo, oltre ai consueti benefit del programma.

A tal proposito Francesca Longoni di Microsoft Italia ha pubblicato tutti i dettagli dell'offerta in questo post disponibile nel blog di MSDN Italia. Nel post sono anche elencati i prodotti inclusi nell'abbonamento per il quale vale l'offerta ed è sicuramente qualcosa a cui dare un'occhiata!

Alessandro

posted @ martedì 8 maggio 2012 20.57 | Feedback (0) | Filed Under [ Visual Studio 2010 Visual Studio 11 ]

Aggiornamenti all'interfaccia in Visual Studio 11 RC

Qualcuno aveva espresso apprezzamento, ma la maggior parte della comunità degli sviluppatori ha inviato feedback a Microsoft sul fatto che l'interfaccia utente della beta di Visual Studio 11 presentasse troppe tonalità di grigio e icone di difficile individuazione, incluse quelle relative ai file di codice.

Il feedback più forte è stato quindi: ridateci i colori

E a quanto pare questo feedback è stato assolutamente ascoltato, dal momento che si apprende che una Release Candidate è in fase di preparazione e alcuni screen shot sono stati pubblicati nel blog di Visual Studio. In particolare, in questo post si possono vedere tutti i miglioramenti che sono stati fatti all'interfaccia dell'IDE presentando tutte le aree interessate dagli interventi "coloranti".

Ora il nuovo dibattito è: ci piace lo stile maiuscolo dei nomi dei menu oppure no?

Intanto il ritorno dei colori è sicuramente molto gradito, a livello personale non apprezzavo le sole tonalità di grigio. Va bene lo stile Metro, ma la produttività a volte è legata anche a un simbolo o a un colore.

Bravi

Alessandro

posted @ martedì 8 maggio 2012 20.51 | Feedback (0) | Filed Under [ Visual Studio 11 ]

giovedì 26 aprile 2012

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

Nel precedente post di questa serie abbiamo creato un'app in stile Metro per Windows 8 che legge i feed RSS dal Visual Basic Developer Center di MSDN e abbiamo cominciato a prendere confidenza con il pattern Async, con classi specifiche di WinRT e con l'esecuzione dell'app.

In questo post (il nr. 1500!) facciamo degli interessanti passi in avanti, implementando caratteristiche tipiche di Windows 8: lo Share contract e l'app bar.

Share contract

Come sapete le app Metro godono della possibilità di integrarsi profondamente con il sistema operativo e con altre app, condividendo informazioni. Questo è possibile attraverso i cosiddetti charm (condivisione, ricerca, impostazioni) attivabili passando il puntatore del mouse in basso a destra o usando il touch scorrendo dolcemente il dito dal lato destro del device. I charm sono ciò che vede l'utente, ma dal lato dello sviluppatore ci sono delle apposite API chiamate contracts.

Lo Share contract sostanzialmente consente all'app di condividere un contenuto o un'informazione con altre app o con il sistema. Pensate banalmente a una foto da voler condividere con un'app di social networking o con il client di posta.  

Ciò che ci proponiamo di raggiungere oggi, quindi, è la possibilità di condividere i contenuti scaricati dalla nostra app con altre app in grado di ricevere i contenuti stessi. Nota bene: lo Share contract può essere duplice, nel senso che un'app può sia inviare che ricevere contenuti. Noi implementeremo la sola condivisione, per semplicità di apprendimento.

Il namespace Windows.ApplicationModel.DataTransfer espone la classe DataTransferManager che offre gli strumenti adatti all'implementazione del contract. In altre parole:

  • ottenuta l'istanza della classe DataTransferManager, si ottiene l'istanza dei contenuti visualizzati
  • si sottoscrive l'evento DataTransferRequested
  • quest'ultimo viene scatenato quando l'utente attiva il charm di condivisione
  • si impostano le proprietà della condivisione e il contratto fa il resto

Il che si traduce nel seguente codice da digitare nel code-behind della nostra (per ora) unica pagina:

    'Some initialization values
    Private dataPackageTitle As String = "Default Title"
    Private dataPackageDescription As String = "Default Description"
    Private dataPackageUri As Uri = Nothing
 
    Private datatransferManager As DataTransfer.DataTransferManager
    Private Sub ShareSourceLoad()
        Try
            'Get the current view for sharing
            datatransferManager = DataTransfer.DataTransferManager.GetForCurrentView()
            'Subscribe to the DataRequested event which is raised when the user attempts to share a content
            AddHandler datatransferManager.DataRequested, AddressOf datatransferManager_DataRequested
        Catch
 
        End Try
    End Sub
 
    Private Sub datatransferManager_DataRequested(sender As DataTransferManager, e As DataRequestedEventArgs)
        Dim currentFeed = CType(Me.itemListView.SelectedItem, FeedItem)
 
        dataPackageTitle = currentFeed.Title
        dataPackageDescription = currentFeed.Content
        dataPackageUri = currentFeed.Link
 
        e.Request.Data.Properties.Title = dataPackageTitle
        e.Request.Data.Properties.Description = dataPackageDescription
 
        'Actual sharing here
        e.Request.Data.SetUri(dataPackageUri)
 
    End Sub

Come vedete, il gestore dell'evento assegna alcune proprietà dell'oggetto e.Request che è quello che attiva lo sharing nel momento in cui si invoca il metodo SetUri. A tal proposito è bene evidenziare che il metodo ShareSourceLoad va invocato nel costruttore:

    Public Sub New()
 
        ' This call is required by the designer.
        InitializeComponent()
 
        ' Add any initialization after the InitializeComponent() call.
        ShareSourceLoad()
    End Sub

In realtà la user interface relativa alla condivisione può essere invocata anche da codice, ragion per la quale aspettiamo di vedere in azione il codice precedente dopo aver implementato l'app bar. In ogni caso, con questi pochi passaggi Windows automaticamente fornirà l'elenco di applicazioni che sono in grado di ricevere il contenuto condiviso. Il che significa che Windows non consentirà di condividere un link con un'app per foto, facendo così un lavoro egregio.

"Vuole il menu o sa già?" - L'app bar e i comandi

E' chiaro che anche in un'app Metro ci deve essere la possibilità di offrire dei comandi attraverso dei pulsanti. Mentre nelle classiche applicazioni desktop abbiamo i menu (o recentemente il Ribbon), in Metro si ha la cosiddetta app bar che è una barra a scomparsa sulla quale definiamo i comandi da offrire.

Nel nostro caso metteremo due pulsanti: uno per visualizzare il contenuto del feed in un'anteprima Web migliore e uno per attivare lo share contract da codice (per fini didattici). A questo punto occorre fare un'importante considerazione: i pulsanti nell'app bar devono essere, chiaramente, in stile Metro con delle apposite icone.

Per fortuna Visual Studio 11 ci toglie da un gran bell'impiccio, fornendo tutte le icone di uso più comune direttamente in codice XAML contenuto nel file Common\StandardStyles.xaml.

Non serve riportare pedissequamente quel codice qui, vi basta esplorare il file. Degno di nota è il fatto che viene definito uno stile base chiamato AppBarButtonStyle, dal quale ereditano poi i vari altri stili.

Ovviamente non sempre gli stili standard soddisfano le nostre necessità di conseguenza è possibile definire degli stili personalizzati. Posto quindi che avremo due pulsanti, uno al quale assegneremo un'icona standard e uno al quale assegneremo un'icona custom, definiamo un'icona personalizzata per rappresentare la preview Web. Il codice è ripreso dalla documentazione che vi ho già linkato in precedenza e va inserito nelle risorse della pagina:

        <Style x:Key="WebViewAppBarButtonStyle" TargetType="Button" 
           BasedOn="{StaticResource AppBarButtonStyle}">
            <Setter Property="AutomationProperties.AutomationId" Value="WebViewAppBarButton"/>
            <Setter Property="AutomationProperties.Name" Value="View Web Page"/>
            <Setter Property="Content" Value="&#xE12B;"/>
        </Style>

Due cose: AutomationProperties.Name andrà ad identificare il pulsante con un identificatore, mentre Content rappresenta il modo in cui l'icona deve essere disegnata. Le possibilità di definizione della proprietà Value sono discusse nella documentazione.

L'app bar, per praticità, può essere definita prima della Grid principale ed è rappresentata da un oggetto AppBar che necessariamente va all'interno di un contenitore chiamato Page.TopAppBar se vogliamo che questa risieda nell'area superiore o Page.BottomAppBar se vogliamo che risieda in quella inferiore. Nel nostro caso è fatta così:

        <Page.TopAppBar>
            <AppBar Padding="10,0,10,0">
                <Grid>
 
                    <Button Click="ViewDetail_Click" HorizontalAlignment="Right" 
                    Style="{StaticResource WebViewAppBarButtonStyle}"/>
                    <Button Name="ShareButton" Click="ShareButton_Click_1" Style="{StaticResource MailAppBarButtonStyle}" HorizontalAlignment="Left"/>
                </Grid>
            </AppBar>
        </Page.TopAppBar>

Quindi come vedete l'app bar contiene due pulsanti definiti nel modo consueto, che puntano a due gestori di evento Click. Notate come lo style vada a fare riferimento a quello custom e a quello predefinito.

Passando ai gestori di evento, innanzitutto vediamo come sia possibile richiamare da codice il charm della condivisione con un metodo statico chiamato ShowShareUI:

    Private Sub ShareButton_Click_1(sender As Object, e As RoutedEventArgs)
        Dim currentFeed = CType(Me.ItemListView.SelectedItem, FeedItem)
 
        dataPackageTitle = currentFeed.Title
        dataPackageDescription = currentFeed.Content
        dataPackageUri = currentFeed.Link
 
        datatransferManager.ShowShareUI()
    End Sub

Si ottiene semplicemente l'istanza del feed, si popolano le proprietà, si invoca l'interfaccia grafica da codice. Poi si passa all'altro pulsante:

    Private Sub ViewDetail_Click(sender As Object, e As RoutedEventArgs)
        Dim selectedItem As FeedItem = Me.ItemListView.SelectedItem
        If selectedItem IsNot Nothing And Me.Frame IsNot Nothing Then
            RemoveHandler datatransferManager.DataRequested, AddressOf datatransferManager_DataRequested
            Me.Frame.Navigate(GetType(WebPreviewItemsPage), selectedItem)
        End If
    End Sub

L'oggetto Frame rappresenta il modo con cui controlliamo la navigazione tra pagine. Il suo metodo Navigate permette di aprire la pagina il cui nome/oggetto è il primo argomento, mentre il secondo rappresenta il dato da passare. Chiaramente la pagina di destinazione sarà definita a momenti, ma voglio prima sottolineare la necessità di rimuovere la sottoscrizione all'evento DataRequested poiché quando si ritorna alla pagina principale, il Runtime tenta di sottoscriverlo ancora e rimuoverla serve ad evitare eccezioni.

Navigazione tra pagine

Avete già sviluppato per Windows Phone? Bravi  Pur se con un'infrastruttura di oggetti diversa, in Metro esiste il concetto di pagina e di navigazione. Esiste un framework che permette di spostarsi tra pagine e che addirittura espone metodi per tornare alla pagina principale e di controllo sul fatto che tornare indietro sia possibile.

Aggiungiamo quindi al progetto un nuovo elemento di tipo Basic Page, un template nel quale viene ereditata la classe LayoutAwarePage che offre l'infrastruttura sopra citata:

Una volta creato il nuovo elemento con il nome come in figura, andiamo a definire lo XAML:

        <Grid Background="{StaticResource ApplicationPageBackgroundBrush}">
            <Grid.RowDefinitions>
                <RowDefinition Height="140"/>
                <RowDefinition Height="*"/>
            </Grid.RowDefinitions>
 
            <!-- Back button and page title -->
            <Grid>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="Auto"/>
                    <ColumnDefinition Width="*"/>
                </Grid.ColumnDefinitions>
                <Button x:Name="backButton" Click="GoBack" 
                    IsEnabled="{Binding Frame.CanGoBack, ElementName=pageRoot}" 
                    Style="{StaticResource BackButtonStyle}"/>
                <TextBlock x:Name="pageTitle" Grid.Column="1" 
                       Text="{Binding Title}" 
                       FontSize="26.667"
                       Margin="12,20,12,2"/>
            </Grid>
            <Border x:Name="contentViewBorder" BorderBrush="Gray" BorderThickness="2" 
                Grid.Row="1" Margin="120,15,20,20">
                <WebView x:Name="contentView" />
            </Border>
 
            <!-- Omissis .... -->
 
        </Grid>

Ho omesso tutta la parte relativa al Visual State Manager che Visual Studio ha generato. Qui è importante sottolineare ancora l'utilizzo del WebView per la visualizzazione e il fatto che il pulsante per tornare alla pagina precedente sia in binding con uno specifico metodo chiamato GoBack che non c'è necessità di implementare perché definito già nella classe base; stesso dicasi per il CanGoBack che abilita il pulsante se il suo stato è True. A questo punto dobbiamo dire alla pagina come trattare i dati che la pagina chiamante ha inviato.

Si fa l'override del metodo OnNavigatedTo, in un modo simile al seguente:

    Protected Overrides Sub OnNavigatedTo(e As Navigation.NavigationEventArgs)
        ' Add this code to navigate the web view to the selected blog post.
        Dim _feedItem As FeedItem = e.Parameter
        If _feedItem IsNot Nothing Then
            Me.contentView.Navigate(_feedItem.Link)
            Me.DataContext = _feedItem
        End If
    End Sub

L'oggetto e.Parameter riceve i dati dalla pagina chiamante; questi vengono convertiti in un oggetto FeedItem e il relativo link viene passato al controllo WebView. A questo punto possiamo finalmente testare l'app!

Test dell'app

Ecco quindi che tornano familiari le immagini presentate nel post precedente. L'app bar (che per i test col mouse potete attivare col tasto destro):

 E lo Share contract:

Vi invito a fare un test reale dello sharing, selezionando ad esempio il client di posta elettronica, per capire come la tecnica funzioni.

Conclusioni

In questo lunghissimo post abbiamo quindi visto due caratteristiche specifiche di Windows 8, ossia l'interazione con uno dei charm e l'app bar. In realtà manca un tassello, che è la definizione dell'orientamento dell'app in base alla rotazione del dispositivo, che vedremo nel prossimo post.

Download del codice

Ogni promessa è debito e poiché siete stati pazienti, questo è il link per scaricare il codice sorgente del progetto dimostrato finora. Preciso che il codice già contiene anche le modifiche che faremo nel prossimo post, così avrete una traccia utile da seguire.

Alessandro

posted @ giovedì 26 aprile 2012 23.26 | Feedback (0) | Filed Under [ Visual Basic Windows 8 & Metro Visual Studio 11 ]

lunedì 23 aprile 2012

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

posted @ lunedì 23 aprile 2012 20.03 | Feedback (0) | Filed Under [ Visual Basic Windows 8 & Metro Visual Studio 11 ]

Windows Phone: utilizzare le Bing API per cercare e mostrare immagini

Molte volte nelle proprie applicazioni, indipendentemente dalla piattaforma di sviluppo, si ha la necessità di eseguire ricerche e di presentare i risultati della ricerca in un modo appropriato all'applicazione stessa che non sia, quindi, all'interno di un'istanza del browser.

Microsoft, per il suo motore di ricerca Bing, mette a disposizione delle specifiche API che è possibile utilizzare per interrogare Bing e ottenere vari tipi di risultati, che siano ricerche di termini, di immagini, di video, ecc.

In questo post vediamo come utilizzare le API di Bing per ottenere un elenco di immagini da visualizzare in un'app per Windows Phone 7.

La prima cosa da fare per poter fruire delle Bing API è ottenere un codice identificativo per la propria applicazione. Questo si fa registrandosi, con il proprio Live ID, sul sito dedicato agli sviluppatori.

NOTA BENE: al momento le Bing API sono disponibili free, ma Microsoft ha iniziato un processo di migrazione delle stesse verso il Windows Azure Marketplace, il che vuol dire che tra un po' di tempo non saranno più gratuite.

In questo post tratterò il caso specifico della ricerca di immagini, per tutte le altre opportunità vi rimando alla documentazione ufficiale. I servizi di Bing possono restituire dati in vari formati, come JSON o XML. E' quest'ultimo formato che utilizzeremo insieme a LINQ.

Per questo esempio è sufficiente creare un progetto di tipo Windows Phone Application. Di tutte le informazioni che Bing restituisce, in questo caso ce ne interessano solo tre: il titolo dell'immagine, il suo URL e l'URL dell'anteprima; quest'ultima verrà visualizzata nella pagina dell'app. Dobbiamo quindi rappresentare queste informazioni con una classe:

 Public Class BingImage

    Public Property Title As String
    Public Property ImageUri As Uri
    Public Property ThumbnailUri As Uri
End Class

Ora andiamo a definire lo XAML che permette la visualizzazione delle anteprime. In questo particolare esempio siamo nell'unica pagina dell'app. Sicuramente utilizzeremo una ListBox, della quale dobbiamo ridefinire il template del contenitore che visualizza i dati. Una ottima scelta è il WrapPanel che viene fornito attraverso il Silverlight Toolkit per Windows Phone, che va quindi installato (non solo per seguire questo post, ma perché è un must have). Eccolo qui:

        <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
            <ListBox ItemsSource="{Binding}" x:Name="PhotoListBox"
                         SelectionChanged="PhotoListBox_SelectionChanged">
                <ListBox.ItemsPanel>
                    <ItemsPanelTemplate>
                        <toolkit:WrapPanel />
                    </ItemsPanelTemplate>
                </ListBox.ItemsPanel>
                <ListBox.ItemTemplate>
                    <DataTemplate>
                        <Image Margin="3" Width="96" Height="96" 
                                   Source="{Binding ThumbnailUri}"/>
                    </DataTemplate>
                </ListBox.ItemTemplate>
            </ListBox>
        </Grid>

Come vedete abbiamo ridefinito l'ItemsPanelTemplate sostituendo lo StackPanel di default con un WrapPanel. Quest'ultimo permetterà il posizionamento dinamico "a capo" delle thumbnail. Fatto questo abbiamo semplicemente definito l'ItemTemplate che contiene un'immagine, in binding con la proprietà ThumbnailUri della classe BingImage.

Andiamo ora nel code-behind della pagina e aggiungiamo subito le seguenti direttive Imports:

Imports System.Xml.Linq
Imports Microsoft.Phone.Tasks
Imports <xmlns:mms="http://schemas.microsoft.com/LiveSearch/2008/04/XML/multimedia">
Imports <xmlns="http://schemas.microsoft.com/LiveSearch/2008/04/XML/element">

Poiché andremo a lavorare con LINQ to XML, importare i namespace sopra citati diventa necessario per referenziare gli elementi correttamente. Nel codice della pagina principale cominciamo a stabilire l'id dell'applicazione e la query da eseguire:


    Const bingAppId As String = "YOUR_BING_APPLICATION_ID_GOES_HERE"
    Const bingQuery As String = "mare%20in%20inverno"

La query contiene i termini di ricerca, che vanno separati tra loro con uno spazio (%20). In questo caso la ricerca dovrà tirare fuori immagini relative a "mare in inverno". Abbiamo detto che tra i vari formati restituiti da Bing, c'è XML. Quindi utilizzeremo il classico meccanismo legato alla classe WebClient per scaricare il risultato della ricerca e analizzarlo con LINQ, in questo modo:

    Private Sub MainPage_Loaded(sender As Object, e As System.Windows.RoutedEventArgsHandles MyBase.Loaded
        Deployment.Current.Dispatcher.BeginInvoke(Sub()
                                                      Dim client As New WebClient
 
                                                      AddHandler client.DownloadStringCompleted,
                                                          Sub(sender1 As Object, e1 As DownloadStringCompletedEventArgs)
                                                              Dim doc = XDocument.Parse(e1.Result)
 
                                                              Dim query = (From img In doc...<mms:ImageResult>
                                                                          Select New BingImage With {.ImageUri = New Uri(img.<mms:MediaUrl>.Value, UriKind.Absolute),
                                                                                                     .ThumbnailUri = New Uri(img.<mms:Thumbnail>.<mms:Url>.Value, UriKind.Absolute),
                                                                                                     .Title = img.<mms:Title>.Value}).ToList
 
                                                              Me.PhotoListBox.ItemsSource = query
                                                          End Sub
 
                                                      client.DownloadStringAsync(New Uri("http://api.bing.net/xml.aspx?AppId=" + bingAppId + _
                                                                                         "&Query=" + bingQuery + _
                                                                                         "&Sources=Image&Version=2.0&Market=en-us&Adult=Moderate&Image.Count=20&Image.Offset=0",
                                                                                         UriKind.Absolute))
                                                  End Sub)
    End Sub

Notate come il contenuto XML venga convertito in un XDocument tramite il suo metodo Parse. Da qui in poi si fa una classica query (VB ha gli XML literals ) che andrà a generare una collection di oggetti BingImage, assegnata alla proprietà ItemsSource della ListBox.

La query string da inviare a Bing è composta da una parte prefissata, seguita dall'app Id e dai termini di ricerca. La parte finale permette di specificare, in modo molto intuitivo, la localizzazione (Market), filtri per adulti (Adult=Moderate) e il numero di immagini da includere nei risultati.

Giusto per dimostrare l'utilizzo di altri oggetti, facciamo in modo che al tocco di un'anteprima venga aperta l'immagine originale in un'istanza del browser, gestendo il SelectionChanged della ListBox:

    Private Sub PhotoListBox_SelectionChanged(sender As System.Object, e As System.Windows.Controls.SelectionChangedEventArgs)
        Dim webTask As New WebBrowserTask
        webTask.Uri = CType(Me.PhotoListBox.SelectedItem, BingImage).ImageUri
        webTask.Show()
    End Sub

In questo caso viene chiaramente sfruttata la proprietà ImageUri della classe BingImage. La proprietà Title può essere utile se l'immagine viene mostrata in un'altra pagina in cui si voglia mostrare il titolo e verrà passata in query string sul metodo di navigazione.

Il seguente è il risultato che otteniamo nella nostra app:

Il codice a corredo può essere scaricato da questo indirizzo dell'area Download di Visual Basic Tips & Tricks.

Alessandro

posted @ lunedì 23 aprile 2012 10.46 | Feedback (0) | Filed Under [ Visual Basic Silverlight e Windows Phone 7 ]

mercoledì 18 aprile 2012

Da Redmond: video "What's new in VB 11"

Chi meglio del grande Lucian Wischik poteva descrivere le novità di Visual Basic 11?

Lucian, Specification Lead di Visual Basic, ha realizzato un bellissimo video disponibile su Channel 9 a questo indirizzo.

Vi suggerisco anche la lettura del suo companion post, nel quale troverete anche i link al codice dimostrato e alle slide.

Riepilogo anche per comodità il post introduttivo alle novità di Jonathan Aneja e questo mio articolo sulle novità di VB 11 disponibile tra i nostri articoli.

Alessandro

posted @ mercoledì 18 aprile 2012 17.40 | Feedback (0) | Filed Under [ Visual Basic Visual Studio 11 ]

Powered by:
Powered By Subtext Powered By ASP.NET