다음을 통해 공유


Databinding and the SyndicationFeed class

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.

The first property is a string and the second is Collection<SyndicationLink> . 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="https://schemas.microsoft.com/client/2007"
             xmlns:x="https://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

Comments