(Tạp chí Lập trình) – Trong bài viết này tôi sẽ hướng dẫn các bạn xây dựng một ứng dụng đọc tin RSS. Nếu bạn còn chưa quen thuộc với RSS thì xin đọc qua ở đây trước khi chúng ta tiếp tục với bài viết.

Khi xây dựng ứng dụng đọc tin RSS, chúng ta sẽ sử dụng những kỹ thuật sau:

  1. Thiết kế giao diện và xử lý sự kiện với XAML và C#.
  2. Kết nối internet để tải về tập tin RSS.
  3. Sử dụng XML Serializer để chuyển từ dữ liệu XML sang các đối tượng trong C#.
  4. Sử dụng Data Binding để gắn dữ liệu lên các thành phần của XAML.
  5. Dùng dịch vụ NavigationService để di chuyển giữa các trang của ứng dụng.

Cấu trúc file RSS

Chúng ta thử xem qua một file RSS mẫu để biết được cấu trúc chính của nó (ở đây ta đang nói đến phiên bản 2.0):

[source lang=”xml”]
<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:media="http://search.yahoo.com/mrss/" xmlns:bc="http://www.brightcove.tv/link" xmlns:atom="http://www.w3.org/2005/Atom" xml:base="http://www.goupstate.com" xmlns:xlink="http://www.w3.org/1999/xlink">
<channel>
<title>Local News from Spartanburg Herald Journal</title>
<link>http://www.goupstate.com</link>
<description>Local News from Spartanburg Herald Journal</description>
<item>
<title><![CDATA[Spartanburg Mayor Junie White files for re-election ]]></title>
<link><![CDATA[http://www.goupstate.com/article/20130508/articles/130509720]]></link>
<description><![CDATA[
Spartanburg Mayor Junie White filed to run for re-election this afternoon.
]]></description>
</item>
<item>
<title><![CDATA[Chapman goes to court after softball protest denied]]></title>
<link><![CDATA[http://preps.goupstate.com/news/article/45809/chapman-goes-to-court-to-after-softball-protest-denied/]]></link>
<description><![CDATA[
Chapman High School and Spartanburg School District 1 have petitioned the 7th Judicial Circuit for a temporary restraining order and declaratory relief after the South Carolina High School League denied a protest to allow the completion of the Panthers’ road game at Daniel in the 3A upper state softball playoffs.
]]></description>
</item>
</channel>
</rss>

[/source]

Ta nhận thấy rằng một file rss thực ra là một tài liệu xml với thẻ root ngoài cùng là <rss>. Thẻ <rss> chỉ có một thẻ con duy nhất đó là thẻ <channel> đại diện cho một kênh cung cấp tin rss, ta sẽ liệt kê các thẻ con của thẻ <channel> này.

Thẻ <channel> có các thẻ con bắt buộc là:

<title>: Tiêu đề của kênh tin tức.

<link>: Đường dẫn đến trang web có chứa rss này.

<description>: Mô tả của kênh tin tức.

Một thẻ <channel> có thể chứa nhiều thẻ con <item>, mỗi thẻ <item> đại diện cho một tin tức của trang web đó. Tất cả các thẻ con của thẻ <item> đều là tùy chọn (không bắt buộc), tuy nhiên, thông thường thì nó chứa các thành phần sau:

<title>: Tiêu đề của tin tức.

<link>: Đường dẫn đến tin tức.

<description>: Đoạn tóm tắt của tin tức.

Ngoài những thẻ mà chúng ta đã liệt kê ở trên thì <channel><item> còn có thể có thêm nhiều thẻ tùy chọn khác nữa, các bạn hãy ghé qua trang này để biết thêm chi tiết về các thẻ được dùng trong rss.

Các lớp ViewModel

Với cấu trúc của một file rss như đã phân tích ở trên, tôi đã xây dựng các lớp ViewModel như sau:

Lưu ý là tôi có gắn thêm attribute cho các thuộc tính của mỗi lớp này nhằm phục vụ cho việc Serialize sau này, mã nguồn chi tiết các bạn có thể tải về ở cuối bài viết.

Tải dữ liệu về

Để tải về file RSS từ một nguồn trên mạng thì tôi tạo ra lớp RssLoader với phương thức LoadRss().

[source lang=”csharp”]
public class RssLoader
{
public static string RSS_URI = "http://kienthuclaptrinh.vn/feed/";
public event Action<RssModel> RssLoaded;
public void LoadRss()
{
WebRequest request = WebRequest.Create(RSS_URI);
request.BeginGetResponse(DownloadCompleted, request);
}
private void DownloadCompleted(IAsyncResult result) {
WebRequest request = (WebRequest)result.AsyncState;
WebResponse response = request.EndGetResponse(result);
using (Stream stream = response.GetResponseStream()) {
XmlSerializer serializer = new XmlSerializer(typeof(RssModel));
RssModel rss = (RssModel)serializer.Deserialize(stream);
if (RssLoaded != null) {
RssLoaded(rss);
}
}
}
}

[/source]

Có một lưu ý ở đây đó là trong .NET 4.5 đã được đưa vào mô hình lập trình bất đồng bộ, và do đó trong trường hợp này chúng ta cũng tiến hành việc tải dữ liệu bất đồng bộ. Tiếp theo chúng ta sử dụng XmlSerializer để chuyển dữ liệu từ xml thành các đối tượng tương ứng trong C#. Event RssLoaded là nhằm xử lý một RssModel khi việc tải dữ liệu về hoàn tất và đối tượng RssModel được xây dựng xong, sau này chúng ta sẽ sử dụng event này để đưa RssModel này lên giao diện của ứng dụng.

Thiết kế giao diện

Chúng ta sẽ thiết kế giao diện trang chính của ứng dụng với bố cục như sau:

Chúng ta sử dụng TextBlock để hiển thị tên ứng dụng, tiêu đề của trang RSS và mô tả của nó.

Còn đối với danh sách bài viết thì chúng ta sử dụng thành phần LongListSelector, trong đó mỗi item của nó sẽ là một TextBlock để hiển thị tiêu đề của mỗi bài viết. Các bạn hãy tùy chỉnh bố cục của mỗi item này theo cách mình mong muốn. Có thể cho hiển thị thêm phần description của bài viết, hoặc thậm chí ảnh đại diện của bài viết. Lưu ý rằng tất cả các thẻ này đều là tùy chọn nên chỉ có thể sử dụng với RSS của một số trang mà bạn đã kiểm chứng.

Khi người dùng chọn vào một tiêu đề bài viết thì nó sẽ chuyển sang trang đọc tin tức, ở trang này chúng ta sẽ nhúng vào một trình duyệt (WebBrowser).

Tất cả các mã để tạo giao diện các bạn có thể tham khảo ở file mã nguồn.

Data Binding

Chúng ta quan sát một đoạn mã của trang MainPage.xaml:

[source lang=”xml”]
<phone:LongListSelector x:Name="lstRssItems" Margin="0" ItemsSource="{Binding Channel.Items}" SelectionChanged="lstRssItems_SelectionChanged">
<phone:LongListSelector.Resources>
<DataTemplate x:Key="RssItemTemplate">
<Grid Margin="0,0,0,15">
<TextBlock Margin="10,10,0,0" TextWrapping="Wrap" Text="{Binding Title}" VerticalAlignment="Top"/>
</Grid>
</DataTemplate>
</phone:LongListSelector.Resources>
<phone:LongListSelector.ItemTemplate>
<StaticResource ResourceKey="RssItemTemplate"/>
</phone:LongListSelector.ItemTemplate>
</phone:LongListSelector>

[/source]

Ở đây chúng ta gắn thuộc tính ItemsSource của LongListSelector với thuộc tính Items (Items chính là danh sách các bài viết mà mình tải về). Mỗi item của LongListSelector thì có một TextBlock với thuộc tính Text được gắn với thuộc tính Title (Title là tiêu đề của bài viết).

Code Behind

Chúng ta sẽ xét qua hai phương thức OnNavigatedTo()lstRssItems_SelectionChanged() của Lớp MainPage (nằm trong file MainPage.xaml.cs):

[source lang=”csharp”]
protected override void OnNavigatedTo(NavigationEventArgs e)
{
if (App.RssModel == null) {
App.LoadRssModel();
}
}

private void lstRssItems_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (lstRssItems.SelectedItem != null) {
RssItemModel item = (RssItemModel)lstRssItems.SelectedItem;
NavigationService.Navigate(new Uri(string.Format("/NewsPage.xaml?link={0}",item.Link), UriKind.Relative));
}
}

[/source]

Phương thức OnNavigatedTo() sẽ được gọi khi chúng ta đi đến trang MainPage.xaml, do đó ở đây chúng ta bắt đầu tải dữ liệu về. Phương thức lstRssItems_SelectionChanged() sẽ được gọi khi người dùng chọn vào tiêu đề của một bài viết, do đó chúng ta sẽ chuyển đến trang đọc tin bằng cách sử dụng phương thức Navigate() của lớp NavigationService.

Trang đọc tin

Mã tạo giao diện của trang đọc tin (NewsPage.xaml) rất đơn giản, bởi vì ở đây chúng ta chỉ nhúng vào một trình duyệt với đoạn mã sau:

[source lang=”xml”]
<phone:WebBrowser x:Name="browserNews" Margin="0" Grid.Row="1" LoadCompleted="browserNews_LoadCompleted" IsScriptEnabled="True"/>
[/source]

Ở phần code behind của trang này (lớp NewsPage) chúng ta có phương thức
OnNavigatedTo()
được dùng để tải về trang web tương ứng với đường link mà ta nhận được:

[source lang=”csharp”]
protected override void OnNavigatedTo(NavigationEventArgs e)
{
String link = NavigationContext.QueryString["link"];
browserNews.Navigate(new Uri(link));
}

[/source]

Mở rộng

Hãy mở rộng ứng dụng này bằng cách đưa thêm nhiều tính năng mới vào cho nó, tôi có thể gợi ý cho bạn một số tính năng như sau:

  • Hiển thị ảnh của bài viết trong danh sách.
  • Tổng hợp tin từ nhiều nguồn khác nhau chứ không chỉ từ TCLT.
  • Cho phép người dùng chọn các kênh RSS mà họ muốn.
  • Chia các kênh RSS vào từng chủ đề khác nhau như: Thể thao, kinh tế, Âm nhạc…
  • Tính năng đọc tin offline bằng cách lưu các tin lại.
  • Tích hợp thêm các hiệu ứng chuyển trang…

Vậy là chúng ta đã hoàn thành xong một bài tập đơn giản, hy vọng rằng các bạn sẽ tạo được ứng dụng RSS như mình mong muốn. Hãy chia sẻ với mọi người kết quả làm việc của mình nhé.
Tải mã nguồn: Download

Nguyễn Khắc Nhật