Home / ぼやきごと / 2015-02-25
2015-02-25

WPF・XAML: ItemsControl の各項目表示にインデックス値を使う

次のようなごく単純なViewModelクラスを用意します。

すべて開くすべて閉じる
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 
 
 
 
-
|
-
|
-
|
!
|
|
!
!
using System;
using System.Collections.ObjectModel;
 
namespace Sample
{
    public class SampleViewModel
    {
        public SampleViewModel()
        {
            this.Texts = Array.AsReadOnly(new[] { "萃香", "川内", "ラムザ", "弦巻マキ" });
        }
 
        public ReadOnlyCollection<string> Texts { get; private set; }
    }
}

そしてこれを用いてメインウィンドウのXAMLを次のように書きます。

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
<Window
    x:Class="Sample.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:sample="clr-namespace:Sample"
    Title="Sample"
    SizeToContent="WidthAndHeight"
    MinWidth="200">
    <Window.DataContext>
        <sample:SampleViewModel />
    </Window.DataContext>
    <StackPanel>
        <ListBox ItemsSource="{Binding Texts}" />
    </StackPanel>
</Window>

すると次のように表示されます。

サンプルウィンドウ表示1

ここで、各項目表示にインデックス値を使いたいなー、なんて思ったりすることがあります。
それを実現する方法としては次の2通りが考えられます。

  • 各要素にインデックス値自体を持つリストデータをバインドする。
  • AlternationCount プロパティと ItemsControl.AlternationIndex 添付プロパティを用いる。

前者が正攻法ですが、とにかくインデックスの表示さえできればいい場合にわざわざ元のリストデータをラップするというのは面倒です。(元のリストデータが ObservableCollection だったりすると尚更)

後者はやや邪道ですが、ViewModelクラスに手を入れる必要はなく、XAMLの書き換えだけで済むという手軽さが魅力です。
具体的にはXAMLを次のように書き換えます。

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
<Window
    x:Class="Sample.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:sample="clr-namespace:Sample"
    Title="Sample"
    SizeToContent="WidthAndHeight"
    MinWidth="200">
    <Window.DataContext>
        <sample:SampleViewModel />
    </Window.DataContext>
    <StackPanel>
        <ListBox
            ItemsSource="{Binding Texts}"
            AlternationCount="{Binding Items.Count, RelativeSource={RelativeSource Self}}">
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <StackPanel Orientation="Horizontal">
                        <ContentControl>
                            <Binding
                                Path="(ItemsControl.AlternationIndex)"
                                RelativeSource="{RelativeSource AncestorType=ListBoxItem}" />
                        </ContentControl>
                        <TextBlock Text=" : " />
                        <TextBlock Text="{Binding}" />
                    </StackPanel>
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>
    </StackPanel>
</Window>

すると次のように表示されます。

サンプルウィンドウ表示2

AlternationCount プロパティと ItemsControl.AlternationIndex 添付プロパティは、本来なら「1項目おきにスタイルを変更する」といった用途で利用されます。

しかし AlternationCount プロパティの値を総項目数(即ち Items.Count)と同じにしてしまえば、 ItemsControl.AlternationIndex 添付プロパティの値は項目のインデックスと等しくなります。
あとは DataTemplate なり ControlTemplate なりを用いて ItemsControl.AlternationIndex 添付プロパティ値をバインドして表示すればいいというわけです。
バインドする際、 RelativeSourceItemsControl.AlternationIndex 添付プロパティ設定対象のコントロール型(ListBox なら ListBoxItem)を参照するのを忘れずに。

XAMLはGUIを完全にテキストベースで書けるので楽しいですね。
フォームデザイナを使っていた頃はGUI作成なんて大嫌いでしたが、XAMLならいくらでも書こうという気になれます。

Category: [C#][WPF・XAML][プログラミング] - 2015-02-25 23:46:59