none
Getting TreeViewItem for the selected item in a TreeView

    Question

  • Ok.  The TreeView is making me feel really stupid

     

    I have a TreeView that is bound to a tree of objects.  When I select a node in that tree, I want to be able to use the TreeView to navigate up to it's parent to perform operation on it (say I wanted to delete the item).  This seems doable using VisualTreeHelper if I can get the TreeViewItem that has been selected.   I tried using ItemContainerGenerator.ContainerFromItem to get this, but this only works if the selected node is in the first level of the hierarchy.  From other reading, my understanding for this is becuse the TreeView's ItemContainerGenerator actually contains other ItemConainerGenerators which contain ItemContainerGenerators etc. in a data structure that aligns with the tree.  So that doesn't work.  Apperently ItemContainerGenerator does not leverage a flat index into the tree.

     

    Here's the kicker.  In the debugger I can see a private property on the TreeView called SelectedContainer that gives me exactly what I want.   I'm so close its driving me crazy!  What am I doing wrong?  Apparenly the TreeView knows what the selected container is, why can't it tell me?

     

    I'd also be interested to know why all of this is so hard in WPF?   Why cant UI controls just have a Parent property and be done with it?  I'm sure there is a really good architectural reason why all this is better, but it escapes me.  Any pointers to documentation, white papers or blog articles that anyone has found on the topic would be greatly appreciated.

     

    thx.

     

     

    BTW.  I know I can work around this by putting back pointers to the parent in my data structures, which is what I will probably do.  But this has kicked by butt several times now and I'd really like to know why this is so hard.  Its a departure from the way Windows Forms and ASP.NET work.
    Thursday, September 20, 2007 2:10 AM

Answers

  • I have a strategy for dealing with a TreeView that works pretty well.  I leverage the bubbled TreeViewItem.Selected event to track the currently selected item container (the TreeViewItem) at all times.  For convenience, I just stick it in the Tag property of the TreeView itself.  Then, anytime I need to access the TreeViewItem, it is at the ready.

     

    Here's how it works...  On the TreeView declaration, set a handler for the TreeViewItem.Selected event as follows:

     

    Code Snippet

     

    <TreeView Name="myTreeView" TreeViewItem.Selected="OnItemSelected"

        ItemsSource="{Binding Source={StaticResource myHierarchicalData}}"/>

     

     

     

    Your OnItemSelected handler should look something like the following:

     

    Code Snippet

     

    private void OnItemSelected(object sender, RoutedEventArgs e)

    {

        myTreeView.Tag = e.OriginalSource;

    }

     

     

     

    Note that the events happen in the correct order so this is a reliable approach.  Namely, the TreeViewItem.Selected event fires *before* the TreeView.SelectedItemChanged event.  So if you want to do something in response to a change in selection, you will have the correct TreeViewItem in the Tag.

     

    Anytime you need to access the TreeViewItem, you should do this:

     

    Code Snippet

     

    if (myTreeView.SelectedItem != null)

    {

        TreeViewItem selectedTVI = myTreeView.Tag as TreeViewItem;

        // add your code here

    }

     

     

     

    Now for the bigger question...  Why is it so complex?

     

    The Windows Client Platform needed to go through a revolution in order to enable the killer experiences available on other platforms.  The ASP.NET and Windows Forms models are much too restrictive.  Their concept of "treeness," if you will, is of the "logical tree" variety.  It does not enable the scenarios afforded by the lookless control model of WPF... namely, a model that allows a control's appearance to be determined by the composition of visual elements that are not part of the logical tree.  Hence, the birth of the "visual tree".

     

    Understanding the various trees of WPF is definitely one of the more challenging tasks when you are first learning the platform.  There are other trees too... the automation tree, for example.  And I've even seen some third-party concepts of "virtual" trees.  So it can be both confusing and frustrating. 

     

    The big ones to understand are the visual and the logical trees.  They are explained here in the SDK, although a whitepaper with some additional depth would be nice too.  I haven't seen one myself. 

     

    A very simplistic way to view the trees is that the elements you explicitly include in your XAML make up the logical tree.  These elements are represented visually by expanding their control and data templates.  All of the elements in the expanded tree comprise the visual tree.  (Note: This is an oversimplification.  Data items, which can be non-dependency objects like CLR objects and value types, will actually appear in the logical tree but do not appear in the visual tree.  Only "visuals" belong to the visual tree.)

     

    Once you've mastered the concepts of these trees, I think you will come to really appreciate this new level of complexity because of the scenarios it enables.

    Thursday, September 20, 2007 4:46 AM

