Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

WPF TreeView Selection

0.00/5 (No votes)
27 Sep 2007 1  
Implementing TreeViewItem selection in the WPF TreeView control
Screenshot - WPFTreeViewSelection.gif

Introduction

In the process of using the WPF TreeView control, I'm sure many new WPF programmers have tried something like this:

treeView1.SelectedItem = myItem;

...only to discover that the SelectedItem property is read-only. Coming from Windows Forms, it would seem natural to programmatically set the SelectedItem in this manner. In WPF, however, the TreeView control is much more powerful and there are a variety of reasons why the designers chose not to make the SelectedItem property settable. Regardless of design decisions, though, we still need to be able to programmatically set the selected TreeViewItem in a TreeView (and hopefully in an easy manner).

A Solution

The solution I came up with is to have code that "walks" the tree for me, following a list (or chain) of items and selecting the last item in the chain. In the example image above, the selected path is Item 7\Item 8\Item 9\Item 10 and Item 10 is selected in the TreeView. The chain of items, then, is Item 7 -> Item 8 -> Item 9 -> Item 10.

It doesn't matter what type the items in the chain are, just that they're unique for each parent TreeViewItem. Behind the scenes, the code will walk the chain of items from left to right. Each item in the chain is searched for in the Items property of the current ItemsControl (starting with the TreeView and descending into each TreeViewItem). If there are more items in the chain, the current TreeViewItem is expanded and the search continues. The last TreeViewItem has its IsSelected property set to true.

Using the Code

Selecting a TreeViewItem using a path like in the example above is easy:

  1. Include the TreeViewExtensions.cs and UIUtility.cs files in your project.
  2. Reference the namespace to include the extension methods:
    using SynesthesiaM;
  3. Select your item:
    yourTreeView.SetSelectedItem(@"Path\To\Your\Item");

Even for this simple scenario, there is a lot going on in the background and many assumptions are made about the objects in your TreeView. When calling SetSelectedItem(string path), each component of the path (separated by System.IO.Path.DirectorySeparatorChar ) is compared with the ToString representation of the objects in your TreeView. If your TreeView contained objects like this:

public class MyObject
{    
    public string Name { get; set; }
    public int Id { get; set; }
    
    public override ToString()
    {
        return (this.Name);
    }
}

...then the components in your path will effectively be the Name property of each MyObject because that's what ToString returns. What if you wanted to use the Id property instead? Using one of the overloads of SetSelectedItem, you can do it as follows:

// Extract a string representation of the Id property

var myConvertFunction = item =>
    ((MyObject)item).Id.ToString();

// Now you can use the Id numbers in the path

yourTreeView.SetSelectedItem(@"1\2\3\4", myConvertFunction);

If you need more complex item chains that don't have a unique string representation for each item, you can customize the behavior of SetSelectedItem further by specifying a conversion method. Another way of doing the previous example would be:

// Extract the Id property

var myConvertFunction = item =>
    item.Id;

// Use a custom comparison function for matching Id numbers

var myCompareFunction = (id1, id2) =>
    id1 == id2;

// Select the object with Id # 3 in this chain:

// Id # 1

// |-> Id # 2

//     |-> Id # 3 *

yourTreeView.SetSelectedItem(new int[] { 1, 2, 3 }, 
    myCompareFunction, myConvertFunction);

Different aspects of the path searching can be customized. Here's a brief description of each overload:

  • SetSelectedItem(string path, char separatorChar)
    • Lets you specify your own separator character for path components (for example, use "/" for Unix-style paths).
  • SetSelectedItem(string path, Func<object, string> convertMethod)
    • Lets you extract a path component by some other means besides calling ToString on the object.
  • SetSelectedItem<T>(IEnumerable<T> items)
    • Lets you manually provide the item chain and type of items in the TreeView.
    • Items are compared with the "==" operator.
  • SetSelectedItem<T>(IEnumerable<T> items, Func<T, T, bool> compareMethod)
    • Lets you manually provide the item chain and type of items in the TreeView.
    • Items are compared with your custom method.
  • SetSelectedItem<T>(IEnumerable<T> items, Func<T, T, bool> compareMethod, Func<object, T> convertMethod)
    • Lets you manually provide the item chain and type of items in the TreeView.
    • Items are compared with your custom method.
    • Items in the TreeView are converted with your custom method before being compared.

If you want the maximum level of customization, use the UIUtility.SetSelectedItem method. This method allows you to control almost every aspect of the searching, including the actions taken to select an item and request more items. The example project demonstrates how to use the most important SetSelectedItem overloads.

Points of Interest and Potential Problems

The main reason it's so difficult to select items in a TreeView is because of the generation of item containers. When an object is added to a TreeView or a TreeViewItem is expanded, the child TreeViewItems are generated in a background thread. Calling ItemsControl.ItemContainerGenerator.ContainerFromItem before the background thread is finished will just return a null reference. UIUtility.SetSelectedItem gets around the background thread problem by attaching an event handler to the ItemsControl.ItemContainerGenerator.StatusChanged event and waiting for the generator to finish.

With all of the threading kung-fu, there's the potential for strange problems. One that I've noticed so far is calling any of the SetSelectedItem methods twice, one right after the other, with different paths (or chains). Both calls will expand the appropriate TreeViewItems, but the last one to finish (not necessarily the last one called) will have its final item selected. Other potential problems most likely exist, so I wouldn't recommend using this in critical production code.

Contributions and Comments

Any contributions to the code or comments are welcome! I'm fairly new to WPF myself, so there might be many things that I'm missing or mistaken about. Also, this is my first Code Project article, so any formatting or styling recommendations are welcome too.

History

  • 9/25/2007 - Initial version (1.0)

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here