第5回 データを画面に表示する:連載:Windowsストア・アプリ開発入門(4/5 ページ)
Windowsストア・アプリのUIを作るために用意されている標準のコントロールを紹介。実際にWebViewコントロールを使って画面のUIを作成してみる。
メイン画面をデータ・バインドに変更する
ダミー・データとそれをデシリアライズするコードが用意できたので、それをメイン画面にデータ・バインドしていこう。
メイン画面のデータ・コンテキストにダミー・データをセットする
データ・バインドとは、画面のデータ・コンテキストに入っているデータをコントロールで使う仕掛けだった。まずは、データ・コンテキストにダミー・データをセットしよう。
デザイン時のデータ・コンテキストにダミー・データを設定する
デザイン時のデータ・コンテキストにダミー・データを設定するには、JSONファイルを指定するだけだ。次のコードのようにXAMLコードを記述する。
<Page
x:Name="pageRoot"
x:Class="AtmarkItReader.HubPage"
DataContext="{Binding DefaultViewModel, RelativeSource={RelativeSource Self}}"
……省略……
NavigationCacheMode="Required"
xmlns:dm="using:AtmarkItReader.DataModel"
d:DataContext="{d:DesignData Source=/SampleData/FeedsDataSample.json, Type=dm:FeedsData}"
>
太字の部分を追加する。
「2通りの実装方法」の表で説明したように、JSONファイルを使う場合は「Type=〜」でクラス名を指定する必要がある。
また、FeedsDataクラスは、これまで使っていなかった名前空間(AtmarkItReader.DataModel名前空間)に属しているので、名前空間の宣言も追加する(「xmls:dm=〜」の行)。
実に簡単である。
実行時のデータ・コンテキストに設定する
実行時のデータとして使うには、まず「App.xaml」ファイルにFeedsDataクラスのオブジェクトをリソースとして宣言する。リソースの名前は「feedsDataSource」とした。
<Application
x:Class="AtmarkItReader.App"
……省略……
RequestedTheme="Light"
xmlns:dm="using:AtmarkItReader.DataModel"
>
<Application.Resources>
<!-- アプリケーション固有のリソース -->
<x:String x:Key="AppName">@IT RSS Reader</x:String>
<dm:FeedsData x:Name="feedsDataSource" />
</Application.Resources>
</Application>
太字の部分を追加する。
先ほどの「HubPage.xaml」と同様に、名前空間の宣言も必要だ。
このようにすることで、アプリ内のどこからでもfeedsDataSourceリソースを参照できる。なお、データは常にこのように置かねばならないわけではなく、特定の画面でしか使わないデータならばその画面内に保持してもよい。
さて、「App.xaml」ファイル内に宣言しただけでは、FeedsDataオブジェクトの中身は空っぽだ(FeedsDataクラスのコンストラクタではデータを読み込んでいないので)。今回は、次のコードのようにして、「App.xaml.cs」ファイルのOnLaunchedメソッドでダミー・データの投入を開始しよう。
protected override async void OnLaunched(LaunchActivatedEventArgs e)
{
#if DEBUG
……省略……
#endif
// アプリケーションに唯一のFeedsDataインスタンスを生成して保持する(生成処理は非同期)
App.Current.Resources["feedsDataSource"]
= AtmarkItReader.DataModel.FeedsDataSample.Data; // ダミー・データ
Frame rootFrame = Window.Current.Content as Frame;
……省略……
太字の部分を追加する。
FeedsDataSampleクラスのLoadSampleDataメソッドは非同期で動くように作ってあるので、ここで追加したコードを通過した時点ではfeedsDataSourceリソースの中身はやはり空っぽのままで、後から非同期で順次ダミー・データが生成されて追加されていく。その間にもこのOnLaunchedメソッドの処理はどんどん進んでいき、メイン画面が表示される。
なお、重要な注意として、データ・コンテキストに使うオブジェクトをこのように「すげ替え」するのは、画面表示後にやってはいけない。なぜなら、改めてデータ・バインドし直さない限りは、すげ替える前のオブジェクトを使い続けてしまうからだ。画面表示中は、面倒でもデータの内容を逐一更新する必要がある。
最後に、メイン画面の実行時のデータ・コンテキストに、上で作ったfeedsDataSourceリソースをセットする(次のコード)。
<Page
x:Name="pageRoot"
x:Class="AtmarkItReader.HubPage"
……省略(この直上にあった「DataContext=〜」の行は削除した)……
NavigationCacheMode="Required"
xmlns:dm="using:AtmarkItReader.DataModel"
d:DataContext="{d:DesignData Source=/SampleData/FeedsDataSample.json, Type=dm:FeedsData}"
DataContext="{StaticResource feedsDataSource}"
>
太字の部分を追加する。
4行目にあった「DataContext="{Binding DefaultViewModel, RelativeSource={RelativeSource Self}}"」は削除し、開始タグの末尾に改めてDataContext属性を追加する。
これで、デザイン時にも実行時にも、ダミー・データがメイン画面のデータ・コンテキストに入っているようになった。
画面の変更
それでは、メイン画面に表示しているデータを、これまでのハード・コーディングによるものから、データ・バインドに置き換えていこう。
目次セクションのデータ・バインド
まずは、目次セクション(=左端のセクション)のリスト部分から。現状は次のコードのようになっている。
<!-- 目次のリスト -->
<ListView Background="Transparent" Width="500">
<ListView.Resources>
<Style TargetType="ListViewItem" x:Key="IndexItemStyle">
<Setter Property="FontSize" Value="30" />
<Setter Property="Background" Value="#88FFFFFF" />
<Setter Property="Margin" Value="0,0,0,10" />
<Setter Property="BorderThickness" Value="15,10" />
</Style>
</ListView.Resources>
<ListViewItem Content="Insider .NET フォーラム" Style="{StaticResource IndexItemStyle}" />
<ListViewItem Content="NEWS 最新記事一覧" Style="{StaticResource IndexItemStyle}" />
<ListViewItem Content="お気に入り" Style="{StaticResource IndexItemStyle}" />
</ListView>
ここで、それぞれのListViewItemオブジェクトの「Content="Insider .NET フォーラム"」などとなっている部分をデータ・バインドに書き換えてもよさそうだ(実際、そうやっても当面の目的は果たせる)。しかしそれでは、リストの内容が増減するたびにここを書き換えることになる。ListViewコントロールには、バインドされたデータから表示するオブジェクトを作り出す機能があるので、それを利用して汎用的な記述にする(次のコード)。
<!-- 目次のリスト -->
<ListView Background="Transparent" Width="500" SelectionMode="None"
ItemsSource="{Binding Feeds}"><!-- ←Feedオブジェクトのコレクションをバインド -->
<ListView.ItemTemplate>
<DataTemplate><!-- ←コレクション内の個々のFeedオブジェクトがバインドされる -->
<Grid Width="500" Margin="-4,0,-20,0">
<Border Background="#88FFFFFF" Padding="15,10" >
<TextBlock Text="{Binding Title}" FontSize="30" />
<!-- ↑FeedオブジェクトのTitleプロパティをバインドする -->
</Border>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
ListViewコントロールのItemsSourceプロパティに、FeedsDataオブジェクトのFeedsプロパティ(=Feedオブジェクトのコレクション)をバインドする。ListViewコントロールのItemTemplateプロパティに定義した<DataTemplate>要素から、バインドされたコレクションの個々の要素を表示するためのUIオブジェクトが生成され、それぞれのデータがバインドされる。
なお、ListViewコントロールの開始タグに追加した「SelectionMode="None"」という属性は、タッチ/クリックしても選択状態にならないようにするものだ(第3回で作成したときに付け忘れていた)。
上でItemsSourceプロパティにバインドしているのは、データ・コンテキストに入っているFeedsDataオブジェクト全体ではなく、FeedsDataオブジェクトのFeedsプロパティ(=Feedクラスのオブジェクトのコレクション=RSSフィードのコレクション)である。
ListViewコントロールは、ItemsSourceプロパティに与えられたコレクションに入っているデータの数だけ、表示するオブジェクトを自動生成する。ここでは、バインドされているFeedsプロパティに入っているFeedオブジェクトの数だけ、リストに表示するオブジェクトが生成される。そしてListViewコントロールは、生成したオブジェクトにFeedオブジェクトを自動的にバインドする。どのオブジェクトに何がバインドされるのかを、上のコード中のコメントにも書いておいた。確認してほしい。
この自動生成されたリストに表示するオブジェクトというのは、既定では何もビジュアルを持っていない(データが文字列ならそれが表示されるが、それ以外ではオブジェクトの型が表示される)。そこでビジュアルを与えるのが、ListViewコントロールのItemTemplateプロパティにセットしたデータ・テンプレート(=<DataTemplate>タグ)だ。データ・テンプレートの中に自由にUIを定義し、バインドされたFeedオブジェクトの内容を利用できるのだ。
残りのセクションのデータ・バインド
さて先を急ごう。残りのセクションも同様だ。ただし、セクションごとに1つのFeedオブジェクトをバインドし、セクションのヘッダにはFeedオブジェクトのTitleプロパティを、そしてセクションのリスト部分にはFeedオブジェクトのItemsプロパティ(=FeedItemオブジェクトのコレクション=個々の記事データのコレクション)をバインドする。フィードのリストの最初のセクションは次のコードのように変更する。
<HubSection Padding="0,35,80,68" IsHeaderInteractive="True"
DataContext="{Binding Feeds[0]}"><!-- ←1つ目のFeedオブジェクトをバインド -->
<HubSection.Header>
<TextBlock Text="{Binding Title}"/>
<!-- ↑1つ目のFeedオブジェクトのTitleプロパティがバインドされる -->
</HubSection.Header>
<DataTemplate>
<ListView Width="500"
ItemsSource="{Binding Items}"
><!-- ↑1つ目のFeedオブジェクトのItemsプロパティがバインドされる -->
<ListView.ItemTemplate>
<DataTemplate><!-- ←コレクション内の個々のFeedItemオブジェクトがバインドされる -->
<Border Background="DodgerBlue" Width="500">
<StackPanel Margin="15,10,35,10">
<TextBlock Text="{Binding Title}" Foreground="White" FontSize="24" TextWrapping="Wrap" />
<!-- ↑FeedItemオブジェクトのTitleプロパティがバインドされる -->
<TextBlock Text="{Binding PubDate}" Foreground="White" FontSize="12" />
<!-- ↑FeedItemオブジェクトのPubDateプロパティがバインドされる -->
</StackPanel>
</Border>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</DataTemplate>
</HubSection>
<HubSection>タグの全体をこのコードに置き換える。
ほかのセクションも同様だ。ただし最後の「お気に入り」セクションだけは、項目の背景色を「DodgerBlue」から「BlueViolet」に変える*10。
*10 最終的に3つのハブ・セクションに、ほぼ同じListViewコントロールの記述ができる。この重複をなくすために、ListViewコントロール内のデータ・テンプレートをページ・リソース(またはアプリ・リソース)に置いて共通化することも可能だ。さらには、このListViewコントロール全体を含むデータ・テンプレートをページ・リソース(またはアプリ・リソース)にしてしまうこともできる。いずれも、本稿では扱わないが、Windowsストア・アプリの開発に慣れてきたら挑戦してみてほしい。
メイン画面のデータ・バインドをテストする
以上で、メイン画面の表示が(ページのタイトルと左下の説明を除き)データ・バインドされるようになったはずだ。確認していこう。
まずは、XAMLエディタでの表示はどうだろうか。次の画像のように、ダミー・データが表示されている。
それではデバッグ実行してみよう。次の画像のように、ダミー・データが表示される。
Copyright© Digital Advantage Corp. All Rights Reserved.