画像をバインドするには?[Win 8]WinRT/Metro TIPS

Imageコントロールにデータ・バインディングで画像を表示する方法として、URI文字列をバインドする方法と、BitmapImageオブジェクトをバインドする方法を解説する。

» 2013年07月04日 13時55分 公開
[山本康彦BluewaterSoft]
WinRT/Metro TIPS
業務アプリInsider/Insider.NET

powered by Insider.NET

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

連載目次

 これまで本TIPSでデータ・バインディングについて多く語ってきたが、いずれも文字として画面に表示されるデータばかりだった。では、画像をバインドするにはどうしたらよいだろうか? 本稿では、2通りの方法を解説する。本稿のサンプルは「Windows Store app samples:MetroTips #43(Windows 8版)」と「Windows Store app samples:MetroTips #43(WP 8版)」からダウンロードできる。

 なお、本稿ではWindows Phone 8(以降、WP 8)アプリは割愛するが、画像をバインドする方法は同じである*1

*1 ピクチャ・ライブラリ(WP 8では「メディア・ライブラリ」)のファイルへのアクセス方法が大幅に異なり、バインディングの本質に関係しない部分の説明が煩雑になりすぎるので割愛させていただいた。別途公開するサンプル・コードにはWP 8のコードも含まれているので、そちらをご覧いただきたい。


