Binding to TreeViewItem.IsExpanded. Why does this work?
Asked Answered
R

1

5

I feel like I'm missing some essential concept in WPF databinding. It's always hit-or-miss whether my bindings are going to work for me.

In this example, I want to two-way bind the IsExpanded property of a TreeViewItem to the corresponding property on a bound object. It does work with the first example; it does NOT with the second.

Can anyone explain why? I cannot understand why the second version doesn't work. And I can't help thinking it would save me untold grief if I could.

This works when placed in UserControl.Resources (binding to IsExpanded is done with a Style applied to TreeViewItem:

    <Style TargetType="TreeViewItem">
        <Setter Property="IsExpanded" 
              Value="{Binding Path=IsExpanded, Mode=TwoWay}" />
    </Style>
    <DataTemplate DataType="{x:Type viewModels:FolderItem}">
        <TreeViewItem ItemsSource="{Binding Folders}" 
           IsExpanded="{Binding Mode=TwoWay,Path=IsExpanded}" >
            <TreeViewItem.Header>
                <StackPanel Orientation="Vertical">
                    <Image Source="{Binding IconSource}" 
                          Width="16" Height="16" 
                           Margin="4,0,4,0" VerticalAlignment="Center" />
                    <TextBlock Text="{Binding Title}" 
                            VerticalAlignment="Center" />
                </StackPanel>
            </TreeViewItem.Header>
        </TreeViewItem>
    </DataTemplate>

This does not (direct binding to IsExpanded in the data template):

    <DataTemplate DataType="{x:Type viewModels:FolderItem}">
        <TreeViewItem ItemsSource="{Binding Folders}" 
           IsExpanded="{Binding Path=IsExpanded,Mode=TwoWay}" >
            <TreeViewItem.Header>
                <StackPanel Orientation="Vertical">
                    <Image Source="{Binding IconSource}" 
                          Width="16" Height="16" Margin="4,0,4,0"
                          VerticalAlignment="Center" />
                    <TextBlock Text="{Binding Title}"
                        VerticalAlignment="Center" />
                </StackPanel>
            </TreeViewItem.Header>
        </TreeViewItem>
    </DataTemplate>

The DataTemplate is used in the following Xaml fragment, with the data template given above used to perform data conversion. Documents is an observable list of FolderItems which has been bound correctly.

   <TreeView ItemsSource="{Binding Documents}" />

Both DataTemplates show the file tree. But there is no binding (two-way or otherwise) for IsExpanded in the second case.

Not shown is DataTemplating for FileItem's which are the leaf nodes of FolderItem's..

Refugiorefulgence answered 14/1, 2014 at 20:49 Comment(0)
R
8

The issue is that TreeViewItem is actually a wrapper that is used by the TreeView itself. It needn't (and shouldn't) be part of your DataTemplate, as what you have now is creating a TreeViewItem within a TreeViewItem (the outer one being created by the TreeView, and the inner one being part of the template).

This is why your style works, as it gets applied to all TreeViewItems, both the one that you're declaring (which ends up being meaningless) and the one created by the TreeView.

What you should do is replace your DataTemplate with this:

<HierarchicalDataTemplate ItemsSource="{Binding Folders}">
    <StackPanel Orientation="Vertical">
        <Image Source="{Binding IconSource}" 
               Width="16" Height="16" Margin="4,0,4,0"
               VerticalAlignment="Center" />
        <TextBlock Text="{Binding Title}"
                   VerticalAlignment="Center" />
<HierarchicalDataTemplate>

(Unfortunately I can't test at the moment, but that should at least get you headed in the right direction).

Leave your style as-is to continue binding the property.

Roybal answered 14/1, 2014 at 20:56 Comment(1)
Arg. Not show in the example, was that the treeview had an ItemTemplate as well, which takes precedence over the DataTemplate. But you are correct: Data Templating occurs BEFORE the TreeView control puts the TreeViewItem wrapper around the data-templated object, and the TreeViewItem (unlike other controls) does NOT check to see if it is a TreeViewItem already. Thanks. Blinded by an essential misunderstanding, which you correctly pointed out.Refugiorefulgence

© 2022 - 2024 — McMap. All rights reserved.