連載
» 2013年08月08日 14時26分 UPDATE

WinRT/Metro TIPS:ファイル・ピッカーで開いたファイルを後で自動的に開くには?[Win 8]

ユーザーがファイル・ピッカーを使って指定したファイルを後で自動的に読み書きする方法として、「最近使用した一覧(MRU)」の使い方を説明する。

[山本康彦,BluewaterSoft]
WinRT/Metro TIPS
業務アプリInsider/Insider.NET

powered by Insider.NET

「WinRT/Metro TIPS」のインデックス

連載目次

 以前の記事「WinRT/Metro TIPS:ユーザーが指定したテキスト・ファイルを読み書きするには?[Win 8]」(以降、元記事)で説明したように、ファイル・ピッカーを使えばユーザーが指定したファイルを読み書きできる。しかし、アプリを起動するたびに、以前と同じファイルをユーザーがファイル・ピッカーでもう一度指定しなければならないとしたら、それは使いやすいアプリとは言えないだろう。ユーザーがファイル・ピッカーを使って指定したファイルを、後で自動的に読み書きできないだろうか?

 Windowsストア・アプリには、アプリごとに「最近使用した一覧(MRU)*1」と「後でアクセスする一覧」が用意されている。これを使うと、ユーザーが一度指定したファイルを、次からはコードで直接開けるのだ。本稿ではMRUの使い方を紹介する。本稿のサンプルは「Windows Store app samples:MetroTips #48(Windows 8版)」からダウンロードできる。

*1 「MSDN:最近使ったファイルやフォルダーを追跡する方法」では「最近使用したアプリの一覧 (MRU)」と訳されている。原文は「your app's most recently used list (MRU)」であり、直訳すれば「あなたのアプリの、最近使ったもののリスト」であって、「アプリの一覧」とするのは誤訳だろう。「your app's」とは、アプリごとにMRUが用意されていることを指す。そもそも、MRUにアプリは入れられないのだから、日本語としてもおかしい。


事前準備

 Windows 8(以降、Win 8)向けのWindowsストア・アプリを開発するには、Win 8とVisual Studio 2012(以降、VS 2012)が必要である。これらを準備するには、第1回のTIPSを参考にしてほしい。本稿では64bit版Win 8 ProとVS 2012 Express for Windows 8を使用している。

最近使用した一覧(MRU)と、後でアクセスする一覧

 どちらも、StorageApplicationPermissionsクラス(Windows.Storage.AccessCache名前空間)のプロパティを介して一覧にアクセスする。一覧にはストレージ項目(ファイルまたはフォルダ)を入れられる。2つの一覧はよく似ているが、特徴を次の表に示す。

名称 プロパティ 最大容量 最大容量に達したとき
最近使用した一覧(MRU) MostRecentlyUsedList 25項目 自動的に古いものから削除される
後でアクセスする一覧 FutureAccessList 1000項目 アプリで管理しなければならない
最近使用した一覧(MRU)と後でアクセスする一覧の比較

 なお、どちらの一覧も、データはシステムが持っており、アプリからデータに直接アクセスする方法はない。必ずStorageApplicationPermissionsクラスを介してアクセスする必要がある。

サンプル画面を用意する

 それでは、最近使用した一覧(MRU)を利用するサンプル・アプリを作ってみよう。元記事で作ったアプリをベースにして、画面の左側にMRUの内容を表示するためのリスト・ボックスを追加する(次のコード)。

……省略……
<Grid x:Name="mainGrid" Grid.Row="1" Margin="120,0,40,40">
  <Grid.RowDefinitions>
    <RowDefinition Height="Auto" />
    <RowDefinition Height="*" />
  </Grid.RowDefinitions>

  <StackPanel x:Name="commandStack" Orientation="Horizontal" Margin="0,10,0,0">
    <Button x:Name="openButton" Tapped="openButton_Tapped"
            Content="テキスト・ファイルを開く" />
    <Button Tapped="saveButton_Tapped"
            Content="名前を付けて保存する" Margin="10,0,0,0" />
  </StackPanel>

  <Grid Grid.Row="1">
    <Grid.ColumnDefinitions>
      <ColumnDefinition Width="Auto" />
      <ColumnDefinition Width="*" />
    </Grid.ColumnDefinitions>
    <ListBox x:Name="listBox1" Width="200" Margin="0,10,0,0"
             SelectionChanged="listBox1_SelectionChanged" />
    <TextBox x:Name="textbox1" Grid.Column="1" Margin="10,10,0,0"
            AcceptsReturn="True" TextWrapping="Wrap"
            ScrollViewer.VerticalScrollBarVisibility="Auto" />
  </Grid>
