5

I need to be able to display parent / child rows in a WPF datagrid, the parent are displayed with a plus / minus control which when clicked shows the related children records.

In order to do this I basically place a second datagrid inside the parent grids RowDetailsTemplate and a style trigger to flip the detailsvisibility property on each row. e.g.

<Grid>
    <DataGrid AutoGenerateColumns="False" IsReadOnly="True" ItemsSource="source" Margin="10,10,14,14" Name="dataGrid1" RowDetailsVisibilityMode="Collapsed" SelectionMode="Single">
        <DataGrid.Columns>
            <DataGridTextColumn Binding="{Binding FullName}" Header="Name" />
            <DataGridTextColumn Binding="{Binding Details}" Header="Details" />
        </DataGrid.Columns>
        <DataGrid.RowDetailsTemplate>
            <DataTemplate>
                <DataGrid HeadersVisibility="None" AutoGenerateColumns="False" IsReadOnly="True" ItemsSource="{Binding Path=Attachments}">
                    <DataGrid.Columns>
                        <DataGridTextColumn Binding="{Binding Document}" Header="Document" />
                        <DataGridTextColumn Binding="{Binding Created}" Header="Date Created" />
                    </DataGrid.Columns>
                </DataGrid>
            </DataTemplate>
        </DataGrid.RowDetailsTemplate>
        <DataGrid.RowHeaderTemplate>
            <DataTemplate>
                <ToggleButton IsChecked="{Binding Converter={StaticResource visibleConverter}, RelativeSource={RelativeSource AncestorType={x:Type DataGridRow}}, Path=DetailsVisibility}">
                    <Image>
                        <Image.Style>
                            <Style TargetType="Image">
                                <Style.Triggers>
                                    <DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType=ToggleButton}, Path=IsChecked}" Value="false">
                                        <Setter Property="Image.Source" Value="Images/plus-8.png" />
                                    </DataTrigger>
                                    <DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType=ToggleButton}, Path=IsChecked}" Value="true">
                                        <Setter Property="Image.Source" Value="Images/minus-8.png" />
                                    </DataTrigger>
                                </Style.Triggers>
                            </Style>
                        </Image.Style>
                    </Image>
                </ToggleButton>
            </DataTemplate>
        </DataGrid.RowHeaderTemplate>
    </DataGrid>
</Grid>

Problem 1 is when i use the keyboard to navigate up and down the parent grid it skips the child grid, skipping over the RowDetailsTemplate area.

Another side effect of using multiple grids is if I use the mouse to select rows I can get multiple 'selected' rows effect, ie one of the parents is selected and other child records for other parents that have been previously selected in the inner grids look like they are still highlighted.

Can anybody give me any pointers here as to how to get round these problems?

Here the code for the visibilityConverter referenced in the XAML above ;

 public class VisibleConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        bool visibleValue = false;
        if (value != null && value is Visibility)
        {
            visibleValue = !((Visibility)value == Visibility.Collapsed);
        }
        return visibleValue;
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        Visibility visibleValue = Visibility.Collapsed;
        if (value is bool)
        {
            if ((bool)value == true)
            {
                visibleValue = Visibility.Visible;
            }
        }
        return visibleValue;
    }
}
ΩmegaMan
  • 29,542
  • 12
  • 100
  • 122
Mark D
  • 279
  • 1
  • 7
  • 17
  • side effect: hack into the DataGrid's SelectedCellsChanged and/or SelectionChanged events; when fired, use EventArgs to deselect previous child cells. – Jake Berger Mar 19 '12 at 15:54
  • I'm struggling to work out how to get the previously selected child cells, any clues? I'm pretty new to WPF thanks – Mark D Mar 19 '12 at 16:25
  • well when any cells are selected, the events should be raised; so, just the cells in a local variable (e.g. List selectedCells;); then when the events are raised, check the local variable to see if there are any cells and deselect; then store the newly selected cells to the local variable. – Jake Berger Mar 19 '12 at 17:28
  • Don't use a datagrid inside another datagrid. I have never seen a good reason to do it **ever**. Use a datatemplate which mimics grid layout for the child row – NVM Mar 19 '12 at 19:31
  • thanks NVM, If i use a datatemplate that mimics the grid layout for the child will i still experience the navigation issues? – Mark D Mar 19 '12 at 20:44

1 Answers1

0

Even i have the same requirement. I have implemented the condition in following way. When the expander is opened, you can have the following code.

private void Expander_Expanded(object sender, RoutedEventArgs e)
{
   foreach (var item in viewModel.Results)
   {
     if (item.SubResults.Count > 0)
      {
        item.SubResults.SelectedItem = null;
      }
   }
}

Where Results is the items source for my outer grid, and Subresults is the datasource for my inner grid. This code helps to reset the selected item, whenever the expander is opened.

Shrikey
  • 858
  • 3
  • 11
  • 34