title: WPF Treeview with grouping - auto expand child nodes
body: <p>I am building a WPF application that displays data in a tree view.  The TreeView's ItemsSource is a CollectionViewSource that is grouped.  I am using a HierarchicalDataTemplate to render the tree's nodes.  All of this is working great.  I am struggling, however, with auto-expanding/collapsing nodes in the tree.  I need two things: 1) to be able to click a button and automatically expand/collapse all nodes in the tree.  I have this part working.  2) When a node is clicked, I need to expand the first child node down the tree and automatically select the first item.  To be clear, here is what I am trying to do:</p>
  
  <p><img src="https://i.stack.imgur.com/XS3gC.jpg" alt="enter image description here"></p>
  
  <p>I have searched Google and SO high and low for days but have not come up with anything that works well.  I'm pretty sure I need to use either the LogicalTreeHelper or VisualTreeHelper to find the right nodes but just can't seem to figure it out.  Anyway, here is what I have so far....</p>
  
  <p>XAML:</p>
  
  <pre><code>&lt;Window x:Class="WpfApplication4.MainWindow2"
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      xmlns:local="clr-namespace:WpfApplication4"
      Title="MainWindow" Height="400" Width="500"&gt;
  
  &lt;Window.Resources&gt;
      &lt;local:DocumentViewModel x:Key="documents" /&gt;
  
      &lt;CollectionViewSource Source="{Binding Source={StaticResource documents}, Path=People}" x:Key="DocumentCVS"&gt;
          &lt;CollectionViewSource.GroupDescriptions&gt;
              &lt;PropertyGroupDescription PropertyName="Encounter" /&gt;
              &lt;PropertyGroupDescription PropertyName="Title" /&gt;
          &lt;/CollectionViewSource.GroupDescriptions&gt;
      &lt;/CollectionViewSource&gt;
  
      &lt;DataTemplate x:Key="documentTemplate"&gt;
          &lt;TextBlock Text="{Binding Path=Subtitle}"/&gt;
      &lt;/DataTemplate&gt;
  
      &lt;HierarchicalDataTemplate x:Key="level2Template" ItemsSource="{Binding Path=Items}" ItemTemplate="{StaticResource documentTemplate}"&gt;
          &lt;TextBlock Text="{Binding Path=Name}" FontWeight="Bold"/&gt;
      &lt;/HierarchicalDataTemplate&gt;
  
      &lt;HierarchicalDataTemplate x:Key="level1Template" ItemsSource="{Binding Path=Items}" ItemTemplate="{StaticResource level2Template}"&gt;
          &lt;TextBlock Text="{Binding Path=Name}" FontWeight="Bold"/&gt;
      &lt;/HierarchicalDataTemplate&gt;
  &lt;/Window.Resources&gt;
  
  &lt;DockPanel&gt;
      &lt;StackPanel Orientation="Horizontal" DockPanel.Dock="Top"&gt;
          &lt;Button Name="Expand_Button" Content="Expand All" Padding="5" Margin="5" Click="Expand_Button_Click" /&gt;
          &lt;Button Name="Collapse_Button" Content="Collapse All" Padding="5" Margin="5" Click="Collapse_Button_Click" /&gt;
          &lt;Button Name="Invert_Button" Content="Invert All" Padding="5" Margin="5" Click="Invert_Button_Click" /&gt;
      &lt;/StackPanel&gt;
  
      &lt;TreeView Name="Document_TreeView" ItemsSource="{Binding Source={StaticResource DocumentCVS}, Path=Groups}" Margin="5"
                ItemTemplate="{StaticResource level1Template}"                   SelectedItemChanged="Document_TreeView_SelectedItemChanged"
                &gt;
  
      &lt;/TreeView&gt;
  
  &lt;/DockPanel&gt;
  </code></pre>
  
  <p></p>
  
  <p>Code Behind:</p>
  
  <pre><code>using System.Diagnostics;
  using System.Windows;
  using System.Windows.Controls;
  using System.Windows.Data;
  using System.Windows.Media;
  using System.Windows.Threading;
  
  namespace WpfApplication4
  {
      /// &lt;summary&gt;
      /// Interaction logic for MainWindow.xaml
      /// &lt;/summary&gt;
      public partial class MainWindow2 : Window
      {
          public MainWindow2()
          {
              InitializeComponent();
          }
  
          private void Expand_Button_Click(object sender, RoutedEventArgs e)
          {
              foreach (object item in Document_TreeView.Items)
              {
                  TreeViewItem treeItem = Document_TreeView.ItemContainerGenerator.ContainerFromItem(item) as TreeViewItem;
                  treeItem.IsExpanded = true;
                  DoEvents();
                  if (treeItem != null) ToggleChild(treeItem, true);
              }
          }
  
          private void Collapse_Button_Click(object sender, RoutedEventArgs e)
          {
              foreach (object item in Document_TreeView.Items)
              {
                   TreeViewItem treeItem = Document_TreeView.ItemContainerGenerator.ContainerFromItem(item) as TreeViewItem;
                  treeItem.IsExpanded = false;
                  DoEvents();
                  if (treeItem != null) ToggleChild(treeItem, false);
              }
          }
  
          private void Invert_Button_Click(object sender, RoutedEventArgs e)
          {
              foreach (object item in Document_TreeView.Items)
              {
                  TreeViewItem treeItem = Document_TreeView.ItemContainerGenerator.ContainerFromItem(item) as TreeViewItem;
  
                  bool isCurrentlyExpanded = treeItem.IsExpanded;
  
                  treeItem.IsExpanded = !isCurrentlyExpanded;
                  DoEvents();
                  if (treeItem != null) ToggleChild(treeItem, !isCurrentlyExpanded);
              }
          }
  
          private void Document_TreeView_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs&lt;object&gt; e)
          {
              ItemsControl selected = Document_TreeView.ItemContainerGenerator.ContainerFromItem(Document_TreeView.SelectedItem) as ItemsControl;
              ToggleChild(selected, true);
          }
  
          private void ToggleChild(ItemsControl items, bool expand)
          {
              foreach (object obj in items.Items)
              {
                  ItemsControl childControl = items.ItemContainerGenerator.ContainerFromItem(obj) as ItemsControl;
                  if (childControl != null)
                  {
                      ToggleChild(childControl, expand);
                  }
                  TreeViewItem item = childControl as TreeViewItem;
                  if (item != null) item.IsExpanded = true;
              }
          }
  
          private delegate void EmptyDelegate();
  
          public static void DoEvents()
          {
              Dispatcher.CurrentDispatcher.Invoke(DispatcherPriority.Background, new EmptyDelegate(delegate { }));
          }
      }
  }
  </code></pre>
  
  <p>As you can see, I have the auto-expanding/collapsing of ALL nodes working but I cant seem to find a way to click a node and expand down and select the first child.  When I call the same ToggleChild() method, the resulting ItemsControl is null. What am I doing wrong? </p>