Alessandro Del Sole's Blog

{ A programming space about Microsoft® .NET® }
posts - 1909, 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

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

Creare un aggregatore di feed RSS con VB 2008, WPF & LINQ - terza parte

Riprendiamo oggi il tutorial inerente la creazione di un aggregatore di feed RSS con Microsoft Visual Basic 2008, Windows Presentation Foundation e LINQ to Xml che ci porterà a ricostruire l’applicazione chiamata Custom WPF RSS Feed Aggregator che ho pubblicato su CodePlex, la community Microsoft dedicata all’open source, a questo indirizzo e del quale potete scaricare il codice sorgente. Oggi iniziamo a progettare l’interfaccia grafica, lavorando col linguaggio di markup XAML (eXtensible Application Markup Language) e dichiareremo, sempre da XAML, i gestori di evento la cui implementazione managed sarà di pertinenza di post successivi.

 

Se non l’avete ancora fatto, qui ci sono le puntate precedenti:

 

Prima parte del tutorial

 

Seconda parte del tutorial

 

L’obiettivo che ci proponiamo di raggiungere in questa fase è rappresentato dalla seguente figura, che riprende il designer come si presenta al termine delle operazioni che eseguiremo:

 

 

Tenete la figura sempre come riferimento, poichè spesso vi tornerà utile.

 

Dopo aver riaperto il progetto, fate doppio clic sul file Window1.xaml in Solution Explorer per aprire l’editor di codice XAML e il designer WPF. Come ormai sapete, in WPF il layout dei controlli è gestito nell’interfaccia attraverso i cosiddetti contenitori o panels. Ci sono diversi tipi di contenitori, ognuno dei quali permette l’arrangement dei controlli in modo diverso (Grid, Canvas, StackPanel, WrapPanel, DockPanel, ViewBox ecc.). Nella nostra applicazione il contenitore principale è una Grid, che si comporta come una sorta di tabella divisibile in righe e colonne, all’interno delle quali possono poi eventualmente insistere altri contenitori, secondo la logica gerarchica Xml che caratterizza XAML. La Grid principale è suddivisa essenzialmente in tre righe. La prima, contiene un controllo TextBlock per visualizzare un messaggio di testo. La seconda, contiene una serie di controlli per la gestione dei feed e la terza contiene i controlli per la navigazione (in particolare una TreeView e una ListView). Come potete vedere in figura, alcuni di questi controlli sfruttano un gradiente come colore di primo piano per il testo che mostrano. Ciò posto, in primo luogo definiamo una risorsa a livello di finestra che rappresenti il gradiente che potremo così definire una sola volta ma riutilizzare più volte.

 

Iniziamo quindi a scrivere codice XAML:

 

    <Window.Resources>

        <LinearGradientBrush StartPoint="0,0" EndPoint="0,1" x:Key="OrangeGradient">

            <GradientStop Color="Red" Offset="0"/>

            <GradientStop Color="Yellow" Offset="0.6"/>

            <GradientStop Color="Orange" Offset="1"/>

        </LinearGradientBrush>

    </Window.Resources>

 

L’elemento Window.Resources ci permette di definire risorse riutilizzabili nell’ambito della finestra che le contiene, ma non al suo esterno (per questo scopo, invece, si può ricorrere alle Application.Resources). Il gradiente è di tipo lineare (LinearGradientBrush) e, al contrario di quanto faremmo se lo definissimo nell’ambito di un controllo, circostanza in cui utilizzeremmo una proprietà x:Name, dobbiamo assegnare un identificatore al gradiente mediante una proprietà x:Key. Questo è richiesto nella definizione delle risorse e ci permetterà di richiamare la risorsa sia da XAML che da codice managed. Ciascun elemento GradientStop ci consente di impostare colori (Color) e posizioni (Offset) nel gradiente.

 

Come anticipato poco fa, la nostra Grid principale è suddivisa idealmente in tre righe. Quindi, affinchè la suddivisione non sia più ideale ma reale, dobbiamo scrivere il seguente codice:

 

    <Grid>

 

        <Grid.RowDefinitions>

            <RowDefinition Height="50"/>

            <RowDefinition Height="80"/>

            <RowDefinition/>               

        </Grid.RowDefinitions>

 

