0
votes

I want to collapse or expand all TreeView nodes under parent node if user holds down Left Control key and presses left mouse button on expansion arrow of tree view.

How do you do this in WPF? It's not as obvious as it was in WinForms.

1

1 Answers

1
votes

Here's something that seems to work for me:

<TreeView TreeViewItem.Collapsed="TreeViewItem_Collapsed" TreeViewItem.Expanded="TreeViewItem_Expanded"/>

(The above is omitting things like ItemsSource and ItemTemplate for brevity)

Private Sub TreeViewItem_Expanded(sender As Object, e As RoutedEventArgs)
    If Keyboard.IsKeyDown(Key.LeftCtrl) Then DirectCast(e.OriginalSource, TreeViewItem).ExpandSubtree()
End Sub

Private Sub TreeViewItem_Collapsed(sender As Object, e As RoutedEventArgs)
    If Keyboard.IsKeyDown(Key.LeftCtrl) Then CollapseSubtree(e.OriginalSource)
End Sub

Private Sub CollapseSubtree(Item As TreeViewItem)
    Item.IsExpanded = False

    For Each Child In Item.ItemContainerGenerator.Items
        CollapseSubtree(Item.ItemContainerGenerator.ContainerFromItem(Child))
    Next
End Sub

Explanation

Since TreeViewItem.Expanded and TreeViewItem.Collapsed are both routed events, we can handle them at the TreeView level as they "bubble" up. All items in the TreeView will trigger either TreeViewItem_Collapsed or TreeViewItem_Expanded when they close or open, respectively.

In my quick tests, e.OriginalSource always referred to the TreeViewItem that was expanded/collapsed, but some more rigorous testing might be in order.

In the event handlers, we use Keyboard.IsKeyDown to determine whether the left control key is currently being pressed. If so, we take the appropriate action.

For expansion, TreeViewItem already has a convenient ExpandSubtree method, so we can just use that.

For whatever reason, there is no matching CollapseSubtree built in, so I made my own. At its heart, it's a simple recursive method that closes the node you give it and then runs itself again for all child nodes. The trick is in getting the child TreeViewItems (containers), instead of the data objects they are representing. To do this, I make use of the ItemContainerGenerator, which is the class that each TreeViewItem uses to create its children.