[ACCEPTED]-How to get TreeViewItem from HierarchicalDataTemplate item?-hierarchicaldatatemplate

Accepted answer
Score: 32
TreeViewItem item = (TreeViewItem)(mainTreeList
    .ItemContainerGenerator
    .ContainerFromIndex(mainTreeList.Items.CurrentPosition));

DOES NOT WORK (for me) as mainTreeList.Items.CurrentPosition 12 in a treeview using a HierarchicalDataTemplate 11 will always be -1.

NEITHER DOES below as as mainTreeList.Items.CurrentItem 10 in a treeview using a HierarchicalDataTemplate 9 will always be null.

TreeViewItem item = (TreeViewItem)mainTreeList
    .ItemContainerGenerator
    .ContainerFromItem(mainTreeList.Items.CurrentItem);

INSTEAD I had to set a the 8 last selected TreeViewItem in the routed 7 TreeViewItem.Selected event which bubbles 6 up to the tree view (the TreeViewItem's 5 themselves do not exist at design time as 4 we are using a HierarchicalDataTemplate).

The 3 event can be captured in XAML as so:

<TreeView TreeViewItem.Selected="TreeViewItemSelected" .../> 

Then 2 the last TreeViewItem selected can be set 1 in the event as so:

    private void TreeViewItemSelected(object sender, RoutedEventArgs e)
    {
        TreeViewItem tvi = e.OriginalSource as TreeViewItem;

        // set the last tree view item selected variable which may be used elsewhere as there is no other way I have found to obtain the TreeViewItem container (may be null)
        this.lastSelectedTreeViewItem = tvi;

        ...
     }
Score: 25

From Bea Stollnitz's blog entry about this, try

TreeViewItem item = (TreeViewItem)(mainTreeList
    .ItemContainerGenerator
    .ContainerFromIndex(mainTreeList.Items.CurrentPosition));

0

Score: 9

I ran into this same problem. I needed to 9 get to the TreeViewItem so that I could 8 have it be selected. I then realized that 7 I could just add a property IsSelected to 6 my ViewModel, which I then bound to the 5 TreeViewItems IsSelectedProperty. This can 4 be achieved with the ItemContainerStyle:

<TreeView>
            <TreeView.ItemContainerStyle>
                <Style TargetType="{x:Type TreeViewItem}">
                    <Setter Property="IsExpanded" Value="{Binding IsExpanded, Mode=TwoWay}"/>
                    <Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}" />
                </Style>
            </TreeView.ItemContainerStyle>
</TreeView>

Now 3 if I want to have an item in the treeview 2 selected, I just call IsSelected on my ViewModel 1 class directly.

Hope it helps someone.

Score: 7
TreeViewItem item = (TreeViewItem)(mainTreeList
    .ItemContainerGenerator
    .ContainerFromIndex(mainTreeList.Items.CurrentPosition)); gives first item in the TreeView because CurrentPosition is always 0.

How about

TreeViewItem item = (TreeViewItem)(mainTreeList
    .ItemContainerGenerator
    .ContainerFromItem(mainTreeList.SelectedItem)));

This works better for me.

0

Score: 4

Inspired by Fëanor’s answer, I’ve attempted 18 to make the TreeViewItem easily accessible for every 17 data item that a TreeViewItem has been created for.

The 16 idea is to add a TreeViewItem-typed field to the view 15 model, also exposed via an interface, and 14 have the TreeView automatically populate it whenever 13 the TreeViewItem container is created.

This is done by 12 subclassing TreeView and attaching an event to the 11 ItemContainerGenerator, which records the TreeViewItem whenever it’s created. Gotchas 10 include the fact that TreeViewItems are created lazily, so 9 there might genuinely not be one available 8 at certain times.

Since posting this answer, I've 7 developed this further and used it for a 6 long time in one project. No issues so far, other 5 than the fact that this violates MVVM (but 4 also saves you a ton of boilerplate for 3 the simple cases). Source here.

Usage

Select the parent 2 of the selected item and collapse it, ensuring 1 that it’s in the view:

...
var selected = myTreeView.SelectedItem as MyItem;
selected.Parent.TreeViewItem.IsSelected = true;
selected.Parent.TreeViewItem.IsExpanded = false;
selected.Parent.TreeViewItem.BringIntoView();
...

Declarations:

<Window ...
        xmlns:tvi="clr-namespace:TreeViewItems"
        ...>
    ...
    <tvi:TreeViewWithItem x:Name="myTreeView">
        <HierarchicalDataTemplate DataType = "{x:Type src:MyItem}"
                                  ItemsSource = "{Binding Children}">
            <TextBlock Text="{Binding Path=Name}"/>
        </HierarchicalDataTemplate>
    </tvi:TreeViewWithItem>
    ...
</Window>
class MyItem : IHasTreeViewItem
{
    public string Name { get; set; }
    public ObservableCollection<MyItem> Children { get; set; }
    public MyItem Parent;
    public TreeViewItem TreeViewItem { get; set; }
    ...
}

Code

public class TreeViewWithItem : TreeView
{
    public TreeViewWithItem()
    {
        ItemContainerGenerator.StatusChanged += ItemContainerGenerator_StatusChanged;
    }

