< XAML ファイル内にデータソースを埋め込む | DrawingBrush >

August 11, 2008

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

リストビューの各行にチェックボックスを配置して、さらにカラムヘッダにも全選択/全選択解除用のチェックボックスを配置しました。ヘッダのチェックボックスのチェック状態を変更すると、すべての行のチェック状態が連動するようなイメージです。
<Window x:Class="CheckListView.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="パン">
    <Window.Resources>
        <XmlDataProvider x:Key="MyListSource" XPath="/Breads">
            <x:XData>
                <Breads xmlns="">
                    <Bread>ツォップフ</Bread>
                    <Bread>クロワッサン</Bread>
                    <Bread>ベーグル</Bread>
                    <Bread>フガス</Bread>
                    <Bread>バケット</Bread>
                    <Bread>リュスティック</Bread>
                    <Bread>ロッゲンフォルコーンブロート</Bread>
                    <Bread>チャパタ</Bread>
                </Breads>
            </x:XData>
        </XmlDataProvider>
    </Window.Resources>
    <ListView x:Name="CheckBoxList" ItemsSource="{Binding Source={StaticResource MyListSource}, XPath=Bread}">
        <ListView.View>
            <GridView>
                <GridViewColumn>
                    <GridViewColumn.HeaderTemplate>
                        <DataTemplate>
                            <CheckBox x:Name="CheckAll" Checked="CheckAll_Checked" Unchecked="CheckAll_Checked"/>
                        </DataTemplate>
                    </GridViewColumn.HeaderTemplate>
                    <GridViewColumn.CellTemplate>
                        <DataTemplate>
                            <CheckBox Name="CheckSelect"/>
                        </DataTemplate>
                    </GridViewColumn.CellTemplate>
                </GridViewColumn>
                <GridViewColumn Header="パンの種類" DisplayMemberBinding="{Binding XPath=.}"/>
            </GridView>
        </ListView.View>
    </ListView>
</Window>
ヘッダのチェックボックス(CheckAll)の Checked と Unchecked のイベントハンドラ CheckAll_Checked の実装は、MSDN ライブラリの 方法 : DataTemplate によって生成された要素を検索する を参考にしました。
private void CheckAll_Checked (object sender, RoutedEventArgs e)
{
    var checkAll = sender as CheckBox;
    if (checkAll != null)
    {
        // ListView の各行を走査
        foreach (var item in CheckBoxList.Items)
        {
            // 行より ListViewItem を取得
            ListViewItem listViewItem = CheckBoxList.ItemContainerGenerator.ContainerFromItem (item) as ListViewItem;
            if (listViewItem != null)
            {
                // ListViewItem の VisualTree より、ContentPresenter を検索する
                ContentPresenter presenter = FindVisualChild<ContentPresenter> (listViewItem);
                
                // ContentPresenter の DataTemplate より、名前でコントロールを検索する
                CheckBox checkSelect = presenter.ContentTemplate.FindName ("CheckSelect", presenter) as CheckBox;
                if (checkSelect != null)
                {
                    // コントロールが見つかれば、チェック状態を追従
                    checkSelect.IsChecked = checkAll.IsChecked;
                }
            }
        }
    }
}
FindVisualTree の実装は上記リンクで紹介されているものと同じです。子要素を再帰検索して、最初に見つかった TChild 型の要素を返します。
private static TChild FindVisualChild<TChild> (DependencyObject parent)
where TChild : DependencyObject
{
    for (int i = 0; i < VisualTreeHelper.GetChildrenCount (parent); i++)
    {
        DependencyObject child = VisualTreeHelper.GetChild (parent, i);
        if (child != null && child is TChild)
        {
            return (TChild) child;
        }
        else
        {
            TChild subItem = FindVisualChild<TChild> (child);
            if (subItem != null)
            {
                return subItem;
            }
        }
    }
    return null;
}
結構レスポンスが悪いので、もう少し効率のよい方法がないか考えます・・・。

トラックバック

このエントリーにトラックバック:
http://frog.raindrop.jp/cgi-bin/mt/mt-tb.cgi/2150

コメント

ItemsControl の場合、itemsControl.ItemContainerGenerator.ContainerFromItem (item) がすでに ContentPresenter だったりするみたい。

コメントする

※ コメントスパム対策のため、コメント本文はおはよう、こんにちわ、こんばんわのいずれかより始めるようにしてください。

name:
email:

※ 必要ですが、表示しません。

url:
情報を保存する ?