Thursday, 10 September 2009

WPF TreeView Clear Selection

I needed to enhance an interface by swapping a listbox for a tree view. The data structures needed to be more complex to represent the tree structure but it was all very straight forward. A few minutes making a couple of HierarchicalDataTemplate to format the tree items and it was all working just as I wanted it.

Job done I thought and a great example of WPF's cool productivity gains etc.

There was just one little thing to complete. At one point the app needs to clear the selected item, so nothing remains selected. When using the ListBox this was a one liner:

listBox.SelectedIndex = -1;

TreeView does not have a SelectedIndex, but it does have a SelectedItem, which unfortunately is a read only property.

So this was not going to be easy and took a lot more time to solve than the rest of the conversion did.

There is a IsSelected property on each TreeViewItem in the tree. Find the selected object and set IsSelected to false and that clears the selection.

This being WPF a TreeView can contain objects of any class in its structure. No need to use TreeViewItems like in older frameworks.  So the following will not work as SelectedItem could return anything.

treeView.SelectedItem.IsSelected = false

The way the TreeView works is to generate TreeViewItems in the background that link to the actual data objects that you provide. So we need to get hold of each of the these using the TreeView's  ItemContainerGenerator and then set the IsSelected property to false. To make matters more complex the data is stored in a tree structure with each item acting as a tree in its own right. We have to walk the entire tree to find the selected item.

So here is the solution I came up with to enable me to clear the TreeView selection in a single line of code.

TomWrightsUtils.ClearTreeViewSelection(treeView);

Like all my favourite tree algorithms it uses recursion to do the job.

    class TomWrightsUtils
    {
        public static void ClearTreeViewSelection(TreeView tv)
        {
            if (tv != null)
                ClearTreeViewItemsControlSelection(tv.Items, tv.ItemContainerGenerator);
        }
        private static void ClearTreeViewItemsControlSelection(ItemCollection ic, ItemContainerGenerator icg)
        {
            if ((ic != null) && (icg != null))
                for (int i = 0; i < ic.Count; i++)
                {
                    TreeViewItem tvi = icg.ContainerFromIndex(i) as TreeViewItem;
                    if (tvi != null)
                    {
                        ClearTreeViewItemsControlSelection(tvi.Items, tvi.ItemContainerGenerator);
                        tvi.IsSelected = false;
                    }
                }
        }
    }

2 comments: