none
TreeView.ItemContainerGenerator.ContainerFromItem returns null

    Question

  • Hi, Geeks!

    I cannot understand why the TreeView.ItemContainerGenerator.ContainerFromItem method always returns null. I am calling this method after the tree has been totally initialized and already contains some value.

    here is the code:

        

    Public Sub CollapseTree()
            If sideTreeView.Items.Count > 0 Then
                For Each tItem In sideTreeView.Items(0).Children
                    Dim treeItem As TreeViewItem = TryCast(sideTreeView.ItemContainerGenerator.ContainerFromItem(tItem), TreeViewItem)
                    treeItem.IsExpanded = False
                Next
            End If
        End Sub
      tItem is my custom hierarchical object. Please, tell me the reason for Null referenced object exception returned for above code.
    Wednesday, January 14, 2009 7:30 AM

Answers

  • I hate this "feature", but we had to keep ItemContainerGenerator compatible with how WPF's one works.

    ItemContainerGenerator is hierarchly aware. Meaning that the TreeView.ItemContainerGenerator will only know direct children of the TreeView. and the TreeViewItem.ItemContainerGenerator will only knows it's own direct children.
    So essentially a TreeView has as many ItemContainerGenerators as it has TreeViewItems+1. And again, each of these ItemContainerGenerators only knows about the children of the instance it belongs to.

    Your code snippet is looking at the Children of the 1st TreeViewItem, but instead of querying the 1st TreeViewItem's ItemContainerGenerator is querying the TreeView's ItemContainerGenerator.

    So this change should fix the issue you're seeing:

    Public Sub CollapseTree()
            If sideTreeView.Items.Count > 0 Then
                For Each
    tItem In sideTreeView.Items(0).Children
                    Dim treeItem As TreeViewItem = TryCast(sideTreeView.Items(0).ItemContainerGenerator.ContainerFromItem(tItem), TreeViewItem)
                    treeItem.IsExpanded = False
                Next
            End If
        End Sub

    However, You probably saying to yourself "Well, this sucks, they know it sucks, I wanna die". Well, don't. 
    We're looking intensively as to where we can improve on the existing WPF TreeView without breaking WPF Compat.
    You can read more about these efforts @ http://blogs.msdn.com/sburke/archive/2008/11/11/wpf-and-silverlight-toolkit-compatibility.aspx 

    In the meanwhile, use these 2 extension methods:

    public static class TreeViewExtensions

    {

        public static TreeViewItem ContainerFromItem(this TreeView treeView, object item)

        {

            TreeViewItem containerThatMightContainItem = (TreeViewItem)treeView.ItemContainerGenerator.ContainerFromItem(item);

            if (containerThatMightContainItem != null)

                return containerThatMightContainItem;

            else

                return ContainerFromItem(treeView.ItemContainerGenerator, treeView.Items, item);

        }

     

        private static TreeViewItem ContainerFromItem(ItemContainerGenerator parentItemContainerGenerator, ItemCollection itemCollection, object item)

        {

            foreach (object curChildItem in itemCollection)

            {

                TreeViewItem parentContainer = (TreeViewItem)parentItemContainerGenerator.ContainerFromItem(curChildItem);

                TreeViewItem containerThatMightContainItem = (TreeViewItem)parentContainer.ItemContainerGenerator.ContainerFromItem(item);

                if (containerThatMightContainItem != null)

                    return containerThatMightContainItem;

                TreeViewItem recursionResult = ContainerFromItem(parentContainer.ItemContainerGenerator, parentContainer.Items, item);

                if (recursionResult != null)

                    return recursionResult;

            }

            return null;

        }

     

        public static object ItemFromContainer(this TreeView treeView, TreeViewItem container)

        {

            TreeViewItem itemThatMightBelongToContainer = (TreeViewItem)treeView.ItemContainerGenerator.ItemFromContainer(container);

            if (itemThatMightBelongToContainer != null)

                return itemThatMightBelongToContainer;

            else

                return ItemFromContainer(treeView.ItemContainerGenerator, treeView.Items, container);

        }

     

        private static object ItemFromContainer(ItemContainerGenerator parentItemContainerGenerator, ItemCollection itemCollection, TreeViewItem container)

        {

            foreach (object curChildItem in itemCollection)

            {

                TreeViewItem parentContainer = (TreeViewItem)parentItemContainerGenerator.ContainerFromItem(curChildItem);

                TreeViewItem itemThatMightBelongToContainer = (TreeViewItem)parentContainer.ItemContainerGenerator.ItemFromContainer(container);

                if (itemThatMightBelongToContainer != null)

                    return itemThatMightBelongToContainer;

                TreeViewItem recursionResult = ItemFromContainer(parentContainer.ItemContainerGenerator, parentContainer.Items, container) as TreeViewItem;

                if (recursionResult != null)

                    return recursionResult;

            }

            return null;

        }

    }

    And your code snippet would even look better:

    Public Sub CollapseTree()
            If sideTreeView.Items.Count > 0 Then
                For Each
    tItem In sideTreeView.Items(0).Children
                    Dim treeItem As TreeViewItem = TryCast(sideTreeView.ContainerFromItem(tItem), TreeViewItem)
                    treeItem.IsExpanded = False
                Next
            End If
        End Sub

    Hopefully, this helps.

     

    Sincerely,

     

     

    Friday, January 16, 2009 8:21 PM