</Grid>
……省略……

画面のコンテンツ部分のXAMLコード(XAML)
コードビハインドにイベント・ハンドラを記述していないので、まだビルドできない。
また、この画面はLayoutAwarePageクラスを継承して作る。詳しくは元記事を参照してほしい。なお、太字は元記事と異なる部分。

開いたファイルを最近使用した一覧(MRU)に追加するには?

 最近使用した一覧(MRU)のAddメソッドを使う。

 ファイル・オープン・ピッカーを使ってファイルを開いたときにMRUに追加するには、次のコードのようにファイルを読み取ってテキストボックスに表示するコードの後に*2、MRUに追加するコードを記述する。

 Addメソッドの第1引数には、ファイル・ピッカーから返されたStorageFileオブジェクト(Windows.Storage名前空間)を与える。第2引数には、StorageFileオブジェクトと一緒に保存するメタデータを指定する。メタデータは省略してもよいが、通常は、ユーザーに表示するための文字列を入れておくことが多いだろう(ここではファイル名をメタデータとした)。

*2 この順に処理すれば、ファイルの読み取りに失敗したときにはMRUに追加されないので、一般的には都合がよいだろう。


// ファイルを読み取ってテキストボックスに表示する
this.textbox1.Text = await Windows.Storage.FileIO.ReadTextAsync(selectedFile);

// ファイルを最近使用した一覧(MRU)に追加する
StorageApplicationPermissions
  .MostRecentlyUsedList.Add(selectedFile, selectedFile.Name);

// リスト・ボックスを更新する(後述)
UpdateListBox();

' ファイルを読み取ってテキストボックスに表示する
Me.textbox1.Text = Await Windows.Storage.FileIO.ReadTextAsync(selectedFile)

' ファイルを最近使用した一覧(MRU)に追加する
StorageApplicationPermissions _
    .MostRecentlyUsedList.Add(selectedFile, selectedFile.Name)

' リスト・ボックスを更新する(後述)
UpdateListBox()

