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 - quarta parte

Dopo che il mio piccolo tool ha ricevuto l’onore di essere stato menzionato dalla grande Beth Massi del Visual Basic Team, riprendiamo, in questa quarta parte, il tutorial sulla creazione di un’applicazione WPF che ci sta portando alla ricostruzione dell’applicazione chiamata “Custom WPF RSS Feed Aggregator” da me pubblicata su CodePlex.

 

Nell’ultima puntata c’eravamo lasciati con la dichiarazione dei controlli TreeView e ListView rappresentando che, con riferimento a questo secondo controllo, avremmo dovuto implementare altre caratteristiche.

 

Riaprite il progetto, se non l’avete ancora fatto, e posizionatevi all’interno del file Window1.xaml, in particolare all’interno della definizione del controllo ListView. In primo luogo, dobbiamo stabilire in che modalità debbano essere visualizzati i dati (quindi l’elenco, per ciascun post, di titoli-data di pubblicazione-categoria).

La migliore modalità è quella a griglia, poichè potremo specificare delle colonne e relative intestazioni. In WPF, la ListView prende la forma di una griglia grazie al suo elemento nidificato GridView. La GridView, a sua volta, è suddivisa in tanti elementi GridViewColumn quante sono le colonne da implementare. Tuttavia, e questa è una grande peculiarità di WPF, per ciascuna cella di ciascuna colonna è necessario specificare la modalità di esposizione dei dati. Per esempio, posto che vorremo visualizzare il titolo di ciascun post estratto dai feed, è necessario stabilire in che modo il titolo di tale post venga visualizzato. Nel nostro caso, il titolo del post sarà mostrato sotto forma di collegamento ipertestuale. Ciò premesso, ciascuna cella relativa alla colonna dei post dovrà prevedere l’implementazione di un collegamento ipertestuale.

 

Come si fa? Semplice: attraverso i template, che pervadono WPF dall’inizio alla fine. Dovremo quindi definire il CellTemplate che, a sua volta, contiene, nidificato, un elemento DataTemplate che è quello che effettivamente contiene i controlli per la visualizzazione dei dati.

 

Dopo tante parole, veniamo ai fatti. La nostra ListView sarà suddivisa in tre colonne (titolo post, data di pubblicazione e categoria post). In primo luogo, stabiliamo che la ListView debba essere trattata come griglia e impostiamo il template per le celle della prima colonna:

 

           <ListView.View>

                <GridView AllowsColumnReorder="True">

                    <GridViewColumn Header="Post title" Width="320">

                        <GridViewColumn.CellTemplate>

                            <DataTemplate>

                                    <Label>

                                         <Hyperlink NavigateUri="{Binding Path=Url}"

                                                   Hyperlink.RequestNavigate="Hyperlink_RequestNavigate">

                                            <InlineUIContainer>

                                                     <TextBlock Text="{Binding Path=Title}" />

                                               </InlineUIContainer>

                                        </Hyperlink>

                                    </Label>

                                </DataTemplate>

                        </GridViewColumn.CellTemplate>

                    </GridViewColumn>

 

Notate come ciascuna colonna venga implementata tramite elemento GridViewColumn, la cui proprietà Header imposta il testo dell’intestazione. Poi vengono, rispettivamente, il CellTemplate e il DataTemplate. All’interno di quest’ultimo viene definito il controllo che mostrerà il titolo dei post. In particolare, stiamo implementando una LinkLabel personalizzata (simile a quella vista nel precedente post) il cui testo corrisponde a quello del titolo del post.

 

Nell’HyperLink stiamo facendo uso del databinding. Infatti, l’Url da assegnare al controllo è determinato, tramite la markup extension Binding Path, dalla proprietà Url di ciascun oggetto BlogPost appartenente alla collection di post che otterremo dall’interrogazione LINQ sui feed. Il testo dell’HyperLink, visibile tramite TextBlock, è determinato in modo analogo, tramite binding alla proprietà Title di ciascun oggetto BlogPost facente parte della stessa collezione. Notate, infine, come il gestore di evento RequestNavigate sia lo stesso che abbiamo previsto la scorsa volta, dal momento che quel codice può gestire ogni HyperLink nel modo appropriato.

 