    private void ItemContainerGenerator_StatusChanged(object sender, EventArgs e)
    {
        var generator = sender as ItemContainerGenerator;
        if (generator.Status == GeneratorStatus.ContainersGenerated)
        {
            int i = 0;
            while (true)
            {
                var container = generator.ContainerFromIndex(i);
                if (container == null)
                    break;

                var tvi = container as TreeViewItem;
                if (tvi != null)
                    tvi.ItemContainerGenerator.StatusChanged += ItemContainerGenerator_StatusChanged;

                var item = generator.ItemFromContainer(container) as IHasTreeViewItem;
                if (item != null)
                    item.TreeViewItem = tvi;

                i++;
            }
        }
    }
}

interface IHasTreeViewItem
{
    TreeViewItem TreeViewItem { get; set; }
}
Score: 2

Try something like this:

    public bool UpdateSelectedTreeViewItem(PropertyNode dateItem, ItemsControl itemsControl)
    {
        if (itemsControl == null || itemsControl.Items == null || itemsControl.Items.Count == 0)
        {
            return false;
        }
        foreach (var item in itemsControl.Items.Cast<PropertyNode>())
        {
            var treeViewItem = itemsControl.ItemContainerGenerator.ContainerFromItem(item) as TreeViewItem;
            if (treeViewItem == null)
            {
                continue;
            }
            if (item == dateItem)
            {
                treeViewItem.IsSelected = true;
                return true;
            }
            if (treeViewItem.Items.Count > 0 && UpdateSelectedTreeViewItem(dateItem, treeViewItem))
            {
                return true;
            }
        }
        return false;
    }

0

Score: 1

A single 'ItemContainerGenerator.ContainerFromItem' or 15 'ItemContainerGenerator.ItemContainerGenerator' call 14 cannot find the TreeViewItem associated 13 by tree view object, since the separation 12 of tree view item controller and tree view 11 item data. It is necessary to create a recursive 10 function to use 'ItemContainerGenerator.ContainerFromItem' and 9 'ItemContainerGenerator.ItemContainerGenerator', to 8 locate TreeViewItem in tree view. Example 7 recursive function could look like:

private TreeViewItem GetTreeViewItemFromObject(ItemContainerGenerator container, object tvio)
{
    var item = container.ContainerFromItem(tvio) as TreeViewItem;
    if (item != null)
    {
        return item;
    }

    for (int i = 0; i < container.Items.Count; i++)
    {
        var subContainer = (TreeViewItem)container.ContainerFromIndex(i);
        if (subContainer != null)
        {
            item = GetTreeViewItemFromObject(subContainer.ItemContainerGenerator, tvio);
            if (item != null)
            {
                return item;
            }
        }
    }

    return null;
}

called 6 by var target = GetTreeViewItemFromObject(treeView.ItemContainerGenerator, item);

The 5 recursive function only works after tree 4 view's 'ItemContainerGenerator.Status' is 3 'ContainersGenerated'. So during the initialization 2 view period, 'GetTreeViewItemFromObject' does 1 not work.

Score: 1

I modified William's recursive search to 3 a more compact version:

public TreeViewItem GetTreeViewItemFromObject(ItemContainerGenerator container, object targetObject) {
    if (container.ContainerFromItem(targetObject) is TreeViewItem target) return target;
    for (int i = 0; i < container.Items.Count; i++)
        if ((container.ContainerFromIndex(i) as TreeViewItem)?.ItemContainerGenerator is ItemContainerGenerator childContainer)
            if (GetTreeViewItemFromObject(childContainer, targetObject) is TreeViewItem childTarget) return childTarget;
    return null;
}

One would call it 2 by providing the TreeView instance's ItemContainerGenerator 1 and the target data object:

TreeViewItem tvi = GetTreeViewItemFromObject(treeView.ItemContainerGenerator, targetDataObject);
Score: 0

Do you need the TreeViewItem because you're 6 going to modify what is being displayed? If 5 that is the case, I'd recommend using a 4 Style to change how the item is being displayed 3 instead of using code-behind instead of 2 directly modifying the TreeViewItem. It 1 should hopefully be cleaner.

Score: 0

If you have to find item in children of 2 children you may have to use recursion like 1 this

public bool Select(TreeViewItem item, object select) // recursive function to set item selection in treeview
{
    if (item == null)
        return false;
    TreeViewItem child = item.ItemContainerGenerator.ContainerFromItem(select) as TreeViewItem;
    if (child != null)
    {
        child.IsSelected = true;
        return true;
    }
    foreach (object c in item.Items)
    {
        bool result = Select(item.ItemContainerGenerator.ContainerFromItem(c) as TreeViewItem, select);
        if (result == true)
            return true;
    }
    return false;
}
Score: 0

Here is solution. rtvEsa is treeview. HierarchicalDataTemplate 7 is treeview template Tag make real consumation 6 of current item. This is not selected item 5 it is current item in tree control that 4 use HierarchicalDataTemplate.

Items.CurrentItem 3 is part of internal tree colection. You 2 cant get many and diferent data. For example 1 Items.ParenItem too.

  <HierarchicalDataTemplate ItemsSource="{Binding ChildItems}">

  <TextBox Tag="{Binding ElementName=rtvEsa, Path=Items.CurrentItem }" />

Score: 0

My TreeView template has only one level 1 of headers, so I found success with this:

        foreach (CollectionViewGroup grp in treeView.Items)
        {
            string strHeader = (string)grp.Name;
            TreeViewItem treeViewItem = (TreeViewItem)treeView.ItemContainerGenerator.ContainerFromItem(grp);

More Related questions