tags:

views:

2300

answers:

4

I've got a rather funny problem with WPF. I have a tree-view, and selecting items works fine so far. The problem is, I want to unselect the currently selected item when the user clicks inside the blank area of the treeview. By default, the treeview keeps the current item selected, and I have added a context-menu option to deselect it, which is rather hardcore:

// Note: This is done recursivly from the start, so it
// works for child items as well
treeView.ItemContainerGenerator.ContainerFromItem(treeView.SelectedItem) as TreeViewItem).IsSelected = false;

Moreover, this is counter-intuitive, as it requires the user to right-click first, and second, after deselecting it with this way, the user cannot select it any more by clicking on the item. How is this supposed to work?

Edit: Some more information: I've added a handler to the TreeView to handle mouse click events, but the sender is always a TreeView instance, even if I click directly on a TreeViewItem. If I add a handler to my TreeView.ItemTemplate instead (i.e. the first child in the template), I never get events when I click on the empty area (which is rather logical). The code looks like this:

    private void MyTreeView_MouseUp(object sender, System.Windows.Input.MouseButtonEventArgs e)
    {
        if ((sender as TreeViewItem) == null)
        {
            // Always triggered
            System.Diagnostics.Trace.Write("Empty area clicked");
        }
    }

And the XAML for this is:

<TreeView x:Name="MyTreeView" Margin="3" MouseUp="MyTreeView_MouseUp">
A: 

This will deselect the currently selected TreeViewItem if none were clicked:

private void MyTreeView_PreviewMouseDown(object sender, MouseButtonEventArgs e) {
    if ((sender as TreeViewItem) == null) {
        TreeViewItem item = MyTreeView.SelectedItem as TreeViewItem;
        if(item != null){
            item.IsSelected = false;                    
        }
    }
}

Hope this is what you were looking for!

Pwninstein
Does not work, because after selecting an item, sender is bound to the currently selected item - that is, even if I click into the empty area, sender == the currently selected item.
Anteru
Hmm. If you click on the TreeView itself, the sender should be a TreeView (not a TreeViewItem). Did you add this handler to the TreeView or to each TreeViewItem? Are you using the standard TreeView or a custom one?
Pwninstein
Ahh the problem is I don't get a TreeViewItem, but rather an instance of my viewmodel class, for which I have to find the corresponding TreeViewItem. Worse still, after unselecting an item programmatically, I cannot reselect by clicking on it.
Anteru
More problems, expanding an item deselects it with this approach - and I can't get it working for child items, as ItemContainerGenerator.ContainerFromItem returns null for that?
Anteru
Could you edit your original post to include some more code so I can get a better idea of what's going on? Thanks!
Pwninstein
+2  A: 

The un-selectable problem can be solved with a call to Focus on the TreeView after setting TreeViewItem.IsSelected.

Robert Macnee
Nice, Good Catch!I was wondering why I wasn't able to select the same node once it was unselected.Thanks!
HaxElit
+2  A: 

I found this to work much better for me. I check the originalsource which for me if it comes form a treeviewitem will be an image or a textblock. I also use a view object with a HierarchicalDataTemplate and the BasicTreeViewBase is the base class for all of my different objects. Here is the code.

private void TemplateTreeView_MouseDown(object sender, MouseButtonEventArgs e)
    {
        if (e.ChangedButton == MouseButton.Right && !(e.OriginalSource is Image) && !(e.OriginalSource is TextBlock))
        {
            BasicTreeViewBase item = TemplateTreeView.SelectedItem as BasicTreeViewBase;
            if (item != null)
            {
                TemplateTreeView.Focus();
                item.IsSelected = false;
            }
        }
    }
Erin
+1  A: 

I implemented a general selection control once, and required this behaviour.

This is how my method looked (adapted for treeview):

protected override void OnMouseUp(MouseButtonEventArgs e)
{
    base.OnMouseUp(e);

    DependencyObject dpSource = e.OriginalSource as DependencyObject;

    if (dpSource.FindVisualAncestor(o => typeof(TreeViewItem).IsAssignableFrom(o.GetType())) == null)
            UnselectAll();
}

Basically, walk up the tree from the source. If a TreeViewItem was not found, than the user clicked empty space.

kek444