La considerazione importante da fare è che il DataTemplate ci permette di utilizzare tantissime modalità di esposizione grafica dei dati, per esempio potremmo utilizzare uno StackPanel al cui interno insistano un controllo Image e dei controlli per mostrare testo e così via.

 

La seconda colonna della griglia funziona in modo analogo, ma mostra, come testo, solo la data di pubblicazione:

 

                    <GridViewColumn Header="Date published" Width="150">

                        <GridViewColumn.CellTemplate>

                            <DataTemplate>

                                <TextBlock Text="{Binding Path=DatePublished}"/>

                            </DataTemplate>

                        </GridViewColumn.CellTemplate>

                    </GridViewColumn>

 

In questo caso il data-binding ci permette di referenziare la proprietà DatePublished di ciascun BlogPost appartenente alla collection ottenuta e di assegnarla alla proprietà Text di un TextBlock. Analogo discorso vale per la visualizzazione della categoria a cui ciascun post appartiene:

 

                        <GridViewColumn Header="Category" Width="150">

                            <GridViewColumn.CellTemplate>

                                <DataTemplate>

                                    <TextBlock Text="{Binding Path=Category}"/>

                                </DataTemplate>

                            </GridViewColumn.CellTemplate>

                        </GridViewColumn>

                   </GridView>

            </ListView.View>

 

A questo punto il designer dovrebbe presentarsi come in figura, con le intestazioni di colonna correttamente aggiunte:

 

 

Con questi passaggi abbiamo concluso l’implementazione dell’interfaccia grafica. Tra un paio di puntate passeremo ad utilizzare Microsoft Expression Blend 2 per stilizzare alcuni controlli. Ora concentriamoci sulla scrittura di codice Visual Basic 2008 che renda operativa l’applicazione, aprendo l’editor di codice sul file Window1.xaml.vb.

 

A livello di classe, aggiungete la seguente dichiarazione che ci servirà per il data-binding della collezione contenente i post:

 

    'A repository of blog posts for data-binding

    Private PostCollection As CollectionView

 

La classe CollectionView ci fornisce un’infrastruttura per navigare, filtrare, ordinare e lavorare su una collezione. Ora scriviamo un metodo, che chiameremo PopulateTreeView, che, all’avvio dell’applicazione, legga dal file WebSites.xml l’elenco delle categorie e i siti che queste contengono, creando altrettanti nodi nella TreeView cosicchè potremo agevolmente sfogliare il contenuto di questo controllo per leggere le notizie dei nostri siti preferiti. Per facilitare la lettura, ho aggiunto dei commenti all’interno del metodo, che sfrutta delle iterazioni:

 

    Private Sub PopulateTreeView()

 

        Try

 

            'Reads the list of sites

            Dim SiteList = XDocument.Load("WebSites.xml")

 

            'Gets each category site (E.g. Microsoft)

            Dim query = From webSite In SiteList...<MainSite> _

                        Select webSite

 

            'For each site stored in the IEnumerable collection..

            For Each Sito In query

 

                'Instantiates a new TreeViewItem and assigns

                'its header with the category name

                Dim tempItem As New TreeViewItem

                tempItem.FontWeight = FontWeights.Bold

                tempItem.Header = Sito.@Name

 

                SiteTreeView.Items.Add(tempItem)

 

                'for each child site stored in each category

                For Each sottoSito In Sito...<Site>

                    'instantiates a new child treeviewitem

                    Dim tempChildItem As New TreeViewItem

 

                    tempChildItem.FontWeight = FontWeights.Normal

                    'the tag is the URL of the site's feed

                    tempChildItem.Tag = sottoSito.@URL

                    'The header is the name of the site/blog author

                    tempChildItem.Header = sottoSito.@Author

                    tempItem.Items.Add(tempChildItem)

                Next

            Next

 

 

        Catch ex As IO.FileNotFoundException

            MessageBox.Show("Probably WebSites.xml file is missing. The application will be ended.", "", MessageBoxButton.OK, MessageBoxImage.Error)

            Application.Current.Shutdown()

        Catch ex As Exception

        End Try

 

    End Sub

 

