Click here to Skip to main content
15,878,945 members
Articles / Desktop Programming / WPF
Article

WPF TreeView Selection

Rate me:
Please Sign up or sign in to vote.
4.80/5 (30 votes)
27 Sep 20074 min read 224.4K   6.2K   63   27
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:

C#
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:
    C#
    using SynesthesiaM;
  3. Select your item:
    C#
    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:

C#
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:

C#
// 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:

C#
// 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


Written By
Web Developer
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
GeneralMy vote of 1 Pin
DanWalker2-Oct-17 14:35
DanWalker2-Oct-17 14:35 
QuestionNice solution Pin
Member 1028419619-Sep-13 1:11
Member 1028419619-Sep-13 1:11 
GeneralMy vote of 4 Pin
HOSSEIN.AB30-Oct-12 19:08
HOSSEIN.AB30-Oct-12 19:08 
GeneralMy vote of 4 Pin
Dean Oliver2-Feb-12 19:08
Dean Oliver2-Feb-12 19:08 
GeneralVery Good Solution with the wonderful usage of LINQ Pin
Bharat Mane19-Sep-11 22:55
Bharat Mane19-Sep-11 22:55 
GeneralExcellent Solution! Pin
John Livingston8-Feb-11 17:25
John Livingston8-Feb-11 17:25 
GeneralThis is the wrong way Pin
Ahmad Siddiqi9-Mar-10 12:58
Ahmad Siddiqi9-Mar-10 12:58 
GeneralLook for children Pin
Kyo_Kusanagi999-Oct-09 10:38
Kyo_Kusanagi999-Oct-09 10:38 
GeneralThanks Pin
Gary Wheeler3-Aug-09 4:04
Gary Wheeler3-Aug-09 4:04 
GeneralMoveToNextItem Pin
AVEbrahimi25-Feb-09 18:37
AVEbrahimi25-Feb-09 18:37 
QuestionDoesn't work with Virtualization turned on - WPF 3.5 SP1 Feature Pin
Martin Gagne21-Aug-08 17:11
Martin Gagne21-Aug-08 17:11 
AnswerRe: Doesn't work with Virtualization turned on - WPF 3.5 SP1 Feature Pin
DaWanderer18-Sep-08 3:56
DaWanderer18-Sep-08 3:56 
GeneralRe: Doesn't work with Virtualization turned on - WPF 3.5 SP1 Feature Pin
Zubair Noman28-Oct-08 11:12
Zubair Noman28-Oct-08 11:12 
GeneralRe: Doesn't work with Virtualization turned on - WPF 3.5 SP1 Feature Pin
Kenrae25-Nov-08 0:04
Kenrae25-Nov-08 0:04 
GeneralRe: Doesn't work with Virtualization turned on - WPF 3.5 SP1 Feature Pin
Kenrae25-Nov-08 0:12
Kenrae25-Nov-08 0:12 
GeneralRe: Doesn't work with Virtualization turned on - WPF 3.5 SP1 Feature Pin
Leung Yat Chun24-Jun-09 7:16
Leung Yat Chun24-Jun-09 7:16 
GeneralHave just looked at this Pin
Sacha Barber14-Nov-07 0:33
Sacha Barber14-Nov-07 0:33 
GeneralRe: Have just looked at this Pin
DaWanderer14-Nov-07 6:03
DaWanderer14-Nov-07 6:03 
GeneralRe: Have just looked at this Pin
Sacha Barber14-Nov-07 21:59
Sacha Barber14-Nov-07 21:59 
GeneralRe: Have just looked at this Pin
DaWanderer18-Nov-07 8:11
DaWanderer18-Nov-07 8:11 
GeneralRe: Have just looked at this Pin
Sacha Barber18-Nov-07 20:51
Sacha Barber18-Nov-07 20:51 
GeneralNice approach Pin
Josh Smith30-Sep-07 6:39
Josh Smith30-Sep-07 6:39 
GeneralRe: Nice approach Pin
DaWanderer30-Sep-07 9:31
DaWanderer30-Sep-07 9:31 
GeneralRe: Nice approach Pin
Josh Smith30-Sep-07 9:40
Josh Smith30-Sep-07 9:40 
GeneralRe: Nice approach Pin
DaWanderer30-Sep-07 15:16
DaWanderer30-Sep-07 15:16 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.