1. Problem
You want to create a feed
reader about a service and follow the link attached to every news to see
the destination insinde the browser.
2. Solution
You must use an object of the WebClient class and open the link by using WebBrowserTask.
3. How It Works
For our application, we chose
an Atom feed made available by the British Broadcasting Corporation
(BBC), but it's clear that you can use all feeds in Atom format.
A feed is simply a document
made available on the Web (we hope always at the same address) in an XML
format that follows a specific syntax, described in this XSD:
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified"
xmlns:atom="www.atom.com">
<xs:import namespace="www.atom.com" schemaLocation="atom.xsd"/>
<xs:element name="rss">
<xs:complexType>
<xs:sequence>
<xs:element ref="channel"/>
</xs:sequence>
<xs:attribute name="version" type="xs:string"/>
<xs:attribute name="xmlns:atom" type="xs:string"/>
</xs:complexType>
</xs:element>
<xs:element name="channel">
<xs:complexType>
<xs:sequence>
<xs:element ref="link"/>
<xs:element ref="title"/>
<xs:element ref="link"/>
<xs:element ref="description"/>
<xs:element ref="language"/>
<xs:element ref="copyright"/>
<xs:element ref="item" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="title" type="xs:string"/>
<xs:element name="link" type="xs:string"/>
<xs:element name="description" type="xs:string"/>
<xs:element name="language" type="xs:string"/>
<xs:element name="copyright" type="xs:string"/>
<xs:element name="item">
<xs:complexType>
<xs:sequence>
<xs:element ref="title"/>
<xs:element ref="description"/>
<xs:element ref="link"/>
<xs:element ref="pubDate"/>
<xs:element ref="guid"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="pubDate" type="xs:string"/>
<xs:element name="guid" type="xs:string"/>
</xs:schema>
4. The Code
You can start with the
preceding XSDto generate classes that work to serialize/deserialize
according to the structure defined by the schema. And this is what you
are going to do now. Create three classes in your project with these
names:
In Rss.cs, include the following code:
[XmlRoot(ElementName="rss",Namespace="")]
public class Rss
{
private Channel _channel;
private string _version;
[XmlElement(ElementName="channel")]
public Channel Channel
{
get
{
return this._channel;
}
set
{
this._channel = value;
}
}
[XmlElement(ElementName = "version")]
public string Version
{
get
{
return this._version;
}
set
{
this._version = value;
}
}
}
The attributes used on class and properties say clearly that you are going to use XmlSerializer to deserialize the response in your classes and then go up with Channel.cs:
[DataContract]
public class Channel
{
private string _atomLink;
private string _title;
private string _link;
private string _description;
private string _language;
private string _copyright;
private Item[] _items;
[XmlElement(ElementName = "atom:link")]
public AtomLink AtomLink
{
get
{
return this._atomLink;
}
set
{
this._atomLink = value;
}
}
[XmlElement(ElementName = "title")]
public string Title
{
get
{
return this._title;
}
set
{
this._title = value;
}
}
[XmlElement(ElementName = "link")]
public string Link
{
get
{
return this._link;
}
set
{
this._link = value;
}
}
[XmlElement(ElementName = "description")]
public string Description
{
get
{
return this._description;
}
set
{
this._description = value;
}
}
[XmlElement(ElementName = "language")]
public string Language
{
get
{
return this._language;
}
set
{
this._language = value;
}
}
[XmlElement(ElementName = "copyright")]
public string Copyright
{
get
{
return this._copyright;
}
set
{
this._copyright = value;
}
}
[XmlElement(ElementName = "item")]
public Item[] Items
{
get
{
return this._items;
}
set
{
this._items = value;
}
}
}
And last but not least, here is Item.cs (this is the most important class in our project as it will contain the news items in the feed):
public class Item
{
private string _title;
private string _description;
private string _link;
private string _pubDate;
private string _guid;
[XmlElement(ElementName = "title")]
public string Title
{
get
{
return this._title;
}
set
{
this._title = value;
}
}
[XmlElement(ElementName = "description")]
public string Description
{
get
{
return this._description;
}
set
{
this._description = value;
}
}
[XmlElement(ElementName = "link")]
public string Link
{
get
{
return this._link;
}
set
{
this._link = value;
}
}
[XmlElement(ElementName = "pubDate")]
public string PubDate
{
get
{
return this._pubDate;
}
set
{
this._pubDate = value;
}
}
[XmlElement(ElementName = "guid")]
public string Guid
{
get
{
return this._guid;
}
set
{
this._guid = value;
}
}
e }
Now that you have prepared the containers for your data, it's time to retrieve them from the cloud by using the WebClient class.
In your MainPage.xaml, you define a list box that will show all links to news and the titles:
<ListBox x:Name="NewsList">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Margin="0,5,0,5">
<TextBlock FontFamily="Tahoma" FontWeight="Bold" Text="{Binding Title}" />
<HyperlinkButton Content="{Binding Link}" Click="HyperlinkButton_Click"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
The Click event of all HyperlinkButton controls created by the ItemTemplate will be handled by the HyperlinkButton_Click event handler, which will do the true work.
Then in the code-behind of your application, you will have the following:
...
WebBrowserTask webBrowserTask = null;
...
This declares at the class level your WebBrowserTask object, which will enable you to launch the web browser application.
As you can see in figure 1 the two most important members of this class are the URL property and the Show method. URL represents the target address, while Show shows the browser .
As with the other recipe, you do the work inside the PageLoaded event:
void MainPage_Loaded(object sender, RoutedEventArgs e)
{
WebClient wc = new WebClient();
wc.OpenReadCompleted += new OpenReadCompletedEventHandler(wc_OpenReadCompleted);
wc.OpenReadAsync(new Uri("http://feeds.bbci.co.uk/news/world/rss.xml"));
webBrowserTask = new WebBrowserTask();
}
You instantiate a WebClient and then subscribe to the OpenReadComplete event, which fires after OpenReadAsync completely executes. OpenReadComplete only deserializes the result of the request in your object:
...
void wc_OpenReadCompleted(object sender, OpenReadCompletedEventArgs e)
{
XmlSerializer xms = new XmlSerializer(typeof(Rss));
Rss des = xms.Deserialize(e.Result) as Rss;
NewsList.ItemsSource = des.Channel.Items;
}
...
At this point, all you have to do is to manage the click of the hyperlink button by the user opening the browser:
private void HyperlinkButton_Click(object sender, RoutedEventArgs e)
{
HyperlinkButton hpb = sender as HyperlinkButton;
webBrowserTask.URL = hpb.Content.ToString();
webBrowserTask.Show();
}
NOTE
There is a problem in WebBrowserTask that sometimes doesn't open the site; we have noticed this problem only when the device is connected to Zune.
5. Usage
From Visual Studio 2010,
press Ctrl +F5. The type of target is not important, as this recipe can
work as fine on the emulator as on the device. The application will
start, briefly showing the splash screen and then the main page. After a
few seconds (depending on your Internet connection), a list of news
items will display. Click the hyperlink button to access the Browse
function and open the link relative to the news.