第5回 データを画面に表示する連載:Windowsストア・アプリ開発入門(4/5 ページ)

» 2013年11月01日 15時41分 公開
[山本康彦(http://www.bluewatersoft.jp/),BluewaterSoft]

メイン画面をデータ・バインドに変更する

 ダミー・データとそれをデシリアライズするコードが用意できたので、それをメイン画面にデータ・バインドしていこう。

メイン画面のデータ・コンテキストにダミー・データをセットする

 データ・バインドとは、画面のデータ・コンテキストに入っているデータをコントロールで使う仕掛けだった。まずは、データ・コンテキストにダミー・データをセットしよう。

デザイン時のデータ・コンテキストにダミー・データを設定する

 デザイン時のデータ・コンテキストにダミー・データを設定するには、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}"
  >

「HubPage.xaml」ファイルの開始タグにデザイン時のデータ・コンテキストを追加した(XAML)
太字の部分を追加する。
「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>

「App.xaml」ファイルにFeedsDataオブジェクトをリソースとして宣言した(XAML)
太字の部分を追加する。
先ほどの「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;
  ……省略……

「App.xaml.cs」ファイルのOnLaunchedメソッドでダミー・データの投入を開始する(C#)
太字の部分を追加する。
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}"
  >

「HubPage.xaml」ファイルの開始タグで実行時のデータ・コンテキストも変更した(XAML)
太字の部分を追加する。
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>

第3回で作った目次のリスト部分(XAML)

 ここで、それぞれの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>

目次のリスト部分をデータ・バインドに書き直した(XAML)
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>

フィードのリストの最初のセクションをデータ・バインドに書き直した(XAML)
<HubSection>タグの全体をこのコードに置き換える。

 ほかのセクションも同様だ。ただし最後の「お気に入り」セクションだけは、項目の背景色を「DodgerBlue」から「BlueViolet」に変える*10

*10 最終的に3つのハブ・セクションに、ほぼ同じListViewコントロールの記述ができる。この重複をなくすために、ListViewコントロール内のデータ・テンプレートをページ・リソース(またはアプリ・リソース)に置いて共通化することも可能だ。さらには、このListViewコントロール全体を含むデータ・テンプレートをページ・リソース(またはアプリ・リソース)にしてしまうこともできる。いずれも、本稿では扱わないが、Windowsストア・アプリの開発に慣れてきたら挑戦してみてほしい。


メイン画面のデータ・バインドをテストする

 以上で、メイン画面の表示が(ページのタイトルと左下の説明を除き)データ・バインドされるようになったはずだ。確認していこう。

 まずは、XAMLエディタでの表示はどうだろうか。次の画像のように、ダミー・データが表示されている。

XAMLエディタにダミー・データがデータ・バインドされて表示された XAMLエディタにダミー・データがデータ・バインドされて表示された

 それではデバッグ実行してみよう。次の画像のように、ダミー・データが表示される。

実行時にダミー・データがデータ・バインドされて表示された 実行時にダミー・データがデータ・バインドされて表示された

Copyright© Digital Advantage Corp. All Rights Reserved.

RSSについて

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

メールマガジン登録

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