10
votes

How would I clear the TreeView selection within a WPF TreeView? I have tried looping through the TreeNodes and clearing the IsSelected property, however that is a ReadOnly property. Any ideas?

The TreeView is using XML Binding through the XMLDataProvider object.

9

9 Answers

13
votes

I came across the exact same problems and wrote the following code which will work on any treeview, with just a single line call to the first function.

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;
                }
            }
    }
}
8
votes

Not sure what you mean by TreeNodes.

Typically you would have a corresponding IsSelected property on your view model that your view binds to:

<TreeView>
    <TreeView.ItemContainerStyle>
        <Style TargetType="TreeViewItem">
            <Setter Property="IsSelected" Value="{Binding IsSelected}"/>
        </Style>
    </TreeView.ItemContainerStyle>
</TreeView>

Therefore, you would just loop through the data items in your view model and set IsSelected = false there.

However, it sounds like you don't have such a property. That being the case, you need to get the corresponding TreeViewItem for each data item. See the TreeView.ItemContainerGenerator property for info on how to do this. Something like:

var treeViewItem = _treeView.ItemContainerGenerator.ContainerFromIndex(0) as TreeViewItem;
treeViewItem.IsSelected = false;
6
votes
TreeViewItem tvi = tvMain.ItemContainerGenerator.ContainerFromIndex(0) as TreeViewItem;

if (tvi != null) { tvi.IsSelected = true; tvi.IsSelected = false; }
2
votes

Find the selected item and set the value:

private void Button_Click(object sender, RoutedEventArgs e)
{
  TreeViewItem tvi = treeviewExample.SelectedItem as TreeViewItem;
  if (tvi != null)
  {
    tvi.IsSelected = false;
  }
}
2
votes

This works out great as a extention method so you can call

youTreeview.ClearSelection();

using System.Windows.Forms;
using System.Windows.Controls;

namespace YourAppNamespace
{
    public static void ClearSelection(this TreeView input)
    {
    // this should be some container that you put in
    // possibly the actual treeviewitem, not sure on that though
    var selected = input.SelectedItem;
    if (selected == null)
        return;

    // in my case this works perfectly
    var tvi = input.ItemContainerGenerator.ContainerFromItem(selected) as TreeViewItem;
    var tvi = input.ItemContainerGenerator.ContainerFromItem(selected) as TreeViewItem;
    if (tvi == null)
    {
        // it must be a child, heres a hack fix
        // my nodes are inherited from TreeViewItemViewModel by Josh Smith
        var child = selected as WPF.Controls.TreeViewItemViewModel;
        if (child == null)
            return;
        child.IsSelected = false;

    }
    else
        tvi.IsSelected = false;
     }


}
1
votes

It's been my experience to stay away from the standard ItemContainerGenerator calls because they will fail on nodes at a depth greater than n+1. I use the combination of the following extension methods below. The ContainerFromItem extension methods comes from an MSDN blog and it has worked wonders for me when dealing with the TreeView.

  public static void ClearSelection(this TreeView input)
  {
     var selected = input.SelectedItem;

     if (selected == null) return;

     var tvi = input.ContainerFromItem(selected) as TreeViewItem;

     if (tvi == null) return;

     tvi.IsSelected = false;

  }

  public static TreeViewItem ContainerFromItem(this TreeView treeView, object item)
  {
     TreeViewItem containerThatMightContainItem = (TreeViewItem)treeView.ItemContainerGenerator.ContainerFromItem(item);
     if (containerThatMightContainItem != null)
        return containerThatMightContainItem;
     else
        return ContainerFromItem(treeView.ItemContainerGenerator, treeView.Items, item);
  }

  private static TreeViewItem ContainerFromItem(ItemContainerGenerator parentItemContainerGenerator, ItemCollection itemCollection, object item)
  {
     foreach (object curChildItem in itemCollection)
     {
        TreeViewItem parentContainer = (TreeViewItem)parentItemContainerGenerator.ContainerFromItem(curChildItem);
        if (parentContainer == null)
           return null;
        TreeViewItem containerThatMightContainItem = (TreeViewItem)parentContainer.ItemContainerGenerator.ContainerFromItem(item);
        if (containerThatMightContainItem != null)
           return containerThatMightContainItem;
        TreeViewItem recursionResult = ContainerFromItem(parentContainer.ItemContainerGenerator, parentContainer.Items, item);
        if (recursionResult != null)
           return recursionResult;
     }
     return null;
  }
0
votes

This seems to work so far, but I just put it in like 5 minutes ago so use at your own risk. I basically wanted to clear the selection when user clicks within the tree control, but not on a tree item.

 void DestinationTree_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
 {
     TreeView tree = sender as TreeView;
     DestinationClientViewModel selectedItem = tree.SelectedItem as DestinationClientViewModel;

     if (selectedItem != null)
     {
        int selectedItemIndex = this.DestinationTree.Items.IndexOf(selectedItem);

        if (selectedItemIndex > -1)
        {
            TreeViewItem tvi = this.DestinationTree.ItemContainerGenerator.ContainerFromIndex(selectedItemIndex) as TreeViewItem;
            if (tvi != null)
                tvi.IsSelected = false;
        }
     }
 }
0
votes

I was running into this situation myself with a custom Tree List View implementation after looking for a long time I finally found a solution that worked for me.

The full explanation can be found at http://social.msdn.microsoft.com/Forums/vstudio/en-US/36aca7f7-0b47-488b-8e16-840b86addfa3/getting-treeviewitem-for-the-selected-item-in-a-treeview

The basic idea is you capture the TreeViewItem.Selected event and save the source of the event into the Tag attribute on your TreeView. Then when you need to clear it, you can access the Tag attribute on your control and set the IsSelected value to False. This works for me with 2 levels of nested children. Hopefully it will work for you.

For persistence sake:

TreeView declaration

  <TreeView Name="myTreeView" TreeViewItem.Selected="OnItemSelected"
    ItemsSource="{Binding Source={StaticResource myHierarchicalData}}"/>

Event Handler

private void OnItemSelected(object sender, RoutedEventArgs e)
{
    myTreeView.Tag = e.OriginalSource;
}

Clear selection logic

if (myTreeView.SelectedItem != null)
{
    TreeViewItem selectedTVI = myTreeView.Tag as TreeViewItem;
    // add your code here mine was selectedTVI.IsSelected = false;
}
0
votes

I just came across the same problem.

A quick and dirty solution

tree.ItemsSource = null;

tree.ItemsSource = yourSource;