連載
» 2013年03月07日 13時06分 UPDATE

WinRT/Metro TIPS:GridView/ListViewのアイテムの外観を変えるには?(その1)[Win 8]

テンプレートの中でプロパティをデータ・バインドして、データの一覧表示に使われるGridView/ListViewコントロールの外観をデータに応じて変える方法を説明する。

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

powered by Insider.NET

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

連載目次

 データの一覧をグループごとに分けて表示するときによく使うのが、GridViewコントロールとListViewコントロールだ。その外観を、例えばグループごとにアイテムのサイズを変えたり、アイテムによって色を変えたりできないだろうか?

 アイテムの外観をデータに応じて変えるには、そのテンプレートを動的に変更してやればよい。それには、テンプレートの中でプロパティをデータ・バインドする方法と、テンプレート・セレクタを作って丸ごとテンプレートを入れ替える方法がある。本稿では、前者のデータ・バインドによる方法を説明する。本稿のサンプルは「Windows Store app samples:MetroTips #27」からダウンロードできる。

事前準備

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

こんなことをやりたい!

 次の画像を見ていただきたい。いずれも筆者が作成したアプリで恐縮だが、GridViewコントロールやListViewコントロールを使った画面だ。

ap-winrttips_0027_01.gif
ap-winrttips_0027_02.gif
ap-winrttips_0027_03.gif GridView/ListViewコントロールを使っているアプリの例

 これらの画面には、以下のような特徴がある。

  • アイテムごとに背景色が変わる(上)/(下の右側)
  • ループによってアイテムのサイズが変わる(上)*1
  • グループによってアイテム内に表示される情報が変わる(中、各アイテムの画像の上端に注目)/(下の左側)
  • グループによってグループ・ヘッダが変わる(中)*2/(下の左側)

 表示にこのような変化を付けることが、データ・バインディングで可能なのだ。

*1 グループ内でアイテムのサイズを変える(中)にはテンプレート・セレクタを使う。これは、次回(その2)で解説する予定だ。


*2 中央の画像の、アプリのグループ・ヘッダ: 右端のヘッダには先頭にオレンジ色の四角が見えるが、これはグループ内のデータ件数を表示している(左側のヘッダにはない)。なお、画面左端に縦長の広告の一部が見えているが、これはアイテム0件のグループが作ってあって、そのグループ・ヘッダに広告を表示している。


「グリッド・アプリケーション」プロジェクト・テンプレート

 それでは、VS 2012のテンプレートから始めよう。新しくプロジェクトを作るときに、「Windows ストア」の「グリッド アプリケーション (XAML)」を選択する。そのままビルドして実行すると、「GroupedItemsPage.xaml」ファイルに定義された画面が表示される(次の画像)。

ap-winrttips_0027_04.gif 「グリッド・アプリケーション」プロジェクト・テンプレートをそのまま実行した画面

 この画面にはGridViewコントロールが使われている。そして、SampleDataSourceクラス(プロジェクト内のDataModelフォルダのSampleDataSource.cs/.vbファイル)のインスタンスをデータ・バインドして表示しているのだ。

 バインドしたデータをどのように表示するかは、GridViewコントロールのアイテム・テンプレートで決まる。それは「Standard250x250ItemTemplate」という名前のテンプレートで、「StandardStyles.xaml」ファイル(プロジェクト内のCommonフォルダ)に定義されている(1650行目付近、次のコード)。

<DataTemplate x:Key="Standard250x250ItemTemplate">
  <Grid HorizontalAlignment="Left" Width="250" Height="250">
    <Border
     Background="{StaticResource ListViewItemPlaceholderBackgroundThemeBrush}">
      <Image Source="{Binding Image}" Stretch="UniformToFill"
              AutomationProperties.Name="{Binding Title}"/>
    </Border>
    <StackPanel VerticalAlignment="Bottom"
     Background="{StaticResource ListViewItemOverlayBackgroundThemeBrush}">
      <TextBlock Text="{Binding Title}"
       Foreground="{StaticResource ListViewItemOverlayForegroundThemeBrush}"
       Style="{StaticResource TitleTextStyle}"
       Height="60" Margin="15,0,15,0"/>
      <TextBlock Text="{Binding Subtitle}"
       Foreground
         ="{StaticResource ListViewItemOverlaySecondaryForegroundThemeBrush}"
       Style="{StaticResource CaptionTextStyle}"
       TextWrapping="NoWrap" Margin="15,0,15,10"/>
    </StackPanel>
  </Grid>
</DataTemplate>