L’elemento Grid.RowDefinitions contiene tanti elementi RowDefinition quante sono le righe che costituiscono la griglia. Per le prime due righe abbiamo specificato l’altezza (Height). Quando non si specifica l’altezza, come nella terza, la riga stessa va a riempire tutto lo spazio disponibile rimanente.

 

Fatto questo, aggiungiamo un controllo TextBlock che mostri una semplice intestazione ma il cui colore di primo piano sia il gradiente definito in precedenza. Il seguente codice va digitato di seguito all’ultimo frammento:

 

        <TextBlock Grid.Row="0" Text="Developer Feeds Aggregator"

                   Foreground="{StaticResource OrangeGradient}"

                   FontSize="28" FontWeight="Bold" Name="HeaderText">         

          

            <TextBlock.Background>

                <LinearGradientBrush StartPoint="0,0" EndPoint="0,1">

                    <GradientStop Color="Black" Offset="0"/>

                    <GradientStop Color="Gainsboro" Offset="0.7"/>

                    <GradientStop Color="White" Offset="1"/>

                </LinearGradientBrush>

            </TextBlock.Background>          

       </TextBlock>

 

Il controllo è posizionato nella prima riga sfruttando una c.d. attached property (Grid.Row) ossia una proprietà di un oggetto dal quale il controllo in esame dipende. Il colore di primo piano è stato assegnato mediante una c.d. XAML Markup Extension, un particolare tipo di costrutto estremamente ricorrente in WPF in diverse situazioni. In questo caso, per fare riferimento a una risorsa a livello di finestra si utilizza il tag StaticResource seguito dal nome dell’oggetto da assegnare (OrangeGradient è stato assegnato al gradiente tramite x:Key, ricordate?).

 

Assegnamo una proprietà Name al controllo in modo tale da poterlo poi modificare da codice Visual Basic. La proprietà Name, nei controlli WPF, assegna un identificatore agli stessi. E’ importante sottolineare questo punto, poichè vi capiterà non di rado di vedere controlli senza nome ma che funzionano alla grande, grazie a tutta una serie di concetti tra i quali spiccano i routed events. L’elemento nidificato TextBlock.Background ci consente di specificare uno sfondo per il controllo. Se avessimo voluto un colore a tinta unita ci sarebbe bastato aggiungere una proprietà Background direttamente nella definizione del controllo TextBlock. Se invece vogliamo fare una cosa più articolata, come per esempio un gradiente che è costituito, a conti fatti, da un elemento Xml nidificato, dobbiamo fare per forza così. Ma non è un limite, è anzi un segno di grande potenza.

 

Passiamo ora alla seconda riga della griglia, ma le cose ora si complicano un pochino. Se guardate la figura, potete osservare come i controlli siano suddivisi su due piani. Ciò significa che la seconda riga della griglia principale, contiene, a sua volta, un’altra griglia suddivisa in due righe. Ma è più difficile a dirsi che a farsi. Di seguito al precedente codice, digitate il seguente che dichiara e suddivide in due righe di uguale altezza una griglia secondaria con sfondo nero:

 

                <Grid Grid.Row="1" Background="Black">

                    <Grid.RowDefinitions>

                        <RowDefinition Height="40"/>

                        <RowDefinition Height="40"/>

                    </Grid.RowDefinitions>

 

Nella prima riga di questa griglia, insistono alcuni controlli così strutturati:

 

·         un controllo Border, che disegna un bordo dalle dimensioni e dal colore specificato, intorno ai controlli che contiene;

o    un contenitore StackPanel, che consente di posizionare più controlli affiancati tra loro, sia in senso orizzontale che verticale (orizzontale in questo caso);

§  due Label, una che mostra un messaggio statico, una che mostrerà, aggiornandosi, il nome del sito attualmente visualizzato.

 

