さあ、デバッグ実行してテストしてみよう。起動したら、Webサービスから取得してきた記事タイトルと発行日時がメイン画面に表示されるだろうか? タイトル一覧画面や記事表示画面との間ではスペックどおりに画面遷移するだろうか?
[お気に入り]のタイル背景色は他と違って紫色のはずだが、まだ[お気に入り]機能を作っていないので、このままでは確認できない。ロジックのDataLoaderクラスにテスト用のコードを一時的に挿入して、テストしてみよう(次のコード)。
internal static async Task<DataModel.FeedsData> LoadAsync(DataModel.FeedsData feedsData)
{
……省略……
// 空の「お気に入り」を追加する
AddEmptyFavorite(feedsData);
#if DEBUG
// 《テスト》「お気に入り」にダミーのデータを追加する(テスト後は消す)
feedsData.Feeds[2].Items.Add(
new DataModel.FeedItem("ダミー記事データ", "http://www.bluewatersoft.jp", "2013/11/15")
);
#endif
return feedsData;
}
タイルの背景色は次の画像のように、「お気に入り」では紫色に、そのほかのフィードでは青色になる。
忘れてはいけないのが、中断からシャットダウンし、また起動したとき(=リジューム時)の挙動だ。タイトル一覧画面を表示した状態で[中断とシャットダウン]を選んでデバッグ実行を終了し(第4回参照)、再びデバッグ実行をしてみよう。タイミングによるのだが、タイトル一覧画面が表示されるものの記事データは1件も表示されないはずだ。これは何とかしなくてはいけない。
○リジューム時の対応
このリジューム時の不具合は、タイトル一覧画面に表示するデータを取得するためのGetFeedメソッド(FeedsDataクラス)に原因がある。アプリ起動時にWebサービスからのダウンロードを非同期に開始するが、リジューム時にはそれが完了する前にGetFeedメソッドを呼び出してしまうことになるので、データが無いのだ。
この不具合に対処するには、ダウンロードが完了するまでGetFeedメソッドを待機させればよい。FeedsDataクラスに、ダウンロードが完了したかどうかを示すフラグを追加し、GetFeedメソッドではフラグが変わるまで待つようにする(次のコード)。
// フィードの読み込みが終わったことを示すフラグ
public bool IsLoaded { get; set; }
// メソッド内でawaitするため、シグネチャにasyncキーワードを追加し、
// 返す型はFeedクラスからTask<Feed>クラスに変更し、
// メソッド名の末尾に「Async」を付ける
public async Task<Feed> GetFeedAsync(string feedTitle)
{
// フィードの読み込みが終わっていないときは、終わるまで待つ
while (!IsLoaded)
await Task.Delay(50); // 引数の時間(ミリ秒)だけ待機する
foreach (var feed in this.Feeds)
if (feed.Title == feedTitle)
return feed;
return null;
}
待機している間にUIをブロックしてはいけないので、Taskクラス(System.Threading.Tasks名前空間)のDelayメソッドを使う。awaitキーワードを使うために、メソッドのシグネチャにはasyncキーワードが必要になり、メソッドの返り値はTask<>クラスでラップしなければならない。また、慣習に従って、メソッド名も「GetFeed」から「GetFeedAsync」に変更する。
上のGetFeedメソッドの変更によって、それを使っているタイトル一覧画面のnavigationHelper_LoadStateメソッドも次のコードのように修正が必要になる。
private async void navigationHelper_LoadState(object sender, LoadStateEventArgs e)
{
// 画面遷移時のパラメータとして、フィードのタイトルが渡される
var feedTitle = e.NavigationParameter as string;
// タイトルが合致するRSSフィードのデータを取得して、ページのデータ・コンテキストにセットする
var feedsData = App.Current.Resources["feedsDataSource"] as DataModel.FeedsData;
this.DataContext = await feedsData.GetFeedAsync(feedTitle);
……省略……
}
最後に、FeedsDataクラスに追加したダウンロードが完了したかどうかを示すIsLoadedフラグの制御を、DataLoaderクラスのLoadAsyncメソッドに追加する(次のコード)。
internal static async Task<DataModel.FeedsData> LoadAsync(DataModel.FeedsData feedsData)
{
feedsData.IsLoaded = false; // ダウンロード開始前にフラグを倒す
for (int i = 0; i < URLs.Length; i++)
{
var uri = new Uri(URLs[i]);
var syndicationFeed
= await FeedDownloder.GetFeedAsync(uri);
FeedProcessor.Add(syndicationFeed, feedsData);
}
……省略……
feedsData.IsLoaded = true; // ダウンロード完了後にフラグを立てる
return feedsData;
}
以上で、中断からシャットダウンした後でリジュームしても、タイトル一覧画面が正しく表示されるようになったはずだ。デバッグ実行して確かめてほしい。
今回は、RSSフィードを取得してくるロジックを作り、これまでに作ってきた画面に結び付けた。また、タイトル一覧画面も作成し、これで第3回から作ってきたアプリの機能が1つ完成した。
これでアプリの基盤ができた。WebサービスからRSSフィードを取得し、タイトルを一覧し、それぞれの記事を表示するという一連の機能が実現した。ここまでは何もないところからスタートしてきたので大変だったが、これからはこの基盤に機能を追加していく形になるので今までより楽に進んでいけるだろう。
次回は「指定されたデータを共有に送る」。記事を読んでいて、それをメールやSNSで紹介したくなることがあるだろう。それを実現する「共有」機能を作り込む予定だ。
入門レベルを超えてしまうため本編では扱わないが、本格的なアプリを開発していくうえでは必要になってくることも、別途公開しているサンプル・コードには実装してある。基本機能が完成したこの機会に、その中から重要なものを挙げておこう。サンプル・コード中には「【第N回:記事で説明していないコード】」といったコメントが付けてあるので、該当する部分を見つけるのは難しくないはずだ。よりよいアプリを作るときの参考にしてほしい。
説明 | 実装箇所 |
---|---|
RSSフィードの取得を並列に行う | Logic\DataLoader.cs |
システムで定義済みの色を変更する | App.xaml |
未処理例外をトラップする | App.xaml.cs |
例外発生時にエラー・ページを表示する | App.xaml.cs、ErroPage.xaml/.xaml.cs |
記事一覧画面で以前に選択されていた記事の選択状態を復元する。 この詳細は「WinRT/Metro TIPS:起動時に以前の画面を復元するには?[Win 8]」を参照 |
FeedPage.xaml.cs |
特定のコントロールが画面内に表示されるまでスクロールする | HubPage.xaml/.xaml.cs |
コントロールの子から、特定のコントロールを探す | HubPage.xaml.cs |
デザイン時のデータ・コンテキストのデータを直接XAMLで記述する | ViewPage.xaml |
Webページが表示されるまでの間、プログレス・バーを出す | ViewPage.xaml/.xaml.cs |
Webページの表示に失敗したときフライアウトを出す | ViewPage.xaml/.xaml.cs |
WebViewでリンクを辿ったときにIEへ切り替わるのを防止する | ViewPage.xaml/.xaml.cs |
WebViewでリンクを辿ったときに他のサイトへ移動するのを禁止する*5 | ViewPage.xaml/.xaml.cs |
ロジックのユニット・テスト | AtmarkItReader.Logic.Testプロジェクト |
*5 当初公開していた第4回と第5回のソース・コードは、この部分に不具合があり、「他のサイト」かどうかの判定を間違っていた。11月14日夜、修正したソース・コードに差し替えさせていただいたので、ダウンロードし直してくださるようお願いする。
Copyright© Digital Advantage Corp. All Rights Reserved.