Notate come la prima query basata su LINQ ottenga, in primo luogo, l’elenco delle categorie dei siti sfruttando l’axis dei Descendants (...<). Per contraddistinguere le categorie dai siti, abbiamo, tra l’altro, specificato il grassetto per il font. Ci interessa quindi osservare come, per ciascun sito ottenuto, la proprietà Header di ciascun TreeViewItem rappresenti il nome del sito mentre la proprietà Tag contiene l’indirizzo Internet. Memorizzare l’Url nella Tag ci servirà per determinare il sito di cui recuperare i feed quando l’utente sfoglia la TreeView (come poi vedremo).

Se vi ricordate, avevamo implementato una ComboBox per contenere l’elenco delle categorie dei siti, da sfruttare per l’aggiunta di nuovi indirizzi tramite interfaccia. Sostanzialmente la Combo conterrà lo stesso elenco di categorie previsto nella TreeView e, affinchè ci sia esatta corrispondenza tra i due controlli, quando la Combo viene caricata per la prima volta l’elenco di categorie verrà prelevato, tramite LINQ-to-Objects, dalla TreeView ed assegnato alla proprietà ItemsSource della Combo, che è quella che contiene la sorgente dati del controllo:

 

    Private Sub MainSitesComboBox_Loaded(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs)

        'Queries the treeview to obtain main sites

        'The Header property is related to each main site's name

        Dim webSites = From item In SiteTreeView.Items _

                       Let realItem = DirectCast(item, TreeViewItem) _

                       Select realItem.Header

 

        'Our Combo is populated via the above collection of names

        MainSitesComboBox.ItemsSource = webSites

 

        'But when first loaded, we want the first of them to be shown

        MainSitesComboBox.Text = webSites.First.ToString

 

    End Sub

 

Quando interroghiamo la proprietà Items della TreeView (quindi l’elenco di elementi), nonostante l’inferenza del tipo sia attiva, ciascun elemento è determinato come Object dal compilatore, pertanto, tramite istruzione Let che assegna un identificatore temporaneo, dobbiamo eseguire la conversione esplicita di ciascun elemento ottenuto in TreeViewItem, del quale ci interessa la proprietà Header.

 

Al caricamento, la Combo deve mostrare, come primo elemento, il nome della prima categoria di siti (da qui l’uso del metodo extension First che restituisce il primo elemento di una sequenza).

 

Riscriviamo parzialmente il costruttore affinchè richiami il metodo PopulateTreeView all’avvio dell’applicazione e affinchè il titolo della finestra sia un po’ diverso:

 

    Public Sub New()

 

        ' This call is required by the Windows Form Designer.

        InitializeComponent()

 

        ' Add any initialization after the InitializeComponent() call.

        Me.Title = "{Developer} Feeds Aggregator - powered by Microsoft .NET Framework 3.5"

        HeaderText.Text = "{Developer} Feeds Aggregator"

 

        PopulateTreeView()

    End Sub

 

La modifica del titolo della finestra e del testo di benvenuto poteva essere fatto da XAML, ma per una mia personale comodità ho voluto aggiungere i caratteri speciali (parentesi graffe) da codice VB.

 

Per questa puntata ci fermiamo qui. Nel prossimo post vedremo come la ListView verrà popolata tramite data-binding e inizieremo a vedere come aggiungere, tramite LINQ, nuovi indirizzi al nostro file XML attraverso l’interfaccia.

 

Alessandro

Print | posted on sabato 15 novembre 2008 20:43 | Filed Under [ .NET Framework Visual Basic Visual Studio 2008 Windows Presentation Foundation LINQ ]

Powered by:
Powered By Subtext Powered By ASP.NET