ページ

2009年6月19日金曜日

[WPF] ListBox や ComboBox の各行の見た目にアクセスする

たとえば、ボタンをクリックされたら ListBox の 2行目の背景色を変えたい、なんてことがあったとします。
ListBox のそれぞれの行はたぶん Border とか Rectangle とか TextBlock とかを組み合わせて描かれてるんだと思いますが、そいつらにアクセスして背景色を変えたいというわけです。

Silverlight 編はこちら → 「[Silverlight] ListBox や ComboBox の各行の見た目にアクセスする

■ WPF の場合
VisualTreeHelper を使って ListBox の頭からビジュアルツリーをたどっていけば各行のビジュアルにもたどり着くことができます。
具体的には、ビジュアルツリーをたどっていくと行数分だけ ListBoxItem が出てきます。
この ListBoxItem の下がそれぞれの行のビジュアルです。

ただ、WPF には直接各行の ListBoxItem を取得する方法が用意されています。

private void Button_Click(object sender, RoutedEventArgs e)
{
var item = this.listBox1.ItemContainerGenerator.ContainerFromIndex(2);
var v = VisualTreeHelper.GetChild(item, 0);
if (v is Border)
{
((Border)v).Background = Brushes.Red;
}
}

ItemContainerGenerator を使えば各行で使われている項目コンテナを取得することができます。
項目コンテナっていうのは ListBox の場合 ListBoxItem のことです。
ComboBox の場合は ComboBoxItem になりますし、TreeView の場合は、、、と、ものによって実際のクラスは違うものになりますので、これらを一般化して項目コンテナと呼ばれています。(英語だと item container。そのまんまアイテムコンテナでもいいように思いますが、MSDN Library では項目コンテナと訳されてました)
ContainerFromIndex() メソッドを使えばインデックスに対応する項目コンテナを取得できますし、ContainerFromItem() を使えば項目 (たとえば ListBox.Items[2] といったもの) に対応する項目コンテナを取得することができます。
ListBox の頭からビジュアルツリーをたどるよりはずっと使いやすいです。
これらのメソッドの戻り値は DependencyObject ですが、もちろん、実際の型は ListBoxItem となっています。

項目コンテナの子ビジュアルを取り出してやれば、それが実際に表示されている要素です。
WPF の ListBox の場合 Border になってるのでそれの Background を変えてやれば 「2行目の背景色を変える」 ということができます。
また、もう少しビジュアルツリーをたどると ContentPresenter があります。
データテンプレートを使っている場合などは、この ContentPresenter の下がその内容になっています。

ItemContainerGenerator プロパティが使えるのは ItemsControl クラス、および、その子孫クラスです。
なので、ListBox、ComboBox、TreeView などなどで同じような方法で項目コンテナを取得できるはずです。
ちなみに、WPF Toolkit に含まれる DataGrid も ItemsControl の子孫になってるようです。試してないのでわかりませんが、同じように ItemContainerGenerator を使えるのかもしれません。


0 件のコメント:

コメントを投稿