All replies

  •  Hello? Is there any note for post above?

    Wednesday, January 14, 2009 8:26 PM
  • I hate this "feature", but we had to keep ItemContainerGenerator compatible with how WPF's one works.

    ItemContainerGenerator is hierarchly aware. Meaning that the TreeView.ItemContainerGenerator will only know direct children of the TreeView. and the TreeViewItem.ItemContainerGenerator will only knows it's own direct children.
    So essentially a TreeView has as many ItemContainerGenerators as it has TreeViewItems+1. And again, each of these ItemContainerGenerators only knows about the children of the instance it belongs to.

    Your code snippet is looking at the Children of the 1st TreeViewItem, but instead of querying the 1st TreeViewItem's ItemContainerGenerator is querying the TreeView's ItemContainerGenerator.

    So this change should fix the issue you're seeing:

    Public Sub CollapseTree()
            If sideTreeView.Items.Count > 0 Then
                For Each
    tItem In sideTreeView.Items(0).Children
                    Dim treeItem As TreeViewItem = TryCast(sideTreeView.Items(0).ItemContainerGenerator.ContainerFromItem(tItem), TreeViewItem)
                    treeItem.IsExpanded = False
                Next
            End If
        End Sub

    However, You probably saying to yourself "Well, this sucks, they know it sucks, I wanna die". Well, don't. 
    We're looking intensively as to where we can improve on the existing WPF TreeView without breaking WPF Compat.
    You can read more about these efforts @ http://blogs.msdn.com/sburke/archive/2008/11/11/wpf-and-silverlight-toolkit-compatibility.aspx 

    In the meanwhile, use these 2 extension methods:

    public static class TreeViewExtensions

    {

        public static TreeViewItem ContainerFromItem(this TreeView treeView, object item)

        {

            TreeViewItem containerThatMightContainItem = (TreeViewItem)treeView.ItemContainerGenerator.ContainerFromItem(item);

            if (containerThatMightContainItem != null)

                return containerThatMightContainItem;

            else

                return ContainerFromItem(treeView.ItemContainerGenerator, treeView.Items, item);

        }

     

        private static TreeViewItem ContainerFromItem(ItemContainerGenerator parentItemContainerGenerator, ItemCollection itemCollection, object item)

        {

            foreach (object curChildItem in itemCollection)

            {

                TreeViewItem parentContainer = (TreeViewItem)parentItemContainerGenerator.ContainerFromItem(curChildItem);

                TreeViewItem containerThatMightContainItem = (TreeViewItem)parentContainer.ItemContainerGenerator.ContainerFromItem(item);

                if (containerThatMightContainItem != null)

                    return containerThatMightContainItem;

                TreeViewItem recursionResult = ContainerFromItem(parentContainer.ItemContainerGenerator, parentContainer.Items, item);

                if (recursionResult != null)

                    return recursionResult;

            }

            return null;

        }

     

        public static object ItemFromContainer(this TreeView treeView, TreeViewItem container)

        {

            TreeViewItem itemThatMightBelongToContainer = (TreeViewItem)treeView.ItemContainerGenerator.ItemFromContainer(container);

            if (itemThatMightBelongToContainer != null)

                return itemThatMightBelongToContainer;

            else

                return ItemFromContainer(treeView.ItemContainerGenerator, treeView.Items, container);

        }

     

        private static object ItemFromContainer(ItemContainerGenerator parentItemContainerGenerator, ItemCollection itemCollection, TreeViewItem container)

        {

            foreach (object curChildItem in itemCollection)

            {

                TreeViewItem parentContainer = (TreeViewItem)parentItemContainerGenerator.ContainerFromItem(curChildItem);

                TreeViewItem itemThatMightBelongToContainer = (TreeViewItem)parentContainer.ItemContainerGenerator.ItemFromContainer(container);

                if (itemThatMightBelongToContainer != null)

                    return itemThatMightBelongToContainer;

                TreeViewItem recursionResult = ItemFromContainer(parentContainer.ItemContainerGenerator, parentContainer.Items, container) as TreeViewItem;

                if (recursionResult != null)

                    return recursionResult;

            }

            return null;

        }

    }

    And your code snippet would even look better:

    Public Sub CollapseTree()
            If sideTreeView.Items.Count > 0 Then
                For Each
    tItem In sideTreeView.Items(0).Children
                    Dim treeItem As TreeViewItem = TryCast(sideTreeView.ContainerFromItem(tItem), TreeViewItem)
                    treeItem.IsExpanded = False
                Next
            End If
        End Sub

    Hopefully, this helps.

     

    Sincerely,

     

     

    Friday, January 16, 2009 8:21 PM
  • Thank you for help. The thing that I have actually done is I extended the TreeViewItem object with additional properties and methods that my custom hierarchical object had, and used that class while building my treeview dynamically. That works fine for me.
    Tuesday, January 20, 2009 3:44 AM
  • Hi Justin,

    I am trying to use your suggestion to expand the root items in a treeview: 

     

    Public Sub CollapseTree()
            If sideTreeView.Items.Count > 0 Then
                For Each
    tItem In sideTreeView.Items(0).Children
                    Dim treeItem As TreeViewItem = TryCast(sideTreeView.ContainerFromItem(tItem), TreeViewItem)
                    treeItem.IsExpanded = False
                Next
            End If
        End Sub

    My c# version is this:
     

    foreach (myItemType pcItem in CategoryTreeView.Items)
                    {
                        TreeViewItem tvi = (TreeViewItem)CategoryTreeView.ContainerFromItem(pcItem);
                        tvi.IsExpanded = true;
                    }

    However, at runtime, I get an exception in the helper class you provided above at this line:
     

    TreeViewItem containerThatMightContainItem = (TreeViewItem)parentContainer.ItemContainerGenerator.ContainerFromItem(item);
    
    parentContainer seems to be null.
    What am I missing? I spent ages on investigating this... :(
    vitya
    Wednesday, April 08, 2009 9:34 AM
  • same issue with Vitya~~

    My toolkit is "Microsoft Silverlight 3 Toolkit March 2009"

    Wednesday, April 22, 2009 4:02 AM
  • Hello Justin...

    Im getting a null Exception in this line:

    1            private static TreeViewItem ContainerFromItem(ItemContainerGenerator parentItemContainerGenerator, ItemCollection itemCollection, object item)
    2            {
    3                foreach (object curChildItem in itemCollection)
    4                {
    5                    TreeViewItem parentContainer = (TreeViewItem)parentItemContainerGenerator.ContainerFromItem(curChildItem);
    6                    //parentContainer <-- is now null !=?!?!?
    7    
    8                    TreeViewItem containerThatMightContainItem = (TreeViewItem)parentContainer.ItemContainerGenerator.ContainerFromItem(item);
    9                    if (containerThatMightContainItem != null)
    10                       return containerThatMightContainItem;
    11                   TreeViewItem recursionResult = ContainerFromItem(parentContainer.ItemContainerGenerator, parentContainer.Items, item);
    12                   if (recursionResult != null)
    13                       return recursionResult;
    14               }
    15               return null;
    16           }
    
     

    parentContainer is null Sad
    Monday, May 11, 2009 8:16 AM
  • Hi,

    Sorry about that, I forogt to account for virtualized TreeViewItems. My bad.

    public static class TreeViewExtensions

    {

        public static TreeViewItem ContainerFromItem(this TreeView treeView, object item)

        {

            TreeViewItem containerThatMightContainItem = (TreeViewItem)treeView.ItemContainerGenerator.ContainerFromItem(item);

            if (containerThatMightContainItem != null)

                return containerThatMightContainItem;

            else

                return ContainerFromItem(treeView.ItemContainerGenerator, treeView.Items, item);

        }

     

        private static TreeViewItem ContainerFromItem(ItemContainerGenerator parentItemContainerGenerator, ItemCollection itemCollection, object item)

        {

            foreach (object curChildItem in itemCollection)

            {

                TreeViewItem parentContainer = (TreeViewItem)parentItemContainerGenerator.ContainerFromItem(curChildItem);

                if (parentContainer == null)

                    return null;

                TreeViewItem containerThatMightContainItem = (TreeViewItem)parentContainer.ItemContainerGenerator.ContainerFromItem(item);

                if (containerThatMightContainItem != null)

                    return containerThatMightContainItem;

                TreeViewItem recursionResult = ContainerFromItem(parentContainer.ItemContainerGenerator, parentContainer.Items, item);

                if (recursionResult != null)

                    return recursionResult;

            }

            return null;

        }

     

        public static object ItemFromContainer(this TreeView treeView, TreeViewItem container)

        {

            TreeViewItem itemThatMightBelongToContainer = (TreeViewItem)treeView.ItemContainerGenerator.ItemFromContainer(container);

            if (itemThatMightBelongToContainer != null)

                return itemThatMightBelongToContainer;

            else

                return ItemFromContainer(treeView.ItemContainerGenerator, treeView.Items, container);

        }

     

        private static object ItemFromContainer(ItemContainerGenerator parentItemContainerGenerator, ItemCollection itemCollection, TreeViewItem container)

        {

            foreach (object curChildItem in itemCollection)

            {

                TreeViewItem parentContainer = (TreeViewItem)parentItemContainerGenerator.ContainerFromItem(curChildItem);

                if (parentContainer == null)

                    return null;

                TreeViewItem itemThatMightBelongToContainer = (TreeViewItem)parentContainer.ItemContainerGenerator.ItemFromContainer(container);

                if (itemThatMightBelongToContainer != null)

                    return itemThatMightBelongToContainer;

                TreeViewItem recursionResult = ItemFromContainer(parentContainer.ItemContainerGenerator, parentContainer.Items, container) as TreeViewItem;

                if (recursionResult != null)

                    return recursionResult;

            }

            return null;

        }

    }

    Monday, May 11, 2009 8:32 PM
  •  Still doesn't work. Always returns null. I can't believe how badly Microsoft has obfuscated this control. I just want to iterate the friggin nodes and set expanded to True where appropriate.


    Thursday, July 30, 2009 11:27 AM
  • do it like me...

    build your own god damn TreeView...

    i ensure you that:
    - it works as you want it to
    - its cleaner
    - it looks like you want it to
    - it handle mouse gestures much better !


    maybe i should publish my tree somewhere...

    you can see it on http://87.54.35.102/planit (test / test)
    it doesn't fold, but that would take me 10 min to do ;-)
    Thursday, July 30, 2009 12:16 PM
  • JustinAngel's  method is great . But before you use it ,make sure that the treeviewitem has generated,or ,null is return .

    example :  treeview.ExpandAll();
                treeview.UpdateLayout();

    then try to find .

    Monday, September 07, 2009 3:30 AM
  • Hello, I don't know if anyone keeps having this problem, but I was and it seems I solve it. It seems you need to call method ExpandAndSelectNode(string idOrWhatever) in asynchronous way. So if you call it normally, you will get NULL from ContainerFromItem(), but if you call it like this: this.Dispatcher.BeginInvoke(()=>ExpandAndSelectNode(string idOrWhatever)); it will work! At least in my case this solved the "null-returning-problem". Please correct me if I am telling some nonsenses...
    Friday, September 18, 2009 9:13 AM
  •  Had the same problem; yeah looks like a timing issue.

    Fixed it by moving the code into the 'Loaded' event handler, i.e. instead of:

     

    MyTreeViewControl.ItemsSource = data;
    
    // code that makes calls to ItemContainerGenerator.ContainerFromItem
    ...
    

      

    I use:

     

    MyTreeViewControl.ItemsSource = data;
    
    MyTreeViewControl.Loaded += (s, e) =>
    {
    	// code that makes calls to ItemContainerGenerator.ContainerFromItem
    	...
    };
    

      

    HTH

     

    Wednesday, December 02, 2009 12:41 PM
  • I have tried every possible solution given here but still getting null for level 2 hierarchical childs.. The Loaded event is not getting invoked at all

    Friday, July 16, 2010 5:09 AM
  • make your own tree

    its worth the effort !

    Friday, July 16, 2010 5:18 AM
  • brother anuger u want to autoselect childitem in treeviewitem ?

    Friday, July 16, 2010 5:26 AM
  • XAML

    <sdk:HierarchicalDataTemplate x:Key="ChildTemplate" ItemsSource="{Binding Path=Children}">
                    <TextBlock Text="{Binding Path=Name}"/>
    </sdk:HierarchicalDataTemplate>
    <sdk:TreeView ItemsSource="{Binding}" x:Name="myTreeView" ItemTemplate="{StaticResource ChildTemplate}"  Grid.Row="1"/>
    

    CS Part

    public partial class MainPage : UserControl
    {
        public MainPage()
        {
            InitializeComponent();
            //PolluteTreeFunction
            myTreeView.DataContext = <polluted dataTree collection>;
        }
    }
    
    public class DataTree
    {
        private ObservableCollection<DataTree> _children;
    
        public string Name
        {
            get;
            set;
        }
    
        public ObservableCollection<DataTree> Children
        {
            get { return _children; }
            set { _children = value; }
        }
    
        public DataTree(string name, ObservableCollection<DataTree> children)
        {
            Name = name;
            Children = children;
        }
    }

    Now I want a ExpandAll() kinda function.

    public void ExpandAll()
    {
        DataTree dataNode = (myTreeView.DataContext as ObservableCollection<DataTree>)[0];
        TreeViewItem treeViewItem = myTreeView.ItemContainerGenerator.ContainerFromItem(dataNode) as TreeViewItem;
        treeViewItem.IsExpanded = true;
        foreach (DataTree childDataNode in dataNode.Children)
        {
            Expand(childDataNode, treeViewItem);
        }
    }
    
    public void Expand(DataTree dataNode, TreeViewItem parentNode)
    {
        if (dataNode.Children != null)
        {
            TreeViewItem treeViewItem = parentNode.ItemContainerGenerator.ContainerFromItem(dataNode) as TreeViewItem;
            treeViewItem.IsExpanded = true;
            foreach (DataTree childDataNode in dataNode.Children)
            {
                Expand(childDataNode, treeViewItem);
            }
        }
    }
    

    But treeViewItem is getting null in Expand() function.. I have tried the Loaded, Dispatcher, UpdateLayout methods.. but none works

    @Montago I already have made a custom TreeViewItem and want to check the performance difference between the ViewModel approach and CustomTreeNode approach

    Friday, July 16, 2010 6:42 AM
  • @Montago I already have made a custom TreeViewItem and want to check the performance difference between the ViewModel approach and CustomTreeNode approach


    regardless of the performance, i think you should throw the SL.NET TreeView in the trash and create your own TreeView

    there are so many fuzzy-weird-F'ed Up problems using the treeview that i simply never use it for advanced tasks anymore.

    such as a simple task of getting the event when selecting or clicking on a node does a whole lot of weird things.... i don't know if any of the issues has been solved in SL4 - i haven't checked - but i fear they haven't.... otherwize you wouldn't be posting, right ??

    even styling of a treeview is hard (unless you are a Blend-Geek) - styling your own tree takes like 30 minutes compared to a whole day of styling a generic TreeView :-(

    Friday, July 16, 2010 7:07 AM
  • Hi Jumbo,

     

    can you provide your full source code plz....i am also facing same issue..

    Actually i am trying to expand the parent node. can u pla help..

    Friday, October 22, 2010 7:25 AM
  • Hi JustinAngel,

    I am creating a silverlight application. I am trying to expand the treevire items selectively on page load..

    meaning..i am pasing some value in querystring paramater. if parameter value exists as a child of any treeview item then it has to expand its parent node.

    i tried in button click event and am able to achive this.

    How to use the extension methos that you have mentioned in your blog? Can you please suggest me how can i expand the treeview node on page load?

    could you please provide sample example?

    thanks,

    Saturday, October 23, 2010 3:02 PM
  • Well the solution is much simpler than anyone has ever thought. Just set the IsExpanded property of the newly inserted node to true and that would take away all the pain :)

    -Kuntal Kishore

    Tuesday, October 26, 2010 5:07 PM
  • MUHAHAHAHAHAHAHA AAHAHAHAHAH !!! HAHAHAHAHA

    if only it was that simple !

    The scenario is: 

    you click on a node to select it

    the selectednode_changed event is fired

    the object: sender = your bound object, NOT your TreeViewItem

    so now you need to find the real TreeViewItem - which is the reason why we have this thread.


    Wednesday, October 27, 2010 2:44 AM
  • Hi all,

    thanks for your reply. I figured out the solution for my problem.

    I caled used LayoutUpdated event to expand the specific treeview node on page load.

    MyTreeView.LayoutUpdated +=

    new EventHandler

    (MyTreeView_LayoutUpdated);

     

    Tuesday, November 02, 2010 8:23 AM
  • none of above solutions helped me out, I am still unable to expand a single tree node on 4th level...

    instead of ObservableCollection<> i m using List<> does it make any differece ? or I should forcfully use ObservableCollection<> ?

    Wednesday, March 02, 2011 9:26 AM
  • any one please help

    Monday, March 14, 2011 4:02 AM
  • How to find the TreeViewItem when the tree view is collapsed.

    Tuesday, February 21, 2012 11:26 PM