In the world of mobile devices content rules the ball. Each smartphone user wants on his device to get access to the information he needs quickly, beautifully and, preferably, with traffic savings.
On the other hand, every information resource owner wants his resource to be used. The simplest and most common option is RSS feeds. And a variety of RSS readers are available on all mobile platforms. There is even a service that can generate an application for you on some mobile platforms via an RSS or ATOM source, which allows a webmaster or resource owner to quickly access the army of mobile device users.
But we are developers and we are not looking for easy ways. Let's write a blank for the future mega-advanced RSS Reader for the Windows Phone platform, using all the features of the platform.
So, the original task. Write an RSS reader for the RSS blog feed (the example will be my blog on MSDN), which will display the list of posts and display the content of the post. Actually - that's all. Quite a workable billet for the future of advanced RSS Reader.
')
Let's start with an analysis of the task and the resources available to us. So, we need to download the RSS feed (XML), parse it, show it in the form of a list and choose the name of the post — display it (HTML).
WebClient copes with simple HTTP tasks. The list will help us display ListBox. LINQ to XML will help us parse XML into a list of objects. To link the list of objects with ListBox - DataBinding, beautifully to display - DataTemplate. It remains the display of HTML. The choice is simple - take advantage of the built-in WebBrowser control. And in order to conveniently display the contents of the post in WebBrowser, when moving from the list, we will save it to a file with a specific name on IsolatedStorage.
Well, this could be the end, but let's try to implement all our ideas in the code.
As usual, we will create a new project based on the Windows Phone Application template. Immediately add to it a link (Reference) to System.Xml.Linq, and the following entries to the using block:
using System.Xml.Linq; using System.IO; using System.IO.IsolatedStorage;
We need the first namespace to conveniently parse the resulting XML into a list of objects, and the other two to work with IsolatedStorage and StreamWriter.
We will determine the RSS feed of which blog we will display and what the file containing the text of the selected post will be called. I will use my blog and will simply call the file post.html:
const string blogRSSURL = "http://blogs.msdn.com/b/stasus/rss.aspx"; public static string postFileName = "post.html" bool isPageNew = false;
To access the file name from the second page on which I plan to display the selected post, I declare the name of the file public static. The isPageNew flag will be used to determine whether the constructor was called (i.e. whether the page was created) or it has already been created. This is useful to us in order to optimize calls to the server, as well as correctly handle the outputs from the Dormant and Tumbstone states.
To parse XML into a list of objects, you need to define an object. Add to the project a class file called PostMessage.cs, which will contain the minimum set of fields:
public class PostMessage { public DateTime pubDate { get; set; } public string title { get; set; } public string link { get; set; } public string description { get; set; } }
Let's go to the MainPage.xaml page, give the name of the application and the page, and add a ListBox with data binding, a template, and a handler for the change event of the selected item:
<StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="12,17,0,28"> <TextBlock x:Name="ApplicationTitle" Text=" " Style="{StaticResource PhoneTextNormalStyle}"/> <TextBlock x:Name="PageTitle" Text="" Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle1Style}"/> </StackPanel> <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0"> <ListBox Name="PostList" SelectionChanged="PostList_SelectionChanged"> <ListBox.ItemTemplate> <DataTemplate> <StackPanel> <TextBlock Text="{Binding pubDate, ConverterCulture=ru-RU, StringFormat=D}" FontSize="20" Foreground="Coral"/> <TextBlock Text="{Binding title}" TextWrapping="Wrap" FontSize="22"/> </StackPanel> </DataTemplate> </ListBox.ItemTemplate> </ListBox> </Grid>
Pay attention, I use formatting, presenting the date in the form I need when outputting to ListBox.
Now everything is ready with us and after loading the page we can request RSS and parse it into the list of objects.
Please note that RSS is downloaded only when we called the constructor. This means that when we exit the Dormant state, we will not download RSS, and when we exit Tumbstoned, we will. It also ensures that when we go back from the page of viewing the post to the list, we will not re-request RSS, which would have happened, since in this case the Loaded event occurs.
In this example, we use the simplest version of using WebClient, we ask you to download asynchronously the content as a string using the GET method with the specified URI and process the completion, converting the resulting RSS XML into a list of objects.
To display the post, add the PostPage.xaml page to the project, change the name of the application and the page on it, and add the WebBrowser control:
<StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="12,17,0,28"> <TextBlock x:Name="ApplicationTitle" Text=" " Style="{StaticResource PhoneTextNormalStyle}"/> <TextBlock x:Name="PageTitle" Text="" Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle1Style}"/> </StackPanel> <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0"> <phone:WebBrowser Name="blogPost"/> </Grid>
Let's go back to the MainPage.xaml.cs page code and add a selection change handler to the ListBox:
private void PostList_SelectionChanged(object sender, SelectionChangedEventArgs e) { if (e.AddedItems.Count > 0) { IsolatedStorageFile appStorage = IsolatedStorageFile.GetUserStoreForApplication(); IsolatedStorageFileStream postFileStrieam = appStorage.CreateFile(MainPage.postFileName); StreamWriter sw = new StreamWriter(postFileStrieam); sw.WriteLine("<html><head><meta http-equiv='Content-Type' content='text/html; charset=UTF-8' /></head><body>"); sw.Write(((PostMessage)e.AddedItems[0]).description); sw.WriteLine("</body></html>"); sw.Close(); postFileStrieam.Close(); NavigationService.Navigate(new Uri("/PostPage.xaml", UriKind.RelativeOrAbsolute)); } }
So, in the handler, we check that something is really selected. In the ListBox settings, we are allowed to select only one item (these are the default settings), so if only one is selected. Next, we cast the selected item in PostMessage, take the post text from it and save it to a file, wrapping a couple of lines, indicating the type of content and encoding.
Now it remains to display this file when opening the second page:
protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e) { base.OnNavigatedTo(e); blogPost.Navigate(new Uri(MainPage.postFileName, UriKind.RelativeOrAbsolute)); }
For a change, here I use the OnNavigaeTo method, which is called when I go to the page.
You can add by yourself:
- saving RSS in IsolatedStorage and displaying the saved RSS when the application starts;
- add ProgressBar when loading RSS;
- do not automatically update, but prompt the user;
- add Background Agent for automatic updates;
- add live tile to display latest news ...
Further improvements are limited only by your imagination.
In conclusion, I want to note that, as I promised, working with HTTP took 3 lines in the code.
UPD :
project code in the archiveUseful links:Live Tiles and Background AgentsWindows Phone Development Center on MSDN (Windows Phone course)Windows Phone SDK 7.1Forums on development for Windows Phone in Russian