ファイルの読み書きについて学び、「お気に入り」の機能を実装してみよう。ファイルの保存場所としてアプリケーションデータ記憶域のローミングを利用する。
powered by Insider.NET
第6回までの連載で、WebサービスからRSSフィードを取得し、タイトルを一覧し、それぞれの記事を表示するという基本機能が完成した。しかしながら、メイン画面にある「お気に入り」の機能は手付かずのままだった。そこで今回は、ファイルの読み書きについて学び、「お気に入り」の機能を実装してみよう。なお、本稿のサンプルコードは「Dev Center - Windows Store apps サンプル: Windowsストアアプリ開発入門:第8回」からダウンロードできる。
Windows 8.1(以降、Win 8.1)用のWindowsストアアプリ(以降、Win 8.1アプリ)を開発するには、Win 8.1とVisual Studio 2013(以降、VS 2013)が必要だ。本稿では、Windows 8.1 Pro(製品版)とVisual Studio Express 2013 for Windows(製品版)*1を使用している*2。また、前回終了時点のソースコードを用意しておいてほしい(「Windows Store app samples:Windowsストア・アプリ開発入門:第7回」からダウンロード可)。
*1 Visual Studio Express 2013 for Windows(製品版)はマイクロソフトのサイトから無償で入手できる。Expressエディションはターゲットプラットフォームごとに製品が分かれていて紛らわしいが、Windowsストアアプリの開発には「for Windows」を使う(「for Windows Desktop」はデスクトップで動作するアプリ用)。なお、Preview版のWin 8.1では動作しないので注意してほしい。
*2 本稿のコードはPreview版でもビルド/実行できることを確かめている。実際の操作方法などの細部では異なる点があるのはご容赦願いたい。なお、Win 8.1のPreview版の使用期限は来月(2014年1月)の半ばまでだ。
前回までのコードに誤りがあった。いずれも実行時に不具合を生じるものではないが、ここにおわびするとともに訂正させていただく。なお、上記URLからダウンロードできるソースコードではこれらは修正済みである。
訂正箇所は2つある。1つは、第5回のfeedsDataSourceリソースの宣言部分だ(「App.xaml」ファイル内)。
誤)<dm:FeedsData x:Name="feedsDataSource" />
正)<dm:FeedsData x:Key="feedsDataSource" />
「x:Name」ではリソースのインスタンスを格納する変数まで生成されてしまうが、その変数は使っていない。「x:Key」であれば、インスタンスだけが生成される。
もう1つは、前回で作成したFeedsDataExtensionクラスの名前だ。
誤)FeedsDataExtension
正)FeedsExtension
これは、何でもよいのだが、クラス名を「FeedsData」で始まらない名前に変更してほしい。クラス名を参照しているコードはないので、他の部分への影響はない。このクラス名が「FeedsData」で始まっていると、第5回で実装したデザイン時のデータコンテキストが働かなくなってしまうのだ*3。
*3 おそらくは「d:DesignData」でJSONファイルをデシリアライズするフレームワークの部分に不具合があるためだと思われるが、詳細は不明である。
Windowsストアアプリでのファイルの読み書きは、意外と難しい。その理由の1つは、Windowsランタイム(以降、WinRT)と.NET Frameworkをまたがった処理になる場合が多いこと。もう1つは、Windowsストアアプリに課されたセキュリティ面での制限である。
Windowsストアアプリで利用できるクラスライブラリには、WinRTのものと.NET Frameworkのものがあることは第1回で述べた。ここまではその違いを意識することはあまりなかったが、ファイルの読み書きでは両者をまたがったコーディングをすることが多く、そこでは明示的な変換が必要であるため、処理が複雑になる。
ファイルの読み書き処理の多くは、次の図のような4層構造になる。
WinRTでは、ファイルやフォルダーを抽象化して「ストレージ」(Windows.Storage名前空間)として扱う(図の一番上)。ストレージには、ファイルのコピーや簡単な読み書きなどを実行するメソッドが用意されている*4。Windowsストアアプリでは、このWinRTのストレージを使わないとファイルにアクセスできない。
ファイルの中身を自由に読み書きするためには、そのビット列を抽象化した「ストリーム」を使う。WinRTのストレージからは、WinRTのストリーム(Windows.Storage.Streams名前空間)が得られる(図の上から2番目)。
ファイルの中身を読み書きするのに便利なクラスが、既存の.NET Frameworkには多く用意されている(図の一番下)。ところが、それらが扱えるのは、.NET Frameworkのストリーム(System.IO名前空間)だけだ(図の下から2番目)。そこで、WinRTのストリームと.NET Frameworkのストリームを変換する必要が出てくるのだ。この変換は自動的には行われないので、コードで明示的に変換する必要がある。
今回作る機能では、.NET FrameworkのXmlSerializerクラス(System.Xml.Serialization名前空間)を使って.NET Frameworkのストリームを読み書きする。そのため、WinRTと.NET Frameworkの間でストリームを変換しなければならない。
*4 例えば、ファイルをコピーするにはStorageFileオブジェクト(Windows.Storage名前空間)のCopyAsyncメソッドなどを使う。StorageFileオブジェクトからテキストを1行ずつ読み取るには、FileIOクラス(Windows.Storage名前空間)のReadLinesAsyncメソッドなどが使える。これらWinRTのクラスライブラリだけで足りるならば、.NET Frameworkとの間でのストリーム変換は不要だ。
Windowsストアアプリには、エンドユーザーに安全と安心を提供するためにプラットフォームの制限があることは第1回で述べた。そのため、Windowsストアアプリが自由にファイルアクセスを行うことは許されていないのだ。
この制限の基本的な考え方は、エンドユーザーの指定したフォルダーやファイルだけがWindowsストアアプリで利用できるというものだ。エンドユーザーにフォルダーやファイルを指定してもらうには、「ファイルピッカー」(Windows.Storage.Pickers名前空間のFolderPicker/FileOpenPicker/FileSavePickerクラス)を使う。しかしそれだけでは困ってしまうので、マニフェストで指定したフォルダーや、アプリごとに割り当てられた専用の場所などでは、例外的に自由なファイルアクセスができるようになっている。
ファイルの保存場所と読み書き方法をまとめた一覧を次の表に示す。これは、「WinRT/Metro TIPS アプリに同梱したテキスト・ファイルを読むには?[Win 8/WP 8]」にも掲載したものだ。
場所 | 読み書き | ファイルピッカー | 読み書きに利用するもの |
---|---|---|---|
アプリケーションのインストールフォルダー | 読み取り専用 | 利用不可 | Package.Current.InstalledLocationプロパティ「ms-appx:///」スキーマ |
アプリケーションデータ記憶域(ローカル、ローミング、一時) | 読み書き可 | 利用不可 | ApplicationData.Currentプロパティ「ms-appdata:///」スキーマ |
ユーザーのDownloadsフォルダー | 読み書き可(自アプリで作成したファイル以外はファイルピッカーの利用が必須) | 利用可 | DownloadsFolderクラス(Windows.Storage名前空間) |
マニフェストでcapability(機能)を与えた場所 | 読み書き可 | 利用可 | KnownFoldersクラス(Windows.Storage名前空間) |
上記以外でエンドユーザーが指定したファイルやフォルダー | 読み書き可 | 必須 | Windows.Storage.Pickers名前空間 |
ファイルの保存場所とWindowsストアアプリからの読み書き方法 |
この表の「読み書きに利用するもの」に挙げたものを使ってWinRTのストレージ(フォルダーやファイル)を取得する。場所によってストレージを得る方法が異なるのも、Windowsストアアプリでのファイルの読み書きを難しくしている一因だ。
先ほどの表に、ファイルの保存場所としてアプリケーションデータ記憶域の「ローミング(roaming)」がある。「roam」とは「歩き回る、放浪する」といったような意味で、このフォルダーに保存したファイルは自動的に他のPCに複製されるのだ。ただし、ローミングされるのは、同じユーザーアカウントで実行される同じアプリの間だけに限られる。
ローミングはWindowsが管理しており、そのオン/オフはエンドユーザーが変更できる。アプリからは、オン/オフや複製されるタイミングなど、干渉することは一切できない。
ローミングフォルダーにデータを保存すれば、それだけで複製が自動的に行われる。ただし、同期されるファイルの合計サイズには制限があるので、それを超えないように注意が必要だ。その制限サイズはApplicationData.Current.RoamingStorageQuotaプロパティ(Windows.Storage名前空間)で取得できる(単位はKBytes)。現在のWindows 8.1では100KBytesになっている(変更される可能性がある)。
今回作る機能では、「お気に入り」のデータをローミングフォルダーに保存しよう。こうしておけば、エンドユーザーは、自分のアカウントでWindowsにログインすれば、どのデバイスでも同じ「お気に入り」が見られることになるのだ。
Copyright© Digital Advantage Corp. All Rights Reserved.