Tutto questo si traduce nel seguente XAML, nel quale ho aggiunto dei commenti per rendere più semplice la lettura:

 

            <!-- CornerRadius imposta la smussatura degli angoli,

                  BorderThickness lo spessore del bordo-->

            <Border Grid.Row="0" CornerRadius="1"  BorderThickness="2">

                <StackPanel Orientation="Horizontal">

                    <!--Non ci serve identificatore, Label statica-->

                    <Label Margin="5,0,0,0"

                    Foreground="White" FontWeight="Bold" Content="Currently viewing feeds from:"/>

 

                    <!--Diamo un identificatore, Label da aggiornare tramite VB-->

                    <Label x:Name="ViewingLabel" Margin="5,0,0,0" FontWeight="Bold" Content="none"

                           Foreground="{StaticResource OrangeGradient}">

                    </Label>

                </StackPanel>

                <!--Impostiamo un colore per il bordo.

                    Sfruttiamo un gradiente invece che un colore a tinta unita -->

                <Border.BorderBrush>

                    <LinearGradientBrush>

                        <GradientStop Color="Orange" Offset="0"/>

                        <GradientStop Color="Yellow" Offset="0.5"/>

                        <GradientStop Color="Orange" Offset="0.7"/>

                    </LinearGradientBrush>

                </Border.BorderBrush>

            </Border>

 

Anche in questo caso facciamo ricorso all’attached property per posizionare il Border nella prima riga di questa griglia secondaria. La proprietà Margin dei vari controlli stabilisce la distanza del singolo controllo dagli altri. Ora passiamo alla seconda riga di questa griglia. Dobbiamo implementare dei controlli che ci serviranno per permettere all’utente di aggiungere un nuovo sito/blog all’elenco esistente, di modo che questo sia personalizzabile tramite interfaccia, senza dover ricorrere alla modifica manuale del file WebSites.Xml. I controlli che ci occorrono sono:

 

·         un Border, come il precedente;

o    uno StackPanel in grado di affiancare più controlli;

§  Una ComboBox per selezionare la categoria di siti, una Label descrittiva, una TextBox per specificare l’autore del sito/blog, una Label descrittiva, una TextBox per specificare l’Url dei feed del sito/blog, un Button per eseguire l’operazione di aggiunta e una sorta di LinkLabel che indichi l’autore dell’applicazione e che, al clic, apra il browser predefinito sulla pagina Internet desiderata.

 

Iniziamo a scrivere il codice dei vari controlli, con esclusione della LinkLabel. Notate come sia specificato un identificatore per i soli controlli che hanno necessità di gestione a run-time:

 

            <Border Grid.Row="1" BorderThickness="2" CornerRadius="1">

                <StackPanel Orientation="Horizontal">

                    <Label Margin="10,0,0,0" Content="Add new feed:" FontWeight="Bold" Foreground="White"/>

                    <ComboBox Margin="5,5,5,5" Name="MainSitesComboBox" Loaded="MainSitesComboBox_Loaded" />

                    <Label Margin="5,0,0,0" Content="Author:" Foreground="White"/>

                    <TextBox Margin="5,0,0,0" Name="NewFeedAuthorTextBox" Width="150" Height="25" />

                    <Label Margin="5,0,0,0" Content="URL:" Foreground="White"/>

                    <TextBox Margin="5,0,0,0" Name="NewFeedUrlTextBox" Width="200" Height="25" />

                    <Button Width="100" Margin="5,5,5,5" Name="AddFeedButton" Content="Add new feed" 

                            Click="AddFeedButton_Click" />

 

Di questo codice va senz’altro evidenziata la possibilità di assegnare il nome del gestore di evento, che verrà scritto da codice Visual Basic, all’evento corrispondente. Per capirci, l’evento Loaded della ComboBox sarà gestito da una Sub MainSitesComboBox_Loaded, mentre l’evento Click del pulsante sarà gestito da una Sub AddFeedButton_Click. Nell’assegnare il nome del gestore di evento, avrete senz’altro notato che l’IntelliSense mostra un popup di questo tipo:

 

 

Se selezioniamo la voce <New event handler> e premiamo Tab, Visual Studio genera per noi automaticamente sia l’identificatore che il gestore di evento nel codice Visual Basic, lasciando a noi l’implementazione. Passiamo ora a definire una LinkLabel.

 

