我們緊接著上篇,開始我們的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(); }
未完待續,敬請期待....