我們緊接着上篇,開始我們的Metro風格應用開發。
-----------------------------------我是華麗的分割線-----------------------------------------
9.將數據獲取到應用
a)既然我們創建的應用可以從 Internet 下載數據,我們便可以編寫代碼以將博客信息提要置於其中了。
我的博客以 RSS 展示了文章的完整文本。我們希望在閱讀器應用中顯示的博客數據為
每篇最新博客文章的標題、作者、日期和內容。首先,我們需要下載每篇文章的數據。
幸運的是,Windows 運行時包含一組類,這些類可以為我們執行處理信息提要數據的許多工作。
我們可以在 Windows.Web.Syndication命名空間中可以找到這些類。可以直接使用這些類顯示 UI 中的數據。
但在我的博客閱讀器中,我將創建屬於自己的數據類。
b)向項目添加新的類文件
選擇“項目”>“添加類”。“新增項目”對話框即會打開。
輸入類文件的名稱FeedData。
單擊“添加”。
c)代碼如下:
/// <summary> /// FeedData 類容納有關 RSS 或 Atom 信息提要的信息 /// </summary> public class FeedData { public string Title { get; set; } public string Description { get; set; } public DateTime PubDate { get; set; } private List<FeedItem> items = new List<FeedItem>(); public List<FeedItem> Items { get { return items; } } }
其中FeedData類中包含了FeedItem 類,FeedItem 類容納有關信息提要所包含的單個博客文章的信息,
FeedItem 類代碼如下:
/// <summary> /// FeedItem 類容納有關信息提要所包含的單個博客文章的信息 /// </summary> public class FeedItem { public string Title { get; set; } public string Author { get; set; } public string Content { get; set; } public DateTime PubDate { get; set; } public Uri Link { get; set; } }
有了以上兩個基礎數據類,我們就可以准備Rss的數據源,新建一個類:FeedDataSource,代碼如下:
/// <summary> /// FeedDataSource 類包含信息提要的機會以及從網絡檢索信息提要的方法 /// </summary> public class FeedDataSource { private ObservableCollection<FeedData> feeds = new ObservableCollection<FeedData>(); public ObservableCollection<FeedData> Feeds { get { return feeds; } } public async Task GetFeedsAsync() { Task<FeedData> feed = GetFeedAsync("http://feed.cnblogs.com/blog/u/118198/rss"); //如果在 C# 和 Visual Basic 中使用await關鍵字, //則以異步方式檢索信息提要的代碼將與以同步方式檢索信息提要時所使用的代碼相似 //你只能在被定義為async的方法中使用await關鍵字 Feeds.Add(await feed); } private async Task<FeedData> GetFeedAsync(string feedUrl) { SyndicationClient client = new SyndicationClient(); Uri feedUri = new Uri(feedUrl); try { //此處的await關鍵字會告訴編譯器在后台自動為我們執行多種處理。 //編譯器會將該方法中位於此調用之后的其余部分作為此調用返回后將要執行的回調。 //它會緊接着將控制返回給調用會話(通常是 UI 會話),以使應用保持響應。 //此時,會將表示此方法的最終輸出的 Task(一個FeedData 對象)返回給調用者。 //當 RetrieveFeedAsync 返回包含我們所需數據的 SyndicationFeed 時, //將執行我們的方法中其余的代碼。重要的是,系統在我們從中進行原始調用的相同會話上下文 //(UI 會話)中執行這些代碼,因此當我們需要在此代碼中更新 UI 時不必擔心使用調度程序。 //檢索到 SyndicationFeed 后,我們將需要的部分復制到 FeedData 和 FeedItem 數據類中。 SyndicationFeed feed = await client.RetrieveFeedAsync(feedUri); // This code is executed after RetrieveFeedAsync returns the SyndicationFeed. // Process it and copy the data we want into our FeedData and FeedItem classes. FeedData feedData = new FeedData(); feedData.Title = feed.Title.Text; if (feed.Subtitle.Text!=null) { feedData.Description = feed.Subtitle.Text; } feedData.PubDate = feed.Items[0].PublishedDate.DateTime; foreach (var item in feed.Items) { FeedItem feedItem = new FeedItem(); feedItem.Title = item.Title.Text; feedItem.PubDate = item.PublishedDate.DateTime; feedItem.Author = item.Authors[0].Name.ToString(); if (feed.SourceFormat==SyndicationFormat.Atom10) { feedItem.Content = item.Content.Text; } else if (feed.SourceFormat == SyndicationFormat.Rss20) { feedItem.Content = item.Content.Text; feedItem.Link = item.Links[0].Uri; } feedData.Items.Add(feedItem); } //當我們執行到 return 語句時,我們並未真正返回 FeedData 對象。 //請記住,當方法緊隨 await 語句返回給調用程序時,會返回一個表示該方法的最終輸出結果的 Task。 //現在,我們就已經最終獲得想要的結果了。 //return feedData; 將作為方法結果的 FeedData 對象提供給正在等待該對象的 Task。 //GetFeedsAsync 方法中的 Feeds.Add(await feed) 在等待 Task。 //當 Task 獲得正在等待的 FeedData 結果后,代碼執行將繼續, //FeedData 將被添加到 FeedDataSource.Feeds 集合中。 return feedData; } catch (Exception) { return null; } } }
FeedDataSource類應用了.Net4的新類Task,C#5.0新增關鍵字async,await,具體用法請參考msdn.
10.檢索信息提要數據
在准備好用於容納我們的數據的數據類后,我們回過頭來下載這些博客信息提要。
Windows.Web.Syndication.SyndicationClient 類可檢索完全解析的 RSS 或 Atom 信息提要,
因此,我們不用擔心解析 XML 的問題,而可以繼續構建應用中更加有趣的部分。
SyndicationClient類只提供一種檢索信息提要的方法,並且是異步的。
異步編程模型在 Windows 運行時中常常用於幫助應用保持響應。
幸運的是,程序已經為我們處理好了在使用異步方法時可能會遇到的許多復雜問題。
11.使用應用中的數據
為了使用我們的應用中的數據,我們在 App.xaml.cs/vb 中創建了數據源的一個靜態實例。
我們將實例命名為 DataSource。
代碼如下:
/// <summary> /// Provides application-specific behavior to supplement the default Application class. /// </summary> sealed partial class App : Application { public static FeedDataSource DataSource; /// <summary> /// Initializes the singleton application object. This is the first line of authored code /// executed, and as such is the logical equivalent of main() or WinMain(). /// </summary> public App() { this.InitializeComponent(); this.Suspending += OnSuspending; DataSource = new FeedDataSource(); } /// <summary> /// Invoked when the application is launched normally by the end user. Other entry points /// will be used when the application is launched to open a specific file, to display /// search results, and so forth. /// </summary> /// <param name="args">Details about the launch request and process.</param> protected override void OnLaunched(LaunchActivatedEventArgs args) { // Do not repeat app initialization when already running, just ensure that // the window is active if (args.PreviousExecutionState == ApplicationExecutionState.Running) { Window.Current.Activate(); return; } if (args.PreviousExecutionState == ApplicationExecutionState.Terminated) { //TODO: Load state from previously suspended application } // Create a Frame to act navigation context and navigate to the first page var rootFrame = new Frame(); if (!rootFrame.Navigate(typeof(MainPage))) { throw new Exception("Failed to create initial page"); } // Place the frame in the current Window and ensure that it is active Window.Current.Content = rootFrame; Window.Current.Activate(); } /// <summary> /// Invoked when application execution is being suspended. Application state is saved /// without knowing whether the application will be terminated or resumed with the contents /// of memory still intact. /// </summary> /// <param name="sender">The source of the suspend request.</param> /// <param name="e">Details about the suspend request.</param> private void OnSuspending(object sender, SuspendingEventArgs e) { var deferral = e.SuspendingOperation.GetDeferral(); //TODO: Save application state and stop any background activity deferral.Complete(); } }
頁面模板已經在它的代碼隱藏文件中包含一個 OnNavigatedTo 方法的替代。
我們將代碼置於此方法中,以獲得應用的 FeedDataSource 實例並獲得源。
首先,我們將 async 關鍵字添加到方法聲明,因為我們在方法中使用 await 關鍵字。
導航到頁面時,我們檢查以查看 FeedDataSource 是否已包含源。如果未包含,
我們調用 FeedDataSource.GetFeedsAsync 方法。然后,將頁面的 DataContext 設置為第一個源。
以下是 MainPage.xaml.cs/vb 的相關代碼:
/// <summary> /// Invoked when this page is about to be displayed in a Frame. /// </summary> /// <param name="e">Event data that describes how this page was reached. The Parameter /// property is typically used to configure the page.</param> protected override async void OnNavigatedTo(NavigationEventArgs e) { FeedDataSource feedDataSource = App.DataSource; if (feedDataSource.Feeds.Count==0) { await feedDataSource.GetFeedsAsync(); } DataContext = (feedDataSource.Feeds).First(); }
未完待續,敬請期待....