wpf how to binding one TreeView to another TreeView

Viewed 278

I'm trying to bind TreeView t2 to TreeView t1 in xaml as below:

<Window x:Class="WpfApp2.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:local="clr-namespace:WpfApp2"
    mc:Ignorable="d"
    Title="MainWindow" Height="620" Width="600">
<Window.Resources>
    <HierarchicalDataTemplate DataType="{x:Type TreeViewItem}" ItemsSource="{Binding Items}">
        <StackPanel Orientation="Horizontal" >
            <TextBlock Background="AliceBlue" Text="{Binding Path=Header, Mode=TwoWay}" Width="220"/>
        </StackPanel>
    </HierarchicalDataTemplate>
</Window.Resources>
<StackPanel>
    <TreeView x:Name="t1">
        <TreeView.Items>
            <TreeViewItem Header="a"></TreeViewItem>
            <TreeViewItem Header="b"></TreeViewItem>
        </TreeView.Items>
    </TreeView>
    <TreeView x:Name="t2" ItemsSource="{Binding Items, ElementName=t1}"></TreeView>
</StackPanel>

I expected t2 will have the same number of nodes with t1. But the result is that, all nodes of t1 are deleted. Why?

I expected result:

enter image description here

The actual result:

enter image description here

2 Answers

I expected t2 will have the same number of nodes with t1. But the result is that, all nodes of t1 are deleted. Why?

Because an instance of a visual element such as for example a TreeViewItem can only appear once in the visual tree. An element can have no more than one visual parent and in this case the items are removed from the first TreeView and added to the second.

So you need to clone each TreeViewItem element that you want to be able to display in both TreeViews.

To answer my own question:

Found the answer from the source code. Seems it's by design (I still don't understand why it's designed like this). If the given item is TreeViewItem, then it will be used as the node container in stead of creating a new one.

ItemsControl.cs(line #1323)

/// <summary>
    /// Return the element used to display the given item
    /// </summary>
    DependencyObject IGeneratorHost.GetContainerForItem(object item)
    {
        DependencyObject container;

        // use the item directly, if possible (bug 870672)
        if (IsItemItsOwnContainerOverride(item))
            container = item as DependencyObject;
        else
            container = GetContainerForItemOverride();

        // the container might have a parent from a previous
        // generation (bug 873118).  If so, clean it up before using it again.
        //
        // Note: This assumes the container is about to be added to a new parent,
        // according to the ItemsControl/Generator/Container pattern.
        // If someone calls the generator and doesn't add the container to
        // a visual parent, unexpected things might happen.
        Visual visual = container as Visual;
        if (visual != null)
        {
            Visual parent = VisualTreeHelper.GetParent(visual) as Visual;
            if (parent != null)
            {
                Invariant.Assert(parent is FrameworkElement, SR.Get(SRID.ItemsControl_ParentNotFrameworkElement));
                Panel p = parent as Panel;
                if (p != null && (visual is UIElement))
                {
                    p.Children.RemoveNoVerify((UIElement)visual);
                }
                else
                {
                    ((FrameworkElement)parent).TemplateChild = null;
                }
            }
        }

        return container;
    }

And TreeView (line #400)

public class TreeView : ItemsControl {
    ...
    /// <summary>
    ///     Returns true if the item is or should be its own container.
    /// </summary>
    /// <param name="item">The item to test.</param>
    /// <returns>true if its type matches the container type.</returns>
    protected override bool IsItemItsOwnContainerOverride(object item)
    {
        return item is TreeViewItem;
    }
    ....
}
Related