読み取ったファイルをMRUに追加するコード(上:C#、下:VB)
このコードは元記事のopenButton_Tappedメソッドの末尾に挿入する(太字の部分)。
なお、ファイルの先頭でWindows.Storage.AccessCache名前空間をusing/Importする必要がある。
また、リスト・ボックスを更新するUpdateListBoxメソッドについては後述する。

 ファイルを保存するコードにも、同様に次のコードを挿入する。

// テキストボックスの内容をファイルに書き込む
await Windows.Storage.FileIO.WriteTextAsync(saveFile, this.textbox1.Text,
                                Windows.Storage.Streams.UnicodeEncoding.Utf8);

// ファイルを最近使用した一覧(MRU)に追加する
StorageApplicationPermissions
  .MostRecentlyUsedList.Add(saveFile, saveFile.Name);

// リスト・ボックスを更新する(後述)
UpdateListBox();

' テキストボックスの内容をファイルに書き込む
Await Windows.Storage.FileIO.WriteTextAsync(saveFile, Me.textbox1.Text, _
                                Windows.Storage.Streams.UnicodeEncoding.Utf8)

' ファイルを最近使用した一覧(MRU)に追加する
StorageApplicationPermissions _
    .MostRecentlyUsedList.Add(saveFile, saveFile.Name)

' リスト・ボックスを更新する(後述)
UpdateListBox()

書き込んだファイルをMRUに追加するコード(上:C#、下:VB)
このコードは元記事のsaveButton_Tappedメソッドの末尾に末尾する(太字の部分)。
リスト・ボックスを更新するUpdateListBoxメソッドは後述する。

最近使用した一覧(MRU)をリスト・ボックスに表示するには?

 最近使用した一覧(MRU)に格納されている項目はAccessListEntryオブジェクトである。そのままではリスト・ボックスにうまく表示できないので、匿名型のオブジェクトのリストに変換してから、リスト・ボックスにセットする。なお、匿名型には、MetadataとToken(後述)というメンバを持たせる。

 リスト・ボックスの各項目には、AccessListEntryオブジェクトのメタデータ(Metadataプロパティ)を表示しよう。前述したようにメタデータにはファイル名を格納してある。そのように作ったメソッドが、上のコードに登場したUpdateListBoxメソッドだ(次のコード)。

private void UpdateListBox()
{
  // MetaDataとTokenを持つ匿名型のオブジェクトのリストを作る
  AccessListEntryView mruView = StorageApplicationPermissions.MostRecentlyUsedList.Entries;
  var list = mruView.Select(entry => new { Metadata = entry.Metadata, Token = entry.Token });

  // 作ったリストをリスト・ボックスに表示する
  this.listBox1.ItemsSource = list;
  this.listBox1.DisplayMemberPath = "Metadata";
}

Private Sub UpdateListBox()
  ' MetaDataとTokenを持つ項目のリストを作る
  Dim mruView As AccessListEntryView = StorageApplicationPermissions.MostRecentlyUsedList.Entries
  Dim list = mruView.Select(Function(entry) New With {.Metadata = entry.Metadata, .Token = entry.Token})

  ' 作ったリストをリスト・ボックスに表示する
  Me.listBox1.ItemsSource = list
  Me.listBox1.DisplayMemberPath = "Metadata"
End Sub

MRUの内容をリスト・ボックスに表示するコード(上:C#、下:VB)

 MostRecentlyUsedList.Entriesプロパティは前述のAccessListEntryオブジェクトを要素とするリストであり、これを基にリスト・ボックスのItemsSourceプロパティに設定するリストを作成している。このリストには、メタデータとともに追加したファイル(StorageFileオブジェクト)を表すトークン(後述)も格納しておき、後でファイルを開き直すときに利用する。

最近使用した一覧(MRU)のファイルを再び開くには?

 最近使用した一覧(MRU)のGetFileAsyncメソッドを使う。引数には「トークン」を渡す。

 この「トークン」とは、ファイルをMRUに追加したときに自動生成されるユニークなIDであり、そのファイル(StorageFileオブジェクト)と1対1に対応する。トークンは、MRUのAddメソッドで追加したときの返値としても取得できるし、MRUのAccessListEntryオブジェクトのTokenプロパティから取り出すこともできる。

 MRUのGetFileAsyncメソッドを使いStorageFileオブジェクトを取得した後で、ファイルを読み取って表示するのは元記事と同じだ。以上を次のコードのようにメソッドとして作っておこう。

private async void LoadText(string mruToken)
{
  // トークンを使ってMRUからStorageFileオブジェクトを取得する
  Windows.Storage.StorageFile file
    = await StorageApplicationPermissions.MostRecentlyUsedList.GetFileAsync(mruToken);

  // ファイルを読み取ってテキストボックスに表示する
  this.textbox1.Text = await Windows.Storage.FileIO.ReadTextAsync(file);
}

Private Async Sub LoadText(mruToken As String)
  ' トークンを使ってMRUからStorageFileオブジェクトを取得する
  Dim file As Windows.Storage.StorageFile _
      = Await StorageApplicationPermissions.MostRecentlyUsedList.GetFileAsync(mruToken)

  ' ファイルを読み取ってテキストボックスに表示する
  Me.textbox1.Text = Await Windows.Storage.FileIO.ReadTextAsync(file)
End Sub

トークンを使ってファイルを読み込み、テキストボックスに表示するコード(上:C#、下:VB)

 では、リスト・ボックスで項目が選択されたときに、上のLoadTextメソッドを呼び出すようにしてみよう。先にXAMLで名前だけ定義しておいたlistBox1_SelectionChangedイベント・ハンドラは、次のコードのように書ける。

private void listBox1_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
  if (e.AddedItems.Count == 0)
    return;

  dynamic entry = e.AddedItems[0];
  string token = entry.Token;
  LoadText(token);
}

Private Sub listBox1_SelectionChanged(sender As Object, e As SelectionChangedEventArgs)
  If (e.AddedItems.Count = 0) Then
    Return
  End If

  Dim entry = e.AddedItems(0)
  Dim token As String = entry.Token
  LoadText(token)
End Sub

リスト・ボックスで選択されたファイルを読み込んで表示するコード(上:C#、下:VB)
イベント引数のAddedItems(0)プロパティは、リスト・ボックスで選択されている項目である。そのTokenプロパティが対応するファイル(StorageFileオブジェクト)を表すユニークIDであり、これをLoadTextメソッドに渡すことで、そのファイルを開いている。

 なお、上のイベント・ハンドラは、コードからリスト・ボックスの選択項目を変えても実行される。そのことを利用して、アプリの起動時にMRUの先頭にあるファイル(=最後に追加したファイル)を読み込める(次のコード)。

protected override void LoadState(Object navigationParameter, Dictionary<String, Object> pageState)
{
  UpdateListBox();
  if (this.listBox1.Items != null && this.listBox1.Items.Count > 0)
  {
    // 最後に追加されたファイルを読み取る
    this.listBox1.SelectedIndex = 0;
  }
}

Protected Overrides Sub LoadState(navigationParameter As Object, pageState As Dictionary(Of String, Object))
  UpdateListBox()
  If (Me.listBox1.Items IsNot Nothing AndAlso Me.listBox1.Items.Count > 0) Then
    ' 最後に追加されたファイルを読み取る
    Me.listBox1.SelectedIndex = 0
  End If
End Sub

アプリの起動時にMRUの先頭にあるファイルを読み込むためのコード(上:C#、下:VB)

実行結果

 以上で完成だ。実行してみた様子が次の画像である。

完成したアプリ 完成したアプリ
左側のリスト・ボックスには、以前にアクセスしたファイル名が並んでおり、選択するとそのファイルが読み込まれて右側のテキストボックスに表示される。

 最初に起動したときには左のリスト・ボックスは空欄になっている。上のボタンを使ってファイルを読み込んだり、新しく名前を付けて書き込んだりするたびに、リスト・ボックスの一番上にそのファイル名が追加されていく。また、リスト・ボックスのファイル名をタップすると、そのファイルが読み込まれる。

 ファイルをいくつか読み書きしたら、いったんアプリを終了させ、あらためて実行してみよう。リスト・ボックスに先ほど終了させる前のファイル名の一覧が再び表示され、先ほどと同じようにタップするだけでファイルが読み込まれる。

 このように、ユーザーに一度指定してもらったファイルは、後から自動的に開けるのである。

 なお、最近使用した一覧(MRU)にはファイルだけでなく、フォルダも保存できる。混在させてMRUに保持する場合は、ファイルとフォルダを区別する必要が出てくるだろう。メタデータを工夫してもよいが、MRUのGetItemAsyncメソッドで取りあえず取得してから、得られたオブジェクト(=IStorageItemインターフェイス)のIsOfTypeメソッドを使って識別してもよい。

まとめ

 最近使用した一覧(MRU)と後でアクセスする一覧を利用することで、2回目以降のファイル・アクセスを自動化できる。それによって、毎回ユーザーにファイル・ピッカーを操作してもらう煩わしさを低減できる。アプリの使い勝手をよくするために、ぜひ覚えておいてほしいテクニックだ。

 最近使用した一覧(MRU)と後でアクセスする一覧については、次のドキュメントも参照してほしい。

「WinRT/Metro TIPS」のインデックス

WinRT/Metro TIPS

Copyright© 1999-2017 Digital Advantage Corp. All Rights Reserved.

@IT Special

- PR -

TechTargetジャパン

この記事に関連するホワイトペーパー

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。