3

When you set VirtualizingStackPanel.IsVirtualizing="True" on a TreeView in WPF:

<Window x:Class="WpfApplication2.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <TreeView Name="MainTree" VirtualizingStackPanel.IsVirtualizing="True" VirtualizingStackPanel.VirtualizationMode="Recycling"/>
    </Grid>
</Window>

The following code-behind with a call to TreeViewItem.BringIntoView() does not work:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace WpfApplication2
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            TreeViewItem lastTvi2 = null;
            for (int i = 0; i < 100; i++)
            {
                TreeViewItem tvi = new TreeViewItem();
                tvi.Header = "Hello";
                MainTree.Items.Add(tvi);
                for (int j = 0; j < 100; j++)
                {
                    TreeViewItem tvi2 = new TreeViewItem();
                    tvi2.Header = "World";
                    tvi.Items.Add(tvi2);
                    lastTvi2 = tvi2;
                }
            }
            lastTvi2.BringIntoView();
        }
    }
}

Yet when you remove the line VirtualizingStackPanel.IsVirtualizing="True", it will bring the lastTvi2 object (last item in the tree) into the view. Why does this break the TreeViewItem.BringIntoView() method? Is it normal behavior, or is it a .NET bug?

Alexandru
  • 12,264
  • 17
  • 113
  • 208
  • SInce the item has been not generated it wont work,it is normal behaviour(as far as i know ,sorry if it is wrong :) ) https://social.msdn.microsoft.com/forums/vstudio/en-US/8a7cc873-ac8c-4c6e-a6a0-b3bd8559c417/bringintoview-virtualizing-treeview – WPFUser Apr 24 '15 at 05:22
  • 1
    Since your StackPanel is virtualized, the items out of view "do not exist". That is the intend of virtualization and saves a lot of performence by the UI. The problem is that you cannot scroll to a non-existing item (an item out of view). There was a solution using the ItemGeneratedContainer or something. I am looking it up for you and will post it if I can find it. – Noel Widmer Apr 24 '15 at 05:37
  • I see, so virtualization as far as WPF is concerned basically means, "render only visible content, and if other content is requested, render that other content on demand". Much like the default behavior of Objective-C list views. – Alexandru Apr 24 '15 at 13:38

1 Answers1

2

I have stumbled over this issue a couple of times and come up with a solution that works for me using a standard treeview and standard treeviewitem

<UserControl
...
<TreeView
    VirtualizingPanel.IsVirtualizing="True"
    VirtualizingPanel.VirtualizationMode="Recycling"/>
...
</UserControl>

public static class TreeViewHelper
{
    private static T FindVisualChild<T>(System.Windows.Media.Visual visual) where T : System.Windows.Media.Visual
    {
        for (int i = 0; i < VisualTreeHelper.GetChildrenCount(visual); i++)
        {
            System.Windows.Media.Visual child = (System.Windows.Media.Visual)VisualTreeHelper.GetChild(visual, i);
            if (child != null)
            {
                T correctlyTyped = child as T;
                if (correctlyTyped != null)
                {
                    return correctlyTyped;
                }

                T descendent = FindVisualChild<T>(child);
                if (descendent != null)
                {
                    return descendent;
                }
            }
        }

        return null;
    }

    public static void BringIntoView(TreeViewItem item)
    {
        ItemsControl parent = item.Parent as ItemsControl;

        if (parent != null)
        {
            System.Windows.Controls.VirtualizingStackPanel itemHost = FindVisualChild<System.Windows.Controls.VirtualizingStackPanel>(parent);

            if (itemHost != null)
            {
                itemHost.BringIndexIntoViewPublic(parent.Items.IndexOf(item));
                item.Focus();
            }
        }
    }
}