「Standard250x250ItemTemplate」テンプレート(XAML)
太字にした部分が、データ・バインドしているところ。
紙面の都合で改行してある

 このアイテム・テンプレートにバインドされるデータが何かは、「GroupedItemsPage.xaml」ファイルと「GroupedItemsPage.xaml.cs/.vb」ファイルで定義されている。詳細は割愛するが、それは「SampleDataSource.cs/.vb」ファイルにあるSampleDataItemクラスのインスタンスである。それがGridViewコントロールのアイテムにどのように表示されているかを示したのが、次の図だ。

ap-winrttips_0027_05.gif GridViewコントロールのアイテムを表示する仕組み
GridViewコントロールの描画は、アイテム・テンプレートによる。
アイテム・テンプレートに表示する情報は、SampleDataItemクラスのプロパティにバインドされている。
なお、画面上部に表示されているグループ・ヘッダは、ヘッダ・テンプレートを通じてSampleDataGroupクラスのTitleプロパティにバインドされている。

 このような仕組みで描画されていると分かれば、データソース(=SampleDataSourceクラス)とアイテム・テンプレート(=Standard250x250ItemTemplate)に手を加えることで、表現を豊かにしていけるだろう。

データによって背景色を変えるには?

 アイテムのタイトルによって色を変えてみよう。SampleDataItemクラスのTitleプロパティに含まれている数字が偶数のときに赤、奇数のときは青にしてみよう。

 まず、SampleDataItemクラスに「BackgroundColor」というプロパティを作成する(次のコード)。

public string BackgroundColor
{
  get
  {
    // Title から数字だけを取り出す(=数字以外を削る)
    string digits
      = System.Text.RegularExpressions
          .Regex.Replace(this.Title, "[^0-9]", string.Empty);

    // 整数に変換する
    int number = 0;
    int.TryParse(digits, out number);

    if (number % 2 == 0)
      return "DarkRed"; //偶数のとき赤
    else
      return "#00a2e8"; //奇数のとき青
  }
}

Public ReadOnly Property BackgroundColor As String
  Get
    ' Title から数字だけを取り出す(=数字以外を削る)
    Dim digits As String _
      = System.Text.RegularExpressions _
        .Regex.Replace(Me.Title, "[^0-9]", String.Empty)

    ' 整数に変換する
    Dim number As Integer = 0
    Integer.TryParse(digits, number)

    If (number Mod 2 = 0) Then
      Return "DarkRed" '偶数のとき赤
    Else
      Return "#00a2e8" '奇数のとき青
    End If
  End Get
End Property

