Brothers In Code

...a serious misallocation of .net resources

Grouping TreeView Items in WPF

I had a view model that consisted of a linked list of nodes.  I wanted to group one set of nodes so I decided to try out the grouping functionality of CollectionViewSource.

Here's my XAML:


    <HierarchicalDataTemplate x:Key="ActionNodeTemplate">
      <tool:ActionNavigatorActionNodeUC />
    </HierarchicalDataTemplate>   
    <HierarchicalDataTemplate x:Key="GroupTemplate" ItemsSource="{Binding Items}" ItemTemplate="{StaticResource ActionNodeTemplate}">
      <Label Foreground="Green" Content="{Binding Path=Name}" Name="NodeLabel"/>
    </HierarchicalDataTemplate>
    <HierarchicalDataTemplate DataType="{x:Type an:ActionProjectSummaryNode}" ItemsSource="{Binding SubItemsView.View.Groups}" ItemTemplate="{StaticResource GroupTemplate}">
      <Label Foreground="Red" Content="{Binding Path=DisplayText}" Name="NodeLabel"/>
    </HierarchicalDataTemplate>
    <HierarchicalDataTemplate DataType="{x:Type an:RootActionNavigatorNode}" ItemsSource="{Binding SubItems}">
      <Label Foreground="Blue" Content="{Binding Path=DisplayText}" Name="NodeLabel"/>
    </HierarchicalDataTemplate>

ProjectActionSummaryNode exposed a CollectionViewSource of ActionNode items named SubItemsView.  In that node i grouped by the Data.ActionTypeDescription with:


       SubItemsView.GroupDescriptions.Clear();
       SubItemsView.GroupDescriptions.Add(new PropertyGroupDescription("Data.ActionTypeDescription"));

That was pretty much it.  For nodes of type ActionProjectSummaryNode, ItemsSource was set to SubItemsView.View.Groups.  Groups is a ReadOnlyObervableCollection of CollectionViewGroup objects (CollectionViewGroupInternal actually).  Each one has a Name property that represents the Group (in this case Data.ActionTypeDescription), and a Items Property that contains the original items that were grouped (ActionNodes).

WPF ToolBar Buttons Do Not Cause Loss Of Focus

Consider this little WPF Window:


    <DockPanel>
      <ToolBar DockPanel.Dock="Top" >
        <Button Content="Save" Name="BtnSave" Click="BtnSave_Click" />
      </ToolBar>
      <StackPanel>
        <TextBox Name="IptTextBox" Text="{Binding Content, ElementName=OptLabel}"  />
        <Label Name="OptLabel" Content="Hello" />
        <Button Content="Lose Focus" />
      </StackPanel>               
    </DockPanel>

If you type something into the text box and then click the "Lose Focus" button, the label is updated as expected (since the default UpdateSourceTrigger on the binding is on focus).  However, if you click on the save button in the toolbar, the label is not updated.

According to MSDN, ToolBars, in addition to a couple of other objects, have a different "Logical Focus Scope." To fix this problem, I added the following code to my save button:


      Control focusedElement = FocusManager.GetFocusedElement(this) as Control;
      if (focusedElement != null)
      {
        focusedElement.MoveFocus(new TraversalRequest(FocusNavigationDirection.Next));
        focusedElement.Focus();
      }