posts - 315, comments - 268, trackbacks - 15

My Links

News

View Pietro Libro's profile on LinkedIn

DomusDotNet
   DomusDotNet

Pietro Libro

Tag Cloud

Article Categories

Archives

Post Categories

Blogs amici

Links

WPF + LINQ + CollectionViewSource = Rss Reader

Questo post è nato con uno scopo diverso da quello dell'implementazione di un Rss Reader, dato che, è sufficiente cercare su internet per trovarne di belli e pronti, ma per enfatizzare alcune proprietà molto interessanti dei controlli WPF, come  la possibilità di eseguire il raggruppamento dei dati nei controlli ListBox o ListView. Piccola parentesi: per chi volesse conoscere in dettaglio le specifiche RSS 2.0 le può trovare qui. Iniziamo sviluppando un set di classi come riassunto dal seguente Class Diagram:

image

Nel resto dell'esempio saranno prese in considerazione solo istanze di tipo Rss e RssItem. Generalmente, senza considerare gli elementi opzionali, la struttura xml di un documento RSS è il seguente:

1 <rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" ... 2 <channel> 3 <title>Il blog di Pietro Libro</title> 4 <link>http://blogs.ugidotnet.org/PietroLibroBlog/Default.aspx</link> 5 <description /> 6 <language>it-IT</language> 7 <copyright>Pietro Libro</copyright> 8 <managingEditor></managingEditor> 9 <generator>Subtext Version 1.9.5.176</generator> 10 ... 11 <item> 12 <title>Adeona, il software &amp;quot;salva&amp;quot; notebook</title> 13 <category>OT</category> 14 <category>Varie ed eventuali</category> 15 <link>http://blogs.ugidotnet.org/PietroLibroBlog/archive/2008/08/26/93820.aspx</link> 16 <description>...</description> 17 .... 18 </item>

Per leggere un tale documento, utilizziamo un'istanza della classe XDocument, presente nel namespace System.Xml.Linq, contenente metodi che permettono la navigazione dei nodi del documento in maniera molto semplice. Se a questo aggiungiamo la potenza di Linq, veramente c'è poco da dire. Prima di tutto creiamo un'istanza  di XDocument e leggiamo il documento passando direttamente l'uri del fornitore del feed RSS:

1 XDocument xDocument = new XDocument(); 2 xDocument = XDocument.Load(blogUri);

Per recuperare tutti gli elementi item contenuti nel documento, utilizziamo Linq. Per far questo utilizziamo il metodo Descendants passando come parametro la stringa "Item":

1 _items = 2 (from item in xDocument.Descendants("item") 3 select new RssItem 4 { 5 Title = item.Element("title").Value, 6 Description =item.Element ("description").Value, 7 Category = item.Element("category") == null ? "Nessuna Categoria" : item.Element("category").Value , 8 Link = item.Element("link") == null ? "": item.Element("link").Value 9 }).ToList<RssItem>();

 _items è una lista tipizzata di oggetti RssItem.

Per presentare i dati recuperati  utilizziamo una Window WPF. Il nostro obiettivo è mostrare i dati raggruppati per categoria, ovvero ottenere qualcosa del tipo:

image

Per raggiungere il goal,  invece di utilizzare la proprietà ItemSource esposta dal controllo ListBox, utilizziamo un'istanza di oggetto di tipo CollectionViewSource la quale altro non è una che un Proxy della classe CollectionView. Quest'ultima permette di eseguire operazioni di raggruppamento, ordinamento, navigazione e ricerca su di una collezione di dati. Definiamo un'istanza di tipo CollectionViewSource nella collezione Window.Resources della nostra Window WPF:

1 <CollectionViewSource x:Key="cvs"> 2 <CollectionViewSource.GroupDescriptions> 3 <PropertyGroupDescription PropertyName="Category"/> 4 </CollectionViewSource.GroupDescriptions> 5 </CollectionViewSource>

Così facendo, aggiungiamo alla collezione GroupDescriptions un oggetto di tipo GroupDescription che descrive il criterio in base al quale i  dati devono essere raggruppati nella vista, nello specifico in base al valore della proprietà Category.

Tramite codice XAML, andiamo a descrivere come devono essere visualizzati i nostri Gruppi:

1 <Style x:Key="CustomGroupStyle" TargetType="{x:Type GroupItem}"> 2 <Setter Property="Margin" Value="0,0,0,5"/> 3 <Setter Property="Template"> 4 <Setter.Value> 5 <ControlTemplate TargetType="{x:Type GroupItem}"> 6 <Expander IsExpanded="False" BorderBrush="#FFA4B97F" BorderThickness="0,0,0,1"> 7 <Expander.Header> 8 <DockPanel> 9 <TextBlock FontWeight="Bold" Text="{Binding Path=Name}"/> 10 <TextBlock FontWeight="Bold" Text="{Binding Path=ItemCount, 11 Converter={StaticResource CustomConverter}, 12 ConverterParameter=' - Numero di post ({0}) '}"/> 13 </DockPanel> 14 </Expander.Header> 15 <Expander.Content> 16 <ItemsPresenter /> 17 </Expander.Content> 18 </Expander> 19 </ControlTemplate> 20 </Setter.Value> 21 </Setter> 22 </Style>

