tags:

views:

2019

answers:

3

I have a three level treeview. How do I select any item in third level from code? I tried a method mentioned in many blogs and on stackoverflow but it seems to work only for first level (dbObject is null for items on below first level).

Here is the code I'm using to select TreeViewItem. Do I miss something?

public static void SetSelectedItem(this TreeView control, object item)
{
    try
    {
        var dObject = control.ItemContainerGenerator.ContainerFromItem(item);

        //uncomment the following line if UI updates are unnecessary
        ((TreeViewItem)dObject).IsSelected = true;

        MethodInfo selectMethod = typeof(TreeViewItem).GetMethod("Select",
            BindingFlags.NonPublic | BindingFlags.Instance);

        selectMethod.Invoke(dObject, new object[] { true });
    }
    catch { }
}
A: 

Yeah, the ContainerFromItem method isn't giving back anything, even when you call it from the direct parent TreeViewItem.

You may need to do a bit of redesign. If you create everything as an explicit TreeViewItem you should be able to keep a reference to it and set IsSelected on it.

RandomEngy
A: 

After trying different sollutions I came to this site. Zhou Yong shows how to programatically expand all nodes of TreeView. There are two main ideas in his method:

  • ContainerFromItem will return container only if item is direct child of the element. In TreeView that means that only first level child container will be returned and you have to call ContainerFromItem on child TreeViewItem to get container from next level
  • For ContainerFromItem to work TreeViewItem visual children should be created and this happens only when TreeViewItem is expanded. That means that to select TreeViewItem all items preceding required item must be expanded. In practice that means that we will have to provide path to the item we want to select instead of just the item.

Here is the code I ended up with

public static void SelectItem(this ItemsControl parentContainer, List<object> path)
{
    var head = path.First();
    var tail = path.GetRange(1, path.Count - 1);
    var itemContainer = parentContainer.ItemContainerGenerator.ContainerFromItem(head) as TreeViewItem;

    if (itemContainer != null && itemContainer.Items.Count == 0)
    {
        itemContainer.IsSelected = true;

        var selectMethod = typeof(TreeViewItem).GetMethod("Select", BindingFlags.NonPublic | BindingFlags.Instance);
        selectMethod.Invoke(itemContainer, new object[] { true });
    }
    else if (itemContainer != null)
    {
        itemContainer.IsExpanded = true;

        if (itemContainer.ItemContainerGenerator.Status != GeneratorStatus.ContainersGenerated)
        {
            itemContainer.ItemContainerGenerator.StatusChanged += delegate
            {
                SelectItem(itemContainer, tail);
            };
        }
        else
        {
            SelectItem(itemContainer, tail);
        }
    }
}
Sergej Andrejev
+3  A: 

Another option would be to use binding. If you have an object that you are using binding with to get the text of each TreeViewItem (for example), you can create a style that also binds the IsSelected property:

<TreeView>
    <TreeView.Resources>
        <Style TargetType="TreeViewItem">
            <Setter Property="IsSelected"
                    Value="{Binding Path=IsSelected, Mode=TwoWay}" />
        </Style>
    </TreeView.Resources>
</TreeView>

This assumes that the bound object has an IsSelected property of type bool. You can then select a TreeViewItem by setting IsSelected to true for its corresponding object.

The same approach can be used with the IsExpanded property to control when a TreeViewItem is expanded or collapsed.

Andy
Yes, I was aware of this. But it looks like it introduces code coupling. Anyway it's good that you have that answer here. People who will come to this page may prefer your way over mine
Sergej Andrejev