データ・テンプレートやアイテム・コンテナ・スタイルを動的に切り替える「セレクタ」を使ってGridView/ListViewコントロールの外観を大きく変化させる方法を説明する。
powered by Insider.NET
データの一覧をグループごとに分けて表示するときによく使うのが、GridViewコントロールとListViewコントロールだ。その外観を変える方法として、前回はデータ・バインドを応用する方法を説明したが、外観にもっと大胆な変化を付けることはできないだろうか? 例えば、よく見かける、グリッドに大小のセルが混在したデザインはどのようにして実現しているのだろうか?
このような場合に便利に使えるのが、データ・テンプレートやアイテム・コンテナ・スタイルを動的に切り替える「セレクタ」だ。そこで本稿では、セレクタを使ってGridViewコントロールの外観に変化を付ける方法を説明する。本稿のサンプルは「Windows Store app samples:MetroTips #28」からダウンロードできる。
●事前準備
Windows 8(以降、Win 8)向けのWindowsストア・アプリを開発するには、Win 8とVisual Studio 2012(以降、VS 2012)が必要である。これらを準備するには、第1回のTIPSを参考にしてほしい。本稿では64bit版Win 8 ProとVS 2012 Express for Windows 8を使用している。
●データ・テンプレートを切り替えるには?
複数のデータ・テンプレートを用意しておき、DataTemplateSelectorクラス(Windows.UI.Xaml.Controls名前空間)を継承して作ったセレクタで切り替える。
前回と同様にVS 2012の「グリッド・アプリケーション」プロジェクト・テンプレートから始めよう。グリッド・アプリケーションの起動時には、GroupedItemsPage.xamlファイルで定義される画面が表示される。この画面では、GridViewコントロールを使ってアイテムが表示されるが、そのときにはGridViewコントロールのItemTemplateプロパティに設定されているデータ・テンプレートが使われる。そして、このデータ・テンプレートはデフォルトでは「Standard250x250ItemTemplate」となっている*1。これを次の画像のように別のデータ・テンプレートと動的に切り替えられるようにしてみよう。
*1 Standard250x250ItemTemplateは、プロジェクトのCommonフォルダのStandardStyles.xamlファイルに記述されている。
まず、新しいデータ・テンプレートを「App.xaml」ファイルのResourceDictionary要素の中に追加する。名前は「itemGridView2ndItemTemplate」としておこう(次のコード)。
<!-- itemGridViewの最初のアイテムに適用するテンプレート -->
<DataTemplate x:Key="itemGridView2ndItemTemplate">
<Grid Background="DarkRed" Width="250" Height="250">
<TextBlock Text="{Binding Title}" Foreground="White"
FontSize="36" HorizontalAlignment="Center"
VerticalAlignment="Center" />
</Grid>
</DataTemplate>
次に、上述の2つのテンプレートを動的に切り替えるためのクラスを新しく追加する。クラス名は「ItemGridViewTemplateSelector」としよう。DataTemplateSelectorクラスを継承させ、SelectTemplateCoreメソッドを次のようにオーバーライドする。
このメソッドには表示しようとしているデータが引数として渡されるので、メソッドの側では受け取ったデータを調べてそれに見合ったテンプレートを返してやればよいのだ。ここでは、表示するデータ(=プロジェクト・テンプレートに含まれるSampleDataItemクラスのオブジェクト)のUniqueIdプロパティに「Item-1」という文字列が含まれているときに、新しく作ったテンプレートが使われるようにした。
class ItemGridViewTemplateSelector : DataTemplateSelector
{
protected override DataTemplate SelectTemplateCore(
object item, DependencyObject container)
{
var data = item as SampleDataItem;
if (data.UniqueId.Contains("Item-1"))
{
return (DataTemplate)App.Current.Resources["itemGridView2ndItemTemplate"];
}
return (DataTemplate)App.Current.Resources["Standard250x250ItemTemplate"];
}
}
Public Class ItemGridViewTemplateSelector
Inherits DataTemplateSelector
Protected Overrides Function SelectTemplateCore( _
item As Object, container As DependencyObject) As DataTemplate
Dim data As SampleDataItem = item
If (data.UniqueId.Contains("Item-1")) Then
Return App.Current.Resources("itemGridView2ndItemTemplate")
End If
Return App.Current.Resources("Standard250x250ItemTemplate")
End Function
End Class
最後に、GroupedItemsPage.xamlファイルを書き変えて、上で作成したセレクタを使うようにする。
まず、ファイルの先頭の方にあるPage.Resources要素の中に次のコードを追加して、セレクタのインスタンスを宣言する。
<local:ItemGridViewTemplateSelector x:Key="ItemGridViewTemplateSelector" />
次に、GridViewコントロールでItemTemplateプロパティを設定している箇所を見つける(次のコード)。
【変更前】
ItemTemplate="{StaticResource Standard250x250ItemTemplate}"
この行は削除し、代わりに次のコードを挿入する。
【変更後】
ItemTemplateSelector="{StaticResource ItemGridViewTemplateSelector}"
以上で完了だ。実行すると、冒頭に掲げた画像のように表示される。
●アイテム・コンテナ・スタイルを切り替えるには?
アイテム・コンテナに適用するためのスタイルを複数用意しておき、StyleSelectorクラス(Windows.UI.Xaml.Controls名前空間)を継承して作ったセレクタで切り替える。
GroupedItemsPage.xamlファイルでは、アイテム・グループの外観を指定するためにGridView.GroupStyleプロパティ内でItemsPanelTemplateとしてVariableSizedWrapGridコントロール(Windows.UI.Xaml.Controls名前空間)が指定されている。このVariableSizedWrapGridコントロールにはColumnSpan/RowSpanの2つの添付プロパティがあり、これらを指定することで複数のセルにまたがった大きなサイズでアイテムを表示できるようになるのだ。そこで、次の画像のように特定のアイテムだけ縦横2倍のサイズにしてみよう。
この場合に注意すべきは、ColumnSpan/RowSpan添付プロパティを適用する先だ。これらのプロパティは、それぞれのアイテムではなく、アイテムを保持しているアイテム・コンテナに適用しなければならない。
今回の場合は次の図のように、アイテムはDataTemplate(デフォルトのStandard250x250ItemTemplateと先ほど作成したitemGridView2ndItemTemplate)を使って表示しており(=緑色の枠)、その入れ物であるアイテム・コンテナはGridViewItem(=黄色の枠)である。そして、ColumnSpan/RowSpan添付プロパティの指定は、GridViewItemに対して行わなければならないのだ。この場合、テンプレート・セレクタではなく、アイテム・コンテナに対するスタイル・セレクタを使わなければならない。
それでは、まず切り替えるスタイルから作っていこう。ここでは縦横2倍のサイズで表示するスタイルと、標準サイズのスタイルの2種類のスタイルを作る。これらも、「App.xaml」ファイルのResourceDictionary要素の中に追加する。
<!-- itemGridViewのアイテムのコンテナに適用するスタイル(標準) -->
<Style x:Key="itemGridViewContainer_NormalSize" TargetType="GridViewItem">
<Setter Property="Width" Value="250" />
<Setter Property="Height" Value="250" />
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
<Setter Property="VerticalContentAlignment" Value="Stretch" />
</Style>
上のコードは標準サイズのスタイルだ。幅と高さの250pxは、中に入るデータ・テンプレートに合わせてある。中に入るコンテンツの横方向/縦方向の配置を「Stretch」としてあるのは、後ほど説明する。
次のコードは、縦横2倍のサイズで表示するスタイルだ。標準サイズのスタイルと比べて、幅と高さを2倍にしたほか、前述のColumnSpan/RowSpan添付プロパティの設定を追加した。
<!-- itemGridViewのアイテムのコンテナに適用するスタイル(2倍サイズ) -->
<Style x:Key="itemGridViewContainer_DoubleSize" TargetType="GridViewItem">
<Setter Property="VariableSizedWrapGrid.RowSpan" Value="2" />
<Setter Property="VariableSizedWrapGrid.ColumnSpan" Value="2" />
<Setter Property="Width" Value="500" />
<Setter Property="Height" Value="500" />
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
<Setter Property="VerticalContentAlignment" Value="Stretch" />
</Style>
次に、これら2つのスタイルを動的に切り替えるためのクラスを新しく追加する。クラス名は「ItemGridViewItemContainerStyleSelector」としよう。StyleSelectorクラスを継承させ、SelectStyleCoreメソッドを次のようにオーバーライドする。このメソッドにも表示しようとしているデータが引数として渡されるので、ここでは、表示するデータのUniqueIdプロパティに「Item-3」という文字列が含まれているときに、2倍サイズのスタイルを返すようにした。
class ItemGridViewItemContainerStyleSelector : StyleSelector
{
protected override Style SelectStyleCore(
object item, DependencyObject container)
{
var data = item as SampleDataItem;
if (data.UniqueId.Contains("Item-3"))
{
return (Style)App.Current.Resources["itemGridViewContainer_DoubleSize"];
}
return (Style)App.Current.Resources["itemGridViewContainer_NormalSize"];
}
}
Public Class ItemGridViewItemContainerStyleSelector
Inherits StyleSelector
Protected Overrides Function SelectStyleCore( _
item As Object, container As DependencyObject) As Style
Dim data As SampleDataItem = item
If (data.UniqueId.Contains("Item-3")) Then
Return App.Current.Resources("itemGridViewContainer_DoubleSize")
End If
Return App.Current.Resources("itemGridViewContainer_NormalSize")
End Function
End Class
最後に、GroupedItemsPage.xamlファイルを書き変えて、作成したセレクタを使うようにする。
まず、ファイルの先頭の方にある「Page.Resources」要素の中に次のコードを追加して、セレクタのインスタンスを宣言する。
<local:ItemGridViewItemContainerStyleSelector
x:Key="ItemGridViewItemContainerStyleSelector" />
次に、GridViewコントロールの宣言部分を見つけ、次のコードを挿入する。
<GridView
X:Name="itemGridView"
……
ItemContainerStyleSelector
="{StaticResource ItemGridViewItemContainerStyleSelector}"
……
これで完成かと思って実行してみると、アイテム・コンテナを説明した図のようになってしまう。アイテム・コンテナのサイズは縦横2倍になったのだが、その中のデータ・テンプレートのサイズは元のままなのだ。これはアイテムのサイズを、テンプレートで指定したままになっているからだ。
それを修正するには、Standard250x250ItemTemplateに指定されている幅と高さを削除する*2。
<DataTemplate x:Key="Standard250x250ItemTemplate">
<Grid HorizontalAlignment="Left"><!-- Width="250" Height="250" -->
<!-- Width/Heightの設定を削除した -->
……
すると、標準サイズのスタイルで指定したコンテンツの横方向/縦方向の配置(=「Stretch」)が効いて、アイテムがアイテム・コンテナ一杯に広がって表示されるのである。
以上で完成だ。実行すると、冒頭に掲げた画像のように表示される。
*2 同様に、itemGridView2ndItemTemplateからも幅と高さの指定を削除する。今回の例では、このテンプレートは2倍サイズで表示されないので実害はないが、「標準サイズのスタイル」で幅と高さを指定するようにしたので、テンプレートでのサイズの指定は不要である。
なお、このようにしてサイズの異なるセルを混在させたとき、並び順によっては次の画像のように「穴」が開いてしまうことがあるので注意してほしい。
●まとめ
GridViewコントロールやListViewコントロールでは、動的にテンプレートやスタイルを切り替えることができる。どちらも、新しいテンプレート/スタイルを用意し、セレクタのクラスを作り、画面のリソースにセレクタのインスタンスを定義し、GridView/ListViewコントロールの適切なプロパティにセレクタを指定する、という手順は同じである。
2回にわたってGridView/ListViewコントロールの外観に変化を付ける方法を説明してきた。これには、ListViewコントロールで1行おきに色を変えたり、GridViewコントロールのグループの中にさらに小見出しを入れたりするなどといった、さまざまな応用が考えられる。この分野の日本語ドキュメントはまだ乏しい状況ではあるが、いろいろと試してみてほしい。
Copyright© Digital Advantage Corp. All Rights Reserved.