BackgroundColorプロパティ(上:C#、下:VB)
返す文字列は、定義されている色名でもよいし、16進表現でもよい。

 このBackgroundColorプロパティを、Standard250x250ItemTemplateテンプレートのStackPanelコントロールのBackgroundプロパティにバインドする(次のコード)。

【変更前】
<DataTemplate x:Key="Standard250x250ItemTemplate">
  ……
    <StackPanel VerticalAlignment="Bottom"
     Background="{StaticResource ListViewItemOverlayBackgroundThemeBrush}">
  ……
</DataTemplate>

ap-down_arrow.gif

【変更後】
<DataTemplate x:Key="Standard250x250ItemTemplate">
  ……
    <StackPanel VerticalAlignment="Bottom"
     Background="{Binding BackgroundColor,Mode=OneWay}">
  ……
</DataTemplate>

アイテム・テンプレートのStackPanelのBackgroundプロパティを変更した*3(XAML)

*3 ここでは省略したが、背景色を指定したので、本来はその上に載るTextBlockの文字色(=Foregroundプロパティ)も指定した方がよい。このテンプレートは「テーマ」の変更に対応しており、テーマによって文字色が変わる。予期せず文字色が背景色と近い色になってしまって読めなくなるのを防ぐためだ。


 これで、次の画像のように色が付く。

ap-winrttips_0027_06.gif アイテムごとに背景色を変えることができた

データによって表示される情報を切り替えるには?

 アイテムに表示するイメージ画像が暗い色のときだけ、その手前に説明を表示してみよう。新たにTextBlockコントロールを追加し、SampleDataItemクラスの_imagePathプロパティが「DarkGray.png」のときに、そのTextBlockコントロールを表示状態にするのだ。

 まず、SampleDataItemクラスに「IsDescriptionDisplay」というプロパティを作成する(次のコード)。

public bool IsDescriptionDisplay
{
  get
  {
    return (base._imagePath == "Assets/DarkGray.png");
  }
}

Public ReadOnly Property IsDescriptionDisplay As Boolean
  Get
    Return (MyBase._imagePath = "Assets/DarkGray.png")
  End Get
End Property

IsDescriptionDisplayプロパティ(上:C#、下:VB)
このほかに、基底クラスであるSampleDataCommonクラスの_imagePathメンバー変数をprivate/Privateからprotected/Protectedに変える必要がある。

 このプロパティをTextBlockコントロールのVisibilityプロパティにバインドしたいのだが、VisibilityプロパティにはTrue/FalseではなくVisibility列挙子(Windows.UI.Xaml名前空間)を与えなければならない。このギャップを埋めるために、BooleanToVisibilityConverterクラス(プロジェクトのCommonフォルダに自動生成されているBooleanToVisibilityConverter.cs/.vbファイル)を使用する(「値コンバータ」と呼ばれる)。

 Standard250x250ItemTemplateテンプレートに次のコードのようにしてTextBlockコントロールを追加する。

<DataTemplate x:Key="Standard250x250ItemTemplate">
    ……
    </Border>
    <TextBlock Text="{Binding Description, Mode=OneWay}"
      Visibility="{Binding IsDescriptionDisplay, Mode=OneWay
        Converter={StaticResource BooleanToVisibilityConverter}}"
      Foreground="LightGreen" Height="150" VerticalAlignment="Top"
      TextWrapping="Wrap" TextTrimming="WordEllipsis" />
    <StackPanel VerticalAlignment="Bottom"
    ……
</DataTemplate>

アイテム・テンプレートにTextBlockを追加した(XAML)
bool値をVisibilityプロパティにバインドするために、値コンバータとして「BooleanToVisibilityConverter」クラスを使っている。

 そして、上のように値コンバータを使うには、そのインスタンスの宣言がXAMLファイル内に必要だ。StandardStyles.xamlファイルに、次のように2か所にコードを追加する。

<ResourceDictionary
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:Common="using:MetroTips027.Common"  ←追加
  >
  ……
  </ResourceDictionary.ThemeDictionaries>

  <Common:BooleanToVisibilityConverter
    x:Key="BooleanToVisibilityConverter"/>  ←追加
  ……

StandardStyles.xamlファイルに値コンバータの宣言も追加した(XAML)
「xmlns:Common」には、BooleanToVisibilityConverterクラスの名前空間を記述する。
値コンバータのインスタンスの宣言は、ResourceDictionary.ThemeDictionariesの後ろなら、どこに書いてもよい。

 これで、次の画像のように、暗い色のイメージのところだけに説明文が表示される。

ap-winrttips_0027_07.gif アイテムごとに情報の表示/非表示を切り替えることができた

 このようにVisibilityプロパティにバインドできるということは、それこそテンプレートの中身を丸ごと切り替えることすらできるのだ。

 なお、新たに値コンバータを作れば、SampleDataItemクラスに余計なプロパティを作らなくても済む。SampleDataItemクラスが画面表示のために「汚染」されるのを嫌うなら、値コンバータを活用するとよい。

そのほか……

 このようにして、データソースのプロパティを直接バインドしたり、値コンバータを介してバインドすることで、色や表示の有無などをアイテムごとに変えることができる。

 そして、SampleDataItemクラスに限ったことだが、このインスタンスは自分が属するグループもプロパティとして持っているので、グループごとに表示を変えることも可能だ。グループごとであれば、アイテムの幅や高さも変えることができる(次の画像)*4

 また、「GroupStyle.HeaderTemplate」テンプレート(GroupedItemsPage.xamlファイル内)では、SampleDataItemGroupクラスのプロパティとバインドすることで、グループ・ヘッダの表現を変更することができる(次の画像)。グループ・ヘッダとアイテムとの画面上での位置関係はグループごとに決まるので、特定のグループ・ヘッダだけ高さを伸ばしてもほかのグループには影響しない。

ap-winrttips_0027_08.gif グループごとにアイテムのサイズやグループ・ヘッダの表現も変えてみた

*4 アイテム単位で幅や高さを変更しても、アイテムが入るセルのサイズはグループ内で最初のアイテムのものにそろえられてしまう。


まとめ

 データ・バインドを活用することで、GridViewコントロールやListViewコントロールのアイテムごとやグループごとに外観を変えることができる。データ・バインディングや値コンバータについて、詳しくは次のドキュメントを参考にしてほしい。

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

WinRT/Metro TIPS

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

@IT Special

- PR -

TechTargetジャパン

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

RSSについて

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

メールマガジン登録

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