I found a couple of nice databinding tricks with the SyndicationFeed class, while putting together a code sample. The sample has been posted on silverlight.net for some time now (click here and then look for "Syndication - RSS/Atom Feed Reader").
For those not familiar with it: databinding is the association of an instance of a type or collection with a WPF control. One-way bindings are useful if you update the type or collection programmatically, then the control will reflect the change automatically. Think about an app that gets search results from a web service and adds them to a collection. As the new search results get added to the collection, a list in the app's UI gets updated automatically. Two-way bindings are nice if you want to be able to keep the UI and underlying instance in sync, regardless of which one is being changed. Think about an app that maintains the user's information (name, address) in an instance of an object. You want to allow the user to update their data, so you populate the object instance properties and the UI updates automatically. Then the user types changes in the UI, and the object instance gets updated automatically.
With SyndicationFeed, our main databinding scenario involves one-way binding: you parse a feed into a SyndicationFeed instance, and you want to bind that to a UI list to display all the entries in the feed.
The first step is easy - bind the IEnumerable<SyndicationItem> collection SyndicationFeed.Items to a ListBox, by adding this in the XAML:
<ListBox x:Name="itemsList" ItemsSource="{Binding}" /
>
and this in the code-behind, assuming feed is an instance of SyndicationFeed:
// Set up databinding for list of items
itemsList.DataContext = feed.Items;
Now every item in the ListBox has an associated SyndicationItem. We create a DataTemplate to define the shape of each item in the ListBox.
<ListBox x:Name="itemsList" ItemsSource="{Binding}">
<ListBox.ItemTemplate>
<DataTemplate>
<HyperlinkButton Content="{Binding Title.Text}}"
NavigateUri="{Binding Links, Converter={StaticResource linkFormatter}}" />
<TextBlock Text="{Binding Summary.Text, Converter={StaticResource htmlSanitizer}}" />
DataTemplate>
ListBox.ItemTemplate>
ListBox>
There are three uses of databinding to explore here. One thing to keep in mind that by default all bindings are one-way.
HyperlinkButton.Content to SyndicationItem.Title.Text
Both properties are of type string so setting up the binding is simple.
HyperlinkButton.NavigateUri to SyndicationItem.Links
The first property is a string and the second is Collection. This binding won't work and we're not allowed to index into Links in a binding: only "dotting down" is supported.
Bindings support "converters", which allow us to map between the two properties in the binding. Here we define the linkFormatter converter, which simply takes the first link in the collection and returns that.
public
class LinkFormatter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
// Get the first link - that's the link to the post
return ((Collection<SyndicationLink>)value)[0].Uri;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
To use the converter in XAML, we declare it as a resource in the page, which causes it to be instantiated at runtime. Notice that we name the instance of the LinkFormatter class "linkFormatter", which is what we use in the binding itself.
<
UserControl x:Class="SyndicationFeedReader.Page"
xmlns="http://schemas.microsoft.com/client/2007"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:SyndicationFeedReader"
>
<UserControl.Resources>
<local:HtmlSanitizer x:Key="htmlSanitizer"/>
<local:LinkFormatter x:Key="linkFormatter"/>
UserControl.Resources>
TextBlock.Text to SyndicationItem.Summary.Text
In this case both properties are of type string, so the converter trick is not really needed. However, here we use the converter to strip out HTML markup and clean up the text to display. Again, the converter class needs to be declared as a resource, as shown above.
public
class HtmlSanitizer : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
// Remove HTML tags and empty newlines and spaces
string returnString = Regex.Replace(value as string, "<.*?>", "");
returnString = Regex.Replace(returnString, @"\n+\s+", "\n\n");
// Decode HTML entities
returnString = HttpUtility.HtmlDecode(returnString);
return returnString;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
Hope this trick is useful!
Yavor Georgiev
Program Manager
Connected Framework