E gli elementi in essi contenuti:

1 <DataTemplate x:Key="ItemTemplate"> 2 <StackPanel> 3 <TextBlock Text="{Binding Path=Title}"></TextBlock> 4 <TextBlock> 5 <Hyperlink NavigateUri="{Binding Path=Link}" Click="Hyperlink_Click">Visualizza</Hyperlink> 6 </TextBlock> 7 </StackPanel> 8 </DataTemplate>

Non resta che definire il controllo ListBox:

1 <ListBox Name="lstBox" ItemTemplate="{StaticResource ItemTemplate}" Margin="12,41,12,12"> 2 <ListBox.GroupStyle> 3 <GroupStyle ContainerStyle="{StaticResource CustomGroupStyle}"/> 4 </ListBox.GroupStyle> 5 </ListBox>

Utilizzando un controllo Button per recuperare il contenuto del documento RSS, scriviamo il seguente codice C#:

1 LinqRss.Rss rss = new LinqRss.Rss(); 2 if (rss.Load("http://blogs.ugidotnet.org/PietroLibroBlog/Rss.aspx")) 3 { 4 CollectionViewSource collectionViewSource = this.FindResource("cvs") as CollectionViewSource ; 5 collectionViewSource.Source = rss.Items; 6 lstBox.ItemsSource = collectionViewSource.View; 7 }

In sintesi, recuperata l'istanza di CollectionViewSource contenuta nelle risorse della nostra Window WPF,  impostiamo la proprietà Source dell'oggetto CollectionViewSource utilizzando la collezione Items esposta dall'istanza Rss. Non resta che impostare la proprietà ItemSource del controllo ListBox con la proprietà View esposta da CollectionViewSource. Osservazione: i GroupItem sono costituiti da controlli Expander. Nell'ItemTemplate è presente anche un HyperLink che permette la navigazione verso il post d'interesse.

Codice C# e XAML

Technorati Tag:

Print | posted on mercoledì 27 agosto 2008 16:30 | Filed Under [ LINQ WPF ]

Feedback

Gravatar

# re: WPF + LINQ + CollectionViewSource = Rss Reader

Ciao. La prorietà Name non si riferisce ad una proprietà dell'oggetto su cui stai effettuando il Binding, ma alla proprietà PropertyName del PropertyGroupDescription. Nell'esempio ho fatto il Grouping sulla proprietà Category del mio oggetto, quindi nel dock dell'expander mi trovo OT, WPF, LINQ etc...Spero di essere stato chiaro.
02/09/2008 15:15 | Pietro Libro
Gravatar

# re: WPF + LINQ + CollectionViewSource = Rss Reader

Ci sono arrivato.... "Name" è la proprietà che rappresenta il nome del gruppo.....
Ciao - Grazie
FabioG
02/09/2008 15:16 | Fabio GRANDE
Gravatar

# re: WPF + LINQ + CollectionViewSource = Rss Reader

Abbiamo postato in contemporanea....
Grazie per l'attenzione
Ciao - FabioG
02/09/2008 15:17 | Fabio GRANDE
Gravatar

# re: WPF + LINQ + CollectionViewSource = Rss Reader

Una precisazione a quanto postato precedentemente. Quando si effettua il Grouping, il View dell'oggetto CollectionViewSource, nello specifico un'instanza di ListCollectionView, espone la proprietà Groups, una
collezzione di oggetti CollectionGroupViewInternal, i quali a loro volta espngono le proprietà Name ed
ItemCount, bindate con i Dock degli expander. Spero di essermi spiegato meglio.
02/09/2008 15:31 | Pietro Libro
Gravatar

# re: WPF + LINQ + CollectionViewSource = Rss Reader

@Fabio GRANDE
>Abbiamo postato in contemporanea....

Direi di si...alla prossima. Ciao.
02/09/2008 15:32 | Pietro Libro
Gravatar

# re: WPF + LINQ + CollectionViewSource = Rss Reader

Ok, grazie.
Penso che adotterò il sistema più rapido e indolore : la proprietà custom...
Ciao - Grazie per l'attenzione
FabioG
02/09/2008 17:26 | Fabio GRANDE
Comments have been closed on this topic.

Powered by:
Powered By Subtext Powered By ASP.NET