1
votes

I have a TreeView that is bound to a collection class that I have no control over. Inside this class is a collection of objects, which each have their own collection of items. This hierarchy is fixed at 3 deep. The children of the TreeView are contained in an ObservableCollection and are updated in the TreeView accordingly. The collections inside each of these objects are not observable, and thus I have to manually re-bind the data to the TreeView each time I add an object to one of the children. This causes all of the expanded children to be reset to an unexpanded state. I am trying to cache the objects that were expanded so they can be re-expanded after re-binding. The children work as I would expect, however when I try to expand a grandchild of the TreeView I get a null object.

To get a TreeViewItem I use the ItemContainerGenerator property of the ItemsControl:

TreeViewItem cfItem = treeView.ItemContainerGenerator
    .ContainerFromItem(obj) as TreeViewItem;
cfItem.IsExpanded = true;

The second level collections all have a reference to their parent object. So since I have many of these object, they are looped over and each uses it's parent object to find the TreeViewItem of it's parent. The order in which they are added to the collection guarantees (I think) that the children will always be processed after the parent. Thus I get this ugly line of code:

qualItem = (
    (TreeViewItem)treeView.ItemContainerGenerator
        .ContainerFromItem(
            ((Child)obj).ParentObject
        )
    )
    .ItemContainerGenerator.ContainerFromItem(obj) as TreeViewItem;

This line always fails when it attempts to get the container from item obj. It successfully gets the parent TreeViewItem, but when attempting to get the Child's TreeViewItem container, I always receive null. The documentation states that ContainerFromItem() returns

"A System.Windows.UIElement that corresponds to the given item. Returns null if the item does not belong to the item collection, or if a System.Windows.UIElement has not been generated for it."

I know that the second level child exists in the parent's item collection. I checked in the debugger in VS 2010, and all of the items are there. I spent a good bit of time on Google searching for an answer and came across someone who said that the container items are created on a background worker thread and may not be generated by the time an attempt is made to get the child item container. I tried waiting for the Status property of the ItemContainerGenerator to be equal to GeneratorStatus.ContainersGenerated, but I still got the same result. I need to somehow obtain the TreeViewItem container for my second level children so they can be re-expanded just like the first level children.

2

2 Answers

0
votes

"A System.Windows.UIElement that corresponds to the given item. Returns null if the item does not belong to the item collection, or if a System.Windows.UIElement has not been generated for it."

Looks like because of Virtualization, the UIElement you look for doesn't exist when you are looking for it. If the collection is not too large, try turning the Virtualization off.

0
votes

The solution is to add another layer of abstraction between the data and the TreeView. The top level collection is an ObservableCollection, and that contains several ViewModels, which implement INotifyPropertyChanged, and also have ObservableCollections of elements that are in the tree beneath it. This system allows WPF to more easily bind and keep track of the elements that are being added and deleted.

My main source of information was this article:

http://msdn.microsoft.com/en-us/magazine/dd419663.aspx