LoginSignup
29
35

More than 3 years have passed since last update.

【WPF】ItemsControlの基本的な使い方

Posted at

はじめに

例えば

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は以下を使用します。

MainViewModel.cs
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

それぞれの持ち場を図にするとこんな感じです。
ItemsControl.jpg

ではそれぞれ見ていきます。

Template

ControlTemplate を指定することで、コントロール全体の設定を行うことが多いです。

MailView.xaml
<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>

表示するとこんな感じ。
2020-04-26_14h23_42.png

見るとわかる通り、表示されているのはListに格納されているオブジェクトの名前になっています。
これはそのオブジェクト(今回の場合は Store クラス)に toString() が実装されていないためです。
なので toString() を実装してあげればこのままでもデータを表示することは可能です。

MainViewModel.cs
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;
        }
    }
}

2020-04-26_14h25_44.png

ItemsPanel

ItemsPanelTemplate でコレクションをどう並べるかを指定します。
指定できるのはPanelクラスの派生クラスである以下の3つです。
なお、デフォルトで StackPanel が指定されているので、何も指定されていない場合は要素が縦に並びます。

クラス名 配置
StacPanel 縦に並ぶ
WrapPanel 横に並ぶ
Grid 指定はできるが子要素を並べる機能がないためすべて重なる
MailView.xaml
<ListBox ItemsSource="{Binding Mall}">
    <ListBox.ItemsPanel>
        <ItemsPanelTemplate>
            <WrapPanel Orientation="Horizontal" />
        </ItemsPanelTemplate>
    </ListBox.ItemsPanel>
</ListBox>

表示するとこんな感じ。
2020-04-26_14h27_07.png

ちなみにGridで表示すると以下のようにすべての要素が重なります。
これ、どういうときに使うんだろうか...
2020-04-26_14h28_37.png

ItemTemplate

DataTemplate でコレクションの項目をどのように表示するかを指定します。
要は、各要素単位での表示設定です。

MailView.xaml
<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>

表示するとこんな感じ。
2020-04-26_14h30_54.png

ItemContainerStyle

Style を指定します。
ItemTemplate と同じく要素ごとの表示方法を指定するプロパティです。
要は、WebページでいうところのCSSです。

MailView.xaml
<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>

表示するとこんな感じ。
2020-04-26_14h45_26.png

Trigger では、特定の動作に連動させてStyleを変更することができます。
上記コードだと

  • マウスオーバーで要素の背景をLightBlueに
  • 選択した要素の背景をLightGreenに

という2点を対応しています。

最終的にできたもの

ここまで整理してきた4つのプロパティをすべて合わせたものがこちらです。
※見栄えを整えるために ItemContainerStyle に一部Styleを追加しています

MailView.xaml
<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>

2020-04-26_14h54_29.png

まとめ

ItemsControlでコントロールの外見を変更する方法を整理しました。
変更のためには4つのパラメーターを変更していきます。
一覧系の画面や選択肢を列挙など、使用用途は幅広くお世話になることも多いかと思います。

なお、今回はListBoxを使用しましたが、基本的な考え方はComboBoxも同じなので流用できるはずです。

参考

WPF 実践
ItemsControl 攻略 ~ 外観のカスタマイズ

29
35
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
29
35