In WPF, al contrario di Windows Forms, non esiste un controllo LinkLabel. Tuttavia, lo scopo si può facilmente raggiungere. Infatti, una delle peculiari caratteristiche di Windows Presentation Foundation è quella che ci consente di aggregare tra loro controlli semplici per ottenere un controllo “composto”. Nel nostro caso, la LinkLabel si costruisce nidificando un controllo HyperLink (che rappresenta un collegamento ipertestuale) all’interno di una normale Label. Osservate il seguente codice (che va aggiunto al precedente), che fa uso anche degli stili:

 

                    <Label Margin="5,5,5,5">

                       

                        <!--Simple style for our Hyperlink-->

                        <Label.Resources>

                            <!—- Assegna un identificatore allo stile -->

                            <Style TargetType="Hyperlink" x:Key="HyperStyle">

                                <Setter Property="Foreground" Value="White"/>

                                <Style.Triggers>

                                    <Trigger Property="IsMouseOver" Value="True">

                                        <Setter Property="Foreground" Value="{StaticResource OrangeGradient}"/>

                                    </Trigger>

                                </Style.Triggers>

                            </Style>

                        </Label.Resources>

                       

                        <Hyperlink Style="{DynamicResource HyperStyle}"

                                   NavigateUri="https://mvp.support.microsoft.com/profile/Alessandro.Del%20Sole"

                                   Hyperlink.RequestNavigate="Hyperlink_RequestNavigate">                          

                            <TextBlock  Text="by Alessandro Del Sole"/>

                        </Hyperlink>

                    </Label>

                </StackPanel>

 

La Label contiene un HyperLink. In altre parole, invece di contenere del testo, contiene un altro controllo. Ciò è reso possibile dal ContentPresenter, l’elemento WPF che consente di specificare cosa deve essere visualizzato. Nell’HyperLink, il sito assegnato al collegamento è determinato dalla proprietà NavigateUri (in questo caso si tratta del link al mio profilo quale Microsoft MVP); il testo da visualizzare è rappresentato da un controllo TextBlock nidificato. Invece di gestire l’evento Click, come sarebbe naturale pensare, si gestisce un evento chiamato RequestNavigate, che vedremo tra breve tradotto in codice Visual Basic. E’ questa una peculiarità delle applicazioni WPF di tipo Client (quindi non Xaml Browser Applications). Da ultimo, all’HyperLink stiamo assegnando uno stile. In WPF gli stili consentono di definire, in un sol colpo, l’assegnazione di più proprietà che riguardano un determinato tipo di controllo e altre che vengono assegnate solo al verificarsi di talune circostanze (Triggers).

In questo estremamente semplice esempio di Style (definito solo per questa Label, e non per altre, nelle Label.Resources) si stabilisce il controllo destinatario (TargetType) quindi si utilizzano gli elementi Setter per impostare una proprietà (Property) con il relativo valore (Value). In questo caso abbiamo semplicemente assegnato un colore di primo piano bianco al controllo, ma avremmo potuto assegnare molte altre proprietà.

 

Ma noi vogliamo anche che il nostro HyperLink cambi colore quando il puntatore del mouse ci passa sopra. Quindi, il colore del controllo deve cambiare solo al verificarsi della condizione specificata, che è determinata dal passaggio a True della proprietà IsMouseOver. Il verificarsi di una condizione è gestito dai Triggers. Ci sono diversi tipi di Trigger in WPF, ma ci soffermiamo su questo tipo che è il property trigger. L’elemento Trigger dice al run-time che quando la proprietà IsMouseOver è uguale a True, l’elemento Setter che esso contiene deve assegnare alla proprietà Foreground del controllo HyperLink il colore di primo piano precedentemente definito come gradiente nelle risorse, sfruttando la stessa identica XAML markup extension utilizzata all’inizio. Tutto chiaro? J

 

