はじめに
例えば
class Store
{
public string Name { get; set; }
public string Prefecture { get; set; }
public int FavoriteCount { get; set; }
}
このようなクラスの List を画面上に表示し、一覧画面を作成したいというようなケースは多々あるかと思います。
言語によってはView上で for
を使用しListをループすることで実現可能だったりしますが
残念ながらWPFではそのような方法では実現できません。
そこで登場するのがItemControlクラスです。
ItemsControlクラスはコレクションを並べて表示することができ、上記のようなことを実現するにはもってこいのクラスです。
また派生クラスにはListBoxクラスやComboBoxクラスなどがあり、UIのカスタマイズなどを行うことができるようになっています。
非常に柔軟性が高く、お世話になる機会も多いだろうと思い、ItemsControlクラス(とその派生クラス)の基本的な使い方を整理していきます。
ソースコードについて
今回はMVVMでいうところのViewModelは以下を使用します。
namespace ItemsControl.ViewModels
{
class MainViewModel
{
public MainViewModel()
{
this.Mall = Enumerable.Range(1 20).Select(x => new Store()
{
Name = "お店" + x,
Prefecture = "東京都",
FavoriteCount = x * 10,
}).ToList();
}
public List<Store> Mall { get; set; }
}
}
またModelとして前述のStoreクラスを使用しています。
ItemsControlでカスタマイズできること
ItemsControlでは4つのプロパティが用意されており、それらを設定することでカスタマイズしています。
プロパティ名 | 概要 | 設定で使用するコントロール |
---|---|---|
Template | ItemsControlそのものを指定します。 | ControlTemplate |
ItemsPanel | 要素の並べ方について指定します。 | ItemsPanaleTemplate |
ItemTemplate | 要素を表示する際のテンプレートを指定します。 | DataTemplate |
ItemContainierStyle | 要素に適応するStyleを指定します。 | Style |
ではそれぞれ見ていきます。
Template
ControlTemplate
を指定することで、コントロール全体の設定を行うことが多いです。
<ListBox ItemsSource="{Binding Mall}">
<ListBox.Template>
<ControlTemplate TargetType="{x:Type ListBox}">
<Border BorderThickness="5" BorderBrush="Red" Background="LightGray">
<ItemsPresenter Margin="5" />
</Border>
</ControlTemplate>
</ListBox.Template>
</ListBox>
見るとわかる通り、表示されているのはListに格納されているオブジェクトの名前になっています。
これはそのオブジェクト(今回の場合は Store
クラス)に toString()
が実装されていないためです。
なので toString()
を実装してあげればこのままでもデータを表示することは可能です。
namespace ItemsControl.ViewModels
{
class MainViewModel
{
public MainViewModel()
{
this.Mall = Enumerable.Range(1 20).Select(x => new Store()
{
Name = "お店" + x,
Prefecture = "東京都",
FavoriteCount = x * 10,
}).ToList();
}
public List<Store> Mall { get; set; }
public override string ToString()
{
return this.Name;
}
}
}
ItemsPanel
ItemsPanelTemplate
でコレクションをどう並べるかを指定します。
指定できるのはPanelクラスの派生クラスである以下の3つです。
なお、デフォルトで StackPanel
が指定されているので、何も指定されていない場合は要素が縦に並びます。
クラス名 | 配置 |
---|---|
StacPanel | 縦に並ぶ |
WrapPanel | 横に並ぶ |
Grid | 指定はできるが子要素を並べる機能がないためすべて重なる |
<ListBox ItemsSource="{Binding Mall}">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
</ListBox>
ちなみにGridで表示すると以下のようにすべての要素が重なります。
これ、どういうときに使うんだろうか...
ItemTemplate
DataTemplate
でコレクションの項目をどのように表示するかを指定します。
要は、各要素単位での表示設定です。
<ListBox ItemsSource="{Binding Mall}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock>
<TextBlock.Text>
<MultiBinding StringFormat="【{0}】{1}">
<Binding Path="Prefecture" />
<Binding Path="Name" />
</MultiBinding>
</TextBlock.Text>
</TextBlock>
<TextBlock Text="{Binding FavoriteCount, StringFormat=お気に入り:{0}}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
ItemContainerStyle
Style
を指定します。
ItemTemplate
と同じく要素ごとの表示方法を指定するプロパティです。
要は、WebページでいうところのCSSです。
<ListBox ItemsSource="{Binding Mall}">
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="OverridesDefaultStyle" Value="True" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ContentControl}">
<Border Background="{TemplateBinding Background}">
<ContentPresenter />
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="LightBlue"/>
</Trigger>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Background" Value="LightGreen" />
</Trigger>
</Style.Triggers>
</Style>
</ListBox.ItemContainerStyle>
</ListBox>
Trigger
では、特定の動作に連動させてStyleを変更することができます。
上記コードだと
- マウスオーバーで要素の背景をLightBlueに
- 選択した要素の背景をLightGreenに
という2点を対応しています。
最終的にできたもの
ここまで整理してきた4つのプロパティをすべて合わせたものがこちらです。
※見栄えを整えるために ItemContainerStyle
に一部Styleを追加しています
<ListBox ItemsSource="{Binding Mall}">
<ListBox.Template>
<ControlTemplate TargetType="{x:Type ListBox}">
<Border BorderThickness="5" BorderBrush="Red" Background="LightGray">
<ItemsPresenter Margin="5" />
</Border>
</ControlTemplate>
</ListBox.Template>
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock>
<TextBlock.Text>
<MultiBinding StringFormat="【{0}】{1}">
<Binding Path="Prefecture" />
<Binding Path="Name" />
</MultiBinding>
</TextBlock.Text>
</TextBlock>
<TextBlock Text="{Binding FavoriteCount, StringFormat=お気に入り:{0}}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="OverridesDefaultStyle" Value="True" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ContentControl}">
<Border Background="{TemplateBinding Background}">
<ContentPresenter />
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="Margin" Value="10" /> <!-- 追加したStykle その1 -->
<Setter Property="Width" Value="100" /> <!-- 追加したStykle その2 -->
<Setter Property="Height" Value="50" /> <!-- 追加したStykle その3 -->
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="LightBlue"/>
</Trigger>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Background" Value="LightGreen" />
</Trigger>
</Style.Triggers>
</Style>
</ListBox.ItemContainerStyle>
</ListBox>
まとめ
ItemsControlでコントロールの外見を変更する方法を整理しました。
変更のためには4つのパラメーターを変更していきます。
一覧系の画面や選択肢を列挙など、使用用途は幅広くお世話になることも多いかと思います。
なお、今回はListBoxを使用しましたが、基本的な考え方はComboBoxも同じなので流用できるはずです。