トップ > Tech > CSharp > WPF > DataTemplateで動的に生成されたコントロールにアクセスする

DataTemplateで動的に生成されたコントロールにアクセスする

はじめに

WPF に ListBox などに DataTemplate を定義してバインディングした場合、DataTemplate によって動的に生成されたコントロールや要素には事前に名前が定義できないため、ソースコードからアクセスできない。

たとえば下記のような XAML においてそれぞれの planListBox にアクセスしたいような状況だ。この例では userListBoxItem というリストボックスの中に DataTemplate で生成された planListBox というリストボックスが含まれていることを仮定している。

<ListBox Name="userListBox">
    <ListBox.ItemTemplate>
        <DataTemplate>
            <StackPanel>
                <TextBlock Text="{Binding Path=DisplayText}" />
                <ListBox Name="planListBox">
                    <ListBox.ItemTemplate>
                        〜中略〜
                    </ListBox.ItemTemplate>
                </ListBox>
            </StackPanel>
        </DataTemplate>
    </ListBox.ItemTemplate>
</ListBox>

対策

ユーティリティメソッドの定義

これらの要素を参照するためにはちょっと込み入った手順が必要である。まず、下記の FindVisualChild メソッドを定義する。これは MSDN のソースそのままだ。

private T FindVisualChild<T>(DependencyObject obj) where T : DependencyObject
{
    for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++)
    {
        DependencyObject child = VisualTreeHelper.GetChild(obj, i);
        if (child != null && child is T)
            return (T)child;
        else
        {
            T childOfChild = FindVisualChild<T>(child);
            if (childOfChild != null)
                return childOfChild;
        }
    }
    return null;
}

要素へのアクセス

実際には下記のように使う。

ListBoxItem userListBoxItem = (ListBoxItem)(userListBox.ItemContainerGenerator.ContainerFromItem(userListBox.SelectedItem));
ContentPresenter contentPresenter = FindVisualChild<ContentPresenter>(userListBoxItem);
DataTemplate dataTemplate = contentPresenter.ContentTemplate;
ListBox planListBox = (ListBox)dataTemplate.FindName("planListBox", contentPresenter);
  1. リストボックスの ItemContainerGenerator.ContainerFromItem にリストボックス中のアイテムを渡すとそのコンテナとなる UIElement が得られる。ここでは ListBoxItem であることが自明なので、ListBoxItem にキャストしている。
  2. さきほど定義した FindVisualChild メソッドでアイテムの ContentPresenter を得る。
  3. ContentPresenter.ContentTemplate に DataTemplate が格納されているので、これの FindName で対象のリストボックスの名前を指定する。これで対象の要素が得られる。

参考

(2010/09/15 16:02:45)
20148
プロフィール

Kenz Yamada(山田研二)。1984年生。大阪。ちょっとずつ好きなプログラム作ってます。 好きなものはカメラと旅行。ガジェットや身の回り、ちょっとこだわります。 詳しくは Web mixi で。

Bookmark and Share