Lo stile viene poi assegnato all’HyperLink mediante la proprietà Style, ancora una volta tramite XAML markup extension. Questa volta, però, invece che utilizzare un elemento StaticResource stiamo utilizzando un elemento DynamicResource. La differenza principale risiede nel fatto che, nel primo caso, si richiama una risorsa definita a livello globale (finestra o applicazione) mentre, nel secondo, si richiama una risorsa localizzata esclusivamente all’interno del controllo su cui si sta lavorando. Ora chiudiamo il Border assegnando lo stesso colore del precedente, quindi chiudiamo questa griglia secondaria:

 

                <Border.BorderBrush>

                    <LinearGradientBrush>

                        <GradientStop Color="Orange" Offset="0"/>

                        <GradientStop Color="Yellow" Offset="0.5"/>

                        <GradientStop Color="Orange" Offset="0.7"/>

                    </LinearGradientBrush>

                </Border.BorderBrush>

            </Border>

        </Grid>

 

Ora, se mi avete seguito attentamente dovrebbe venirvi naturale un’osservazione. Quale? Vi dò un aiutino: abbiamo utilizzato due volte lo stesso gradiente per due Border; non conveniva definire una risorsa una sola volta da richiamare poi tramite XAML markup extension? J Certamente, ve lo lascio come esercizio!

 

Aggiungiamo gli ultimi due controlli, poi vi lascio perchè sarete stanchi! La terza e ultima riga della griglia principale conterrà, indovinate.., un’altra Grid. Questa volta, però, la griglia sarà divisa in 2 colonne. La prima, a sinistra, conterrà una TreeView che verrà poi popolata con l’elenco dei siti suddivisi in categorie mentre la seconda, posta a destra, conterrà una ListView che mostrerà l’elenco degli ultimi post per ciascun sito. Il codice che realizza quanto detto è il seguente:

 

       <Grid Grid.Row="2">

           

            <Grid.ColumnDefinitions>

                <ColumnDefinition Width="200" />

                <ColumnDefinition/>

            </Grid.ColumnDefinitions>      

          

            <TreeView Grid.Row="1" Grid.Column="0" x:Name="SiteTreeView" />

           

            <ListView IsSynchronizedWithCurrentItem="True" Grid.Column="1" Grid.Row="1" x:Name="FeedListView"

                      ItemsSource="{Binding}" >

 

            </ListView>

        </Grid>

 

    </Grid>

 

Questa è solo l’ossatura, poiché, soprattutto con riferimento alla ListView, ci sono un po’ di cose da fare ma lo faremo la prossima volta. Per il momento ci limitiamo a dire che:

 

·         Una Grid si suddivide in colonne in modo analogo a come si suddivide in righe, sfruttando un elemento Grid.ColumnDefinitions ed elementi ColumnDefinition;

·         La proprietà ItemsSource della ListView è quella che contiene l’elenco degli elementi visualizzati. Potremmo associare una collezione di elementi già ben definiti anche in questa fase ma in uno scenario come questo, sfruttando l’elemento Binding sotto forma di markup extension, preferiamo dire a WPF che l’elenco sarà determinato tramite data-binding da codice Visual Basic e che, al momento, deve rimanere in sospeso;

·         La proprietà IsSynchronizedWithCurrentItem della ListView ci consentirà di essere sicuri che l’elemento selezionato sarà correttamente associato a quello corrispondente nella collection collegata.

 

Dopo tutto questo lavoro il designer dovrebbe presentarsi come nella figura presentata all’inizio del post. Direi che per oggi è veramente tutto. Nel prossimo post completeremo la progettazione della nostra ListView e inizieremo a vedere tanti altri interessanti concetti di Windows Presentation Foundation, che ci permetteranno di capire come questa tecnologia consenta di semplificarci la vita in tante situazioni.

 

Come sempre, sperando di non avervi annoiato, vi invito a lasciare i vostri feedback soprattutto se qualcosa non vi è chiaro.

 

Alessandro

Print | posted on mercoledì 12 novembre 2008 23:01 | Filed Under [ .NET Framework Visual Basic Visual Studio 2008 Windows Presentation Foundation LINQ ]

Powered by:
Powered By Subtext Powered By ASP.NET