1. Syndicated Services
Silverlight 3 and
later includes excellent support for easily consuming syndicated feeds
like RSS, ATOM, and formatted data feeds. Unfortunately, the System.ServiceModel.Syndication.dll
is not available in the Silverlight for Windows Phone 7 SDK. You can
simply add a reference to the Silverlight 3 version of the assembly, but
it is just as easy to use LINQ to XML to parse syndication feeds.
For the AdvancedDataBinding project, a page named SyndicatedServices.xaml is added to the View folder and a ViewModel named SyndicatedServicesViewModelViewModel folder. The SyndicatedServicesViewModel is added to the ViewModelLocator class in the ViewModel folder. is added to the
Clicking the syndicated services label shown in Figure 6-3 navigates to the page. The page is configured with a ListBox named feedListBox and added to the ContentPanel in SyndicatedServices.xaml. The Application Bar is enabled to have a single button named LoadFeedAppBarBtn with a title of "load feed."
In the code, the Click event
for the LoadFeedAppBarBtn Application Bar Button is wired up to fire an
event that makes an HttpWebRequest to the Educational Resources feed
from AppHub. Here is the code:
HttpWebRequest httpWebRequest;
private void LoadFeedAppBarBtn_Click(object sender, EventArgs e)
{
httpWebRequest = HttpWebRequest.CreateHttp(
"http://public.create.msdn.com/Feeds/CcoFeeds.svc/CmsFeed?group=Education Catalog List");
httpWebRequest.BeginGetResponse(new AsyncCallback(ReceiveFeedData), null);
}
The ReceiveFeedData method has code to parse the response, load the data into an XDocument, and parse the feed into a collection of FeedItem objects that is databound to the feedListBox.ItemsSource property. Listing 2 has the FeedItem class.
Example 2. FeedItem Class File
using System;
namespace AdvancedDataBinding { public class FeedItem { public string Title { get; set; }
public string Description { get; set; }
public Uri Link { get; set; } } }
Here is the code for the ReceiveFeedData method:
void ReceiveFeedData(IAsyncResult result) { HttpWebResponse response = httpWebRequest.EndGetResponse(result) as HttpWebResponse; using (StreamReader reader = new StreamReader(response.GetResponseStream())) { XDocument doc = XDocument.Parse(reader.ReadToEnd()); var items = from results in doc.Descendants("item") select new FeedItem { Title = results.Element("title").Value.ToString(), Link = new Uri(results.Element("link").Value.ToString(), UriKind.Absolute), Description = results.Element("description").Value.ToString() }; LayoutRoot.Dispatcher.BeginInvoke(() => { feedListBox.ItemsSource = items; }); } }
|
The response is read into a StreamReader object and the feedListBox.Dispatcher is used to marshal the data over to the UI thread for assignment to the ItemsSource property. A simple DataTemplate is configured on feedListBox.ItemTemplate that binds to FeedItem object properties:
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Margin="0,0,0,30">
<TextBlock Text="{Binding Title}" Margin="0,0,12,0" FontSize="24" FontFamily="Segoe WP Semibold" />
<TextBlock Text="{Binding Description}" />
<HyperlinkButton NavigateUri="{Binding Link}" FontFamily="Segoe WP SemiLight"
TargetName="_blank" FontSize="18.667" FontStyle="Italic"
Content="More..." HorizontalAlignment="Left"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
Figure 3 shows the UI when the Application Bar button is clicked.
If you click More... it loads the URL for the content into the Web Browser. Clicking the back button takes you back to the page. Notice in Figure 6-3 that the Description is formatted as HTML. I can change the DataTemplate to have a WebBrowser control instead of a TextBlock but when I do this, the HTML does not render.
The WebBrowser control
supports navigating to an HTML fragment using the NavigateToString
Method, but it does not have a simple HTML property that we can data
bind, too. We would rather not write a lot of code to data bind to the
HTML fragment. When working in Silverlight, whenever a control is
missing a property, you can always add it using XAML Attached
properties. The goal is to have a simple HTML property of type text for
the HTML fragment from the feed that gets loaded via the NavigateToString method. Listing 3 shows the attached property class.
Example 3. WebBrowserHTMLProp Code File
public static class WebBrowserHTMLProp { public static readonly DependencyProperty HtmlProperty = DependencyProperty.RegisterAttached( "Html", typeof(string), typeof(WebBrowserHTMLProp), new PropertyMetadata(OnHtmlPropChanged));
public static string GetHtml(DependencyObject dependencyObject) { return (string)dependencyObject.GetValue(HtmlProperty); }
public static void SetHtml(DependencyObject dependencyObject, string value) { dependencyObject.SetValue(HtmlProperty, value); }
private static void OnHtmlPropChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e) { var webBrowser = dependencyObject as WebBrowser;
if (webBrowser == null) return;
var html = e.NewValue.ToString(); webBrowser.NavigateToString(html); } }
|
It is mostly boilerplate code with the important code in the OnHtmlPropChanged method where the HTML is loaded into the WebBrowser via NavigateToString. Unfortunately, because this code is part of the DataTemplate, an error is thrown stating that "You cannot call WebBrowser methods until it is in the visual tree." Because the DataTemplate
is composited and then added to the Visual Tree, the Attached Property
does not help us here but it can assist in situations where the WebBrowser control is embedded into the static XAML. Instead a TextBlock is placed within the DataTemplate. Figure 6-3 shows the output. Clicking "More..." loads the link into IE Mobile.
An alternative approach for this feed would be to just display the Title in the ListBox and then navigate to another page that displays the Description details in a WebBrowser control that is part of the static XAML visual tree of the page.
In this section I covered how
to load an RSS feed using LINQ to XML. We will explore adding
additional functionality for displaying feed data, showing progress, and
lazy loading images in the following sections.