画像をバインドするには?[Win 8]:WinRT/Metro TIPS
Imageコントロールにデータ・バインディングで画像を表示する方法として、URI文字列をバインドする方法と、BitmapImageオブジェクトをバインドする方法を解説する。
powered by Insider.NET
これまで本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}"
/>
ここで、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>
これを記述する場所は、ファイル先頭の開始タグの直後だ。
上記のようにテンプレートに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文字列は、各自で用意したものに書き換えてほしい。
そうしたら、コードビハインドのLoadStateメソッド(またはOnNavigatedToメソッド)に次のコードを追加して、URL文字列の配列をGridViewコントロールにバインドする。
gridView1.DataContext = _graphicUrls;
gridView1.DataContext = _graphicUrls
このコードを記述する場所は、LoadStateメソッド(LayoutAwarePageクラスを継承した場合)またはOnNavigatedToメソッド(継承しない場合)である。
これで完成だ。実行してみると、次の画像のように表示される。
このように、画像の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}"
/>
データ・テンプレート「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
このコードを記述する場所は、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
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のプロジェクト・テンプレートでは、ハイブリッド方式で実装されている。
Copyright© Digital Advantage Corp. All Rights Reserved.