事前準備

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

 実装を試してみる前に、Webにある適当な画像のURLをいくつかと、ローカルのピクチャ・ライブラリに「sample」というフォルダを作ってそこに実際の画像をいくつか用意しておいてほしい。自分で試してみるだけならどんな画像でも構わないが、フォーマットとしては.pngか.jpgを選んでおいてほしい。本稿のサンプルではWebの画像として、筆者のSkyDriveにあるフォルダ(http://sdrv.ms/17vnC2t)に置いた画像を使う*2

*2 この画像、および後に出てくるピクチャ・ライブラリのsampleフォルダに置いた画像は、第三者による再利用は許可されていないのでご注意いただきたい。
Copyright (c) 1995 GAINAX


簡易的に実装するにはURIをバインドする

 画像を表示する代表的なコントロールは、Windows.UI.Xaml.Controls名前空間のImageコントロールである。そのSourceプロパティにURIをバインドすると、URIから画像を取得して表示してくれるのだ。

 URIとしては、WebのURLでもよいし、ローカルにあるファイルを表すURIでもよい。ここでは、WebのURLをバインドしてみよう。

 バインディング・ターゲットはGridViewコントロール(Windows.UI.Xaml.Controls名前空間)にしよう。次のようなXAMLコードを記述する。

<GridView x:Name="gridView1"
          ItemsSource="{Binding}"
          ItemTemplate="{StaticResource GridView1DataTemplate}"
        />

URLをバインドするGridViewコントロール(XAML)

 ここで、ItemsSourceプロパティには「{Binding}」とだけ指定してあるが、後にコードビハインドからデータ・コンテキストにURL文字列の配列を渡すので、それをそのままバインドすることになる。

 また、ItemTemplateプロパティで指定している「GridView1DataTemplate」は、次のようにする。

<Page.Resources>
  ……省略……
  <DataTemplate x:Key="GridView1DataTemplate">
    <Grid Margin="10,5">
      <Border ……省略……
              Width="250" Height="125">
        <Image Source="{Binding}" Stretch="UniformToFill"/>
      </Border>
    </Grid>
  </DataTemplate>
</Page.Resources>

画像を表示するためのデータ・テンプレート(XAML)
これを記述する場所は、ファイル先頭の開始タグの直後だ。

 上記のようにテンプレートにImageコントロールを置き、そのSourceプロパティにバインドする。ここにバインドするデータは、先に述べたようにURL文字列(=URL文字列の配列の1要素)になる。

 次に、コードビハインドであるが、まずメンバ変数としてURL文字列の配列を宣言する(次のコード)。

private readonly string[] _graphicUrls =
{
  "http……省略……/048.JPG",
  "http……省略……/057.JPG",
  "http……省略……/049.JPG",
  "http……省略……/053.JPG",
  "http……省略……/051.JPG",
};

Private ReadOnly _graphicUrls As String() = _
{ _
  "http……省略……/048.JPG",
  "http……省略……/057.JPG",
  "http……省略……/049.JPG",
  "http……省略……/053.JPG",
  "http……省略……/051.JPG"
}

URL文字列の配列を定義する(上:C#、下:VB)
実際のURL文字列は、各自で用意したものに書き換えてほしい。

 そうしたら、コードビハインドのLoadStateメソッド(またはOnNavigatedToメソッド)に次のコードを追加して、URL文字列の配列をGridViewコントロールにバインドする。

gridView1.DataContext = _graphicUrls;

gridView1.DataContext = _graphicUrls

URL文字列の配列をバインドする(上:C#、下:VB)
このコードを記述する場所は、LoadStateメソッド(LayoutAwarePageクラスを継承した場合)またはOnNavigatedToメソッド(継承しない場合)である。

 これで完成だ。実行してみると、次の画像のように表示される。

画像がGridViewに表示された(Win 8) 画像がGridViewに表示された(Win 8)
ここまでで作成したのは左側の5つの画像。右半分には、この後で作成する部分も表示されている。

 このように、画像のURI文字列をバインドするだけで、簡単に画像を表示できる。アプリの実行中は、一度表示した画像をシステムの側でキャッシュしているらしく、ほかの画面に遷移して戻ってきてもインターネットにアクセスするようなことはない。シンプルなアプリであれば、この方法で十分であろう。

ビットマップを作成してバインドする

 アプリ内で加工した画像や、URIで表現できない場所にある画像*3をバインドするには、コードでBitmapImageオブジェクト(Windows.UI.Xaml.Media.Imaging名前空間)を生成する。ImageコントロールのSourceプロパティは柔軟に作られていて、先ほどはURL文字列をバインドしたが、BitmapImageオブジェクトを与えることもできるのだ。

*3 ローカル・ディスク内のフォルダで、URIで表現できるのは「ms-appx:///」(アプリのインストール・ディレクトリ)と「ms-appdata:///」(アプリケーション・データのフォルダ)の2箇所だけだ。それ以外の場所にあるファイルは、URIではアクセスできない。詳しくは「WinRT/Metro TIPS:アプリに同梱したテキスト・ファイルを読むには?[Win 8/WP 8]」をご覧いただきたい。


 それでは、ローカル・ディスクにある画像ファイルをバインドして表示してみよう。まず、ピクチャ・ライブラリの直下に「sample」という名前のフォルダを作り、そこに適当な画像ファイルをいくつか保存しておいてほしい。

 次に、先ほどと同様にGridViewコントロールを画面に配置する。

<GridView x:Name="gridView2" ……省略……
          ItemsSource="{Binding}"
          ItemTemplate="{StaticResource GridView1DataTemplate}"
        />

BitmapImageオブジェクトをバインドするGridViewコントロール(XAML)

 データ・テンプレート「GridView1DataTemplate」は、以前と同じものを使うので改めて記述することはない。

 次に、VS 2012のソリューション・エクスプローラでPackage.appxmanifestファイルをダブルクリックして開き、[機能](=capability)タブで[画像ライブラリ](=ピクチャ・ライブラリ)にチェックを付ける。この指定によって、コードからピクチャ・ライブラリのファイルへのアクセスが許可されるのだ。

 そうしたら、LoadStateメソッド(またはOnNavigatedToメソッド)に次のコードを追加する。

Windows.Storage.StorageFolder sampleFolder
  = await Windows.Storage.KnownFolders.PicturesLibrary.GetFolderAsync("sample");
List<ImageSource> pictures = new List<ImageSource>();
foreach(Windows.Storage.StorageFile file in (await sampleFolder.GetFilesAsync()))
{
  using (Windows.Storage.Streams.IRandomAccessStream stream
      = await file.OpenReadAsync())
  {
    var bitmap = new Windows.UI.Xaml.Media.Imaging.BitmapImage();
    await bitmap.SetSourceAsync(stream);

    pictures.Add(bitmap as ImageSource);
  }
}
gridView2.DataContext = pictures;

Dim sampleFolder As Windows.Storage.StorageFolder _
  = Await Windows.Storage.KnownFolders.PicturesLibrary.GetFolderAsync("sample")
Dim pictures As List(Of ImageSource) = New List(Of ImageSource)()
For Each file As Windows.Storage.StorageFile In (Await sampleFolder.GetFilesAsync())
  Using stream As Windows.Storage.Streams.IRandomAccessStream _
        = Await file.OpenReadAsync()

    Dim bitmap = New Windows.UI.Xaml.Media.Imaging.BitmapImage()
    Await bitmap.SetSourceAsync(stream)

    pictures.Add(bitmap)
  End Using
Next
gridView2.DataContext = pictures

ピクチャ・ライブラリのsampleフォルダにある画像ファイルを読み出し、BitmapImageオブジェクトを生成して、GridViewコントロールにバインドする(上:C#、下:VB)
このコードを記述する場所は、LoadStateメソッド(LayoutAwarePageクラスを継承した場合)またはOnNavigatedToメソッド(継承しない場合)である。また、非同期メソッドの呼び出し(await/Await)があるので、メソッドのシグネチャにasync/Asyncキーワードの追加が必要だ。

 上のコードでは、まずPicturesLibrary.GetFolderAsync("sample")メソッドで、ピクチャ・ライブラリの中のsampleフォルダを表すStorageFolderオブジェクトを取得している。StorageFolderオブジェクトのGetFilesAsyncメソッドを呼び出すと、そのフォルダ内の全ファイルが列挙されるので、それをforeach/For Eachループで順に取り出し、OpenReadAsyncメソッドでIRandomAccessStreamオブジェクトを作り、それをBitmapImageオブジェクトのSetSourceAsyncメソッドに渡して読み込ませている。最後に、作成したBitmapImageオブジェクトのコレクションを、GridViewコントロールのデータ・コンテキストに設定して完了だ。

 これを実行すると、前に掲載した画像の右半分のように表示される。

 この方法はちょっと面倒であるが、アプリからアクセスできる画像ファイルなら何でも表示できるし、コードで生成/加工したBitmapImageオブジェクトも使えるという汎用性が魅力である。

SampleDataSourceの秘密

 VS 2012のプロジェクト・テンプレートのうち[グリッド アプリケーション (XAML)]または[分割アプリケーション (XAML)]の、DataModelフォルダにあるSampleDataSource.cs/.vbファイルを見ていただきたい。

 これはバインディング・ソースのサンプル実装だ。そのSampleDataSource.cs/.vbファイルの中には、4つのクラスが記述されている。最初にあるSampleDataCommonクラスには、Imageプロパティが次のように実装されている。

private ImageSource _image = null;
private String _imagePath = null;
public ImageSource Image
{
  get
  {
    if (this._image == null && this._imagePath != null)
    {
      this._image = new BitmapImage(new Uri(SampleDataCommon._baseUri, this._imagePath));
    }
    return this._image;
  }

  set
  {
    this._imagePath = null;
    this.SetProperty(ref this._image, value);
  }
}

public void SetImage(String path)
{
  this._image = null;
  this._imagePath = path;
  this.OnPropertyChanged("Image");
}

Private _image As ImageSource
Private _imagePath As String
Public Property Image As ImageSource
  Get
    If Me._image Is Nothing AndAlso Me._imagePath IsNot Nothing Then
      Me._image = New BitmapImage(New Uri(SampleDataCommon._baseUri, Me._imagePath))
    End If
    Return Me._image
  End Get

  Set(value As ImageSource)
    Me._imagePath = Nothing
    Me.SetProperty(Me._image, value)
  End Set
End Property

Public Sub SetImage(path As String)
  Me._image = Nothing
  Me._imagePath = path
  Me.OnPropertyChanged("Image")
End Sub

プロジェクト・テンプレートにあるSampleDataCommonクラスのImageプロパティ(上:C#、下:VB)
setアクセサから呼び出しているSetPropertyメソッドと、SetImageメソッドから呼び出しているOnPropertyChangedメソッドは、継承元のBindableBaseクラスにある(BindableBaseクラスについては「WinRT/Metro TIPS:バインドするデータのPropertyChangedを楽に実装するには?[Win 8/WP 8]」を参照)。

 このImageプロパティは、ImageSourceクラス*4というちょっと見慣れない型になっているが、BitmapImageオブジェクトをset/getすることが想定されている(getアクセサのコードから分かる)。それとは別にSetImageメソッドも用意されており、その引数にはURI文字列が渡せるようになっている。

 これはいわば「ハイブリッド方式」だ。SampleDataCommonクラスを使う側からは、URI文字列*5を与えてもいいし、BitmapImageオブジェクトを与えてもよいのだ。

*4 ImageSourceクラスについては、MSDNの「Image.Source Property」を参照してほしい。

*5 このサンプル実装では、与えられるURI文字列はアプリのインストール・フォルダの下にある画像ファイルに限られている。


まとめ

 Imageコントロールにデータ・バインディングで画像を表示するには、URI文字列をバインドする方法と、BitmapImageオブジェクトをバインドする方法がある。前者は簡単で、後者はコードからアクセス可能などんな画像でも表示できるという特徴がある。なお、VS 2012のプロジェクト・テンプレートでは、ハイブリッド方式で実装されている。

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

WinRT/Metro TIPS

Copyright© Digital Advantage Corp. All Rights Reserved.

RSSについて

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

メールマガジン登録

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