All replies

  • I have a strategy for dealing with a TreeView that works pretty well.  I leverage the bubbled TreeViewItem.Selected event to track the currently selected item container (the TreeViewItem) at all times.  For convenience, I just stick it in the Tag property of the TreeView itself.  Then, anytime I need to access the TreeViewItem, it is at the ready.

     

    Here's how it works...  On the TreeView declaration, set a handler for the TreeViewItem.Selected event as follows:

     

    Code Snippet

     

    <TreeView Name="myTreeView" TreeViewItem.Selected="OnItemSelected"

        ItemsSource="{Binding Source={StaticResource myHierarchicalData}}"/>

     

     

     

    Your OnItemSelected handler should look something like the following:

     

    Code Snippet

     

    private void OnItemSelected(object sender, RoutedEventArgs e)

    {

        myTreeView.Tag = e.OriginalSource;

    }

     

     

     

    Note that the events happen in the correct order so this is a reliable approach.  Namely, the TreeViewItem.Selected event fires *before* the TreeView.SelectedItemChanged event.  So if you want to do something in response to a change in selection, you will have the correct TreeViewItem in the Tag.

     

    Anytime you need to access the TreeViewItem, you should do this:

     

    Code Snippet

     

    if (myTreeView.SelectedItem != null)

    {

        TreeViewItem selectedTVI = myTreeView.Tag as TreeViewItem;

        // add your code here

    }

     

     

     

    Now for the bigger question...  Why is it so complex?

     

    The Windows Client Platform needed to go through a revolution in order to enable the killer experiences available on other platforms.  The ASP.NET and Windows Forms models are much too restrictive.  Their concept of "treeness," if you will, is of the "logical tree" variety.  It does not enable the scenarios afforded by the lookless control model of WPF... namely, a model that allows a control's appearance to be determined by the composition of visual elements that are not part of the logical tree.  Hence, the birth of the "visual tree".

     

    Understanding the various trees of WPF is definitely one of the more challenging tasks when you are first learning the platform.  There are other trees too... the automation tree, for example.  And I've even seen some third-party concepts of "virtual" trees.  So it can be both confusing and frustrating. 

     

    The big ones to understand are the visual and the logical trees.  They are explained here in the SDK, although a whitepaper with some additional depth would be nice too.  I haven't seen one myself. 

     

    A very simplistic way to view the trees is that the elements you explicitly include in your XAML make up the logical tree.  These elements are represented visually by expanding their control and data templates.  All of the elements in the expanded tree comprise the visual tree.  (Note: This is an oversimplification.  Data items, which can be non-dependency objects like CLR objects and value types, will actually appear in the logical tree but do not appear in the visual tree.  Only "visuals" belong to the visual tree.)

     

    Once you've mastered the concepts of these trees, I think you will come to really appreciate this new level of complexity because of the scenarios it enables.

    Thursday, September 20, 2007 4:46 AM
  • Thanks for taking the time to write this up Doc.  This helps a lot.  I was able to overcome the issue when I looked at your code and realized I should be sourcing the bubbled up TreeViewItem.Selected event instead of the TreeView.SelectionChanged event. 

    Friday, September 21, 2007 3:41 PM
  • I see from this thread how to access the selected TreeViewItem, but I'm still not sure how to get back up the hierarchy to its parent TreeViewItem.  I'm writing a control which I am trying to make as agnostic as possible regarding its data source, so a "back pointer" is something I would like to avoid if possible.  Neither LogicalTreeHelper.GetParent() nor VisualTreeHelper.GetParent() appear to help with this.

     

    Thanks, Jon

    Friday, December 28, 2007 9:32 PM
  • You can walk the parent TreeViewItems using the static ItemsControl.ItemsControlFromItemContainer() method.

     

    Friday, December 28, 2007 10:54 PM
  • I created a sample to demonstrate how I solved this.  It is posted here:

    http://www.box.net/shared/ixsyoh2g4s

     

    Hope this is helpful.  Thanks to everyone on the thread who helped out.

    Wednesday, January 02, 2008 5:28 PM
  • My personal recomendation is use 

    LogicalTreeHelper.GetParent because in this case you want to access to Logical tree instead of access VisualTree, and there is huge different.

    TreeViewItem example = LogicalTreeHelper.GetParent(t.SelectedItem as TreeViewItem) as TreeViewItem;

    Thursday, November 20, 2008 12:13 PM
  • Hello,

    This thread has been very helpful.  But could someone show me how to save the current selected position and how to go back to that position if another node is added.

    In other words in my program I add a new node and I lose the current position.  I would like to go back to that position in the tree.

    I thought you could do...

    TreeViewItem selectedTVI = null;

    if (myTreeView.SelectedItem != null)

    {

        selectedTVI = myTreeView.Tag as TreeViewItem;

    }

    // my code goes here

    selectedTVI.IsSelected = true;

    But this doesn't work.
    Friday, November 21, 2008 2:57 PM
  • Try IsVisible,Visiblility Properties this might help you :)
    http://msdn.microsoft.com/en-us/library/system.windows.controls.treeviewitem_properties.aspx


    Prasad - www.beautifulmind.blog.co.in
    Sunday, December 28, 2008 4:23 PM
  • Can I use someting like this?

    <TreeView local:ViewHelper.SelectedPath="{Binding SelectedPath}">

    public class ViewHelper:DependencyObject 
        {
            public static readonly DependencyProperty SelectedPathProperty = DependencyProperty.RegisterAttached("SelectedPath", typeof(object[]), typeof(ViewHelper), new PropertyMetadata(new object[0]));
            public static object[] GetSelectedPath(TreeView w)
            {
                return (object[])w.GetValue(SelectedPathProperty);
            }
            public static void SetSelectedPath(TreeView w, object[] path)
            {
                w.SetValue(SelectedPathProperty, path);
            }
    
    
            public static object[] GetPathFor(TreeViewItem item)
            {
                ArrayList list = new ArrayList();
                FrameworkElement curr = item;
                while (curr != null)
                {
                    list.Add(curr.DataContext);
                    curr = (FrameworkElement)ItemsControl.ItemsControlFromItemContainer(curr);
                }
    
                list.Reverse();
                return list.ToArray();
            }
        }
    this works but I have to handle TreeViewsEvent and in the handler set the value of the attached property.

    is it possible to automatically add handler to the SelectedChanged event whenever attached property is used?

    .NET guy

    Friday, March 09, 2012 1:30 PM
  • a_Matt Smith_ - Thanks a lot for your sample code. Very useful!
    Tuesday, April 10, 2012 9:46 PM
  • Hi Dr.WPF,

    I tried the way you said and I still get selectedTVI as nothing. Please help me.

    I am using a hierarchicaldatatemplate to get the treeview.

    Tuesday, November 20, 2012 10:35 AM