Search code examples
c#wpfdatagriddatagridtemplatecolumnrowdetails

Exclude a DataGrid Column from displaying RowDetails


I am teaching myself WPF, LINQ, and MVVM by writing a little tool to parse the CME XML configuration files like the current Production configuration. I have added a DataGridTemplateColumn with a checkbox to the DataGrid that displays the channels so that I can select a few channels that I'm interested in and filter out the rest. The problem is that when I click the checkbox, the RowDetails are displayed.

How can I prevent the click from triggering the display of the RowDetails while still having the "VisibleWhenSelected" behavior if any other column is clicked? Do I need to handle the visibility of the RowDetails myself or is there an easier way?

Here is the updated code with the ToggleButton added as suggested below just in case it is useful to someone else:

<DataGrid x:Name="ChannelListGrid"
          DockPanel.Dock="Top"
          IsReadOnly="True"
          AutoGenerateColumns="False"
          ItemsSource="{Binding Path=ConfigFileXML.Root.Elements[channel]}"
          RowDetailsTemplate="{StaticResource ResourceKey=ConnectionInfoTemplate}"
          IsTextSearchEnabled="True"
          HorizontalAlignment="Left"
          AreRowDetailsFrozen="True"
          RowDetailsVisibilityMode="Collapsed">
    <DataGrid.Columns>
        <DataGridTemplateColumn Width="Auto"
                                CanUserResize="False"
                                CanUserSort="false"
                                CanUserReorder="False">
            <DataGridTemplateColumn.CellTemplate>
                <DataTemplate>
                    <ToggleButton Click="ToggleRowDetails"
                                  Style="{StaticResource ToggleExpandButtonStyle}" />
                </DataTemplate>
            </DataGridTemplateColumn.CellTemplate>
        </DataGridTemplateColumn>
        <DataGridTemplateColumn Header="Selected"
                                Width="Auto"
                                CanUserReorder="False"
                                CanUserResize="False">
            <DataGridTemplateColumn.CellTemplate>
                <DataTemplate>
                    <CheckBox Unchecked="ChannelDeselected"
                              Checked="ChannelSelected" />
                </DataTemplate>
            </DataGridTemplateColumn.CellTemplate>
        </DataGridTemplateColumn>
        <DataGridTextColumn Header="ID"
                            Binding="{Binding Path=Attribute[id].Value}" />
        <DataGridTextColumn Header="Name"
                            Binding="{Binding Path=Attribute[label].Value}" />
        <DataGridTextColumn Header="Symbol Count"
                            Binding="{Binding Path=Descendants[product].Count}" />
    </DataGrid.Columns>
</DataGrid>

Here are the templates

<DataTemplate x:Key="ConnectionInfoTemplate">
    <DataGrid x:Name="ConnectionListGrid"
              IsReadOnly="True"
              HorizontalAlignment="Left"
              AutoGenerateColumns="False"
              ItemsSource="{Binding Path=Descendants[connection]}">
        <DataGrid.Columns>
            <DataGridTextColumn Header="ID"
                                Binding="{Binding Path=Attribute[id].Value}" />
            <DataGridTextColumn Header="Type"
                                Binding="{Binding Path=Element[type].Value}" />
            <DataGridTextColumn Header="Protocol"
                                Binding="{Binding Path=Element[protocol].Value}" />
            <DataGridTextColumn Header="Source IP"
                                Binding="{Binding Path=Element[ip].Value}" />
        </DataGrid.Columns>
    </DataGrid>
</DataTemplate>
<SolidColorBrush x:Key="GlyphBrush"
                 Color="#444" />

<Style x:Key="ToggleExpandButtonStyle"
       TargetType="{x:Type ToggleButton}">
    <Setter Property="IsChecked"
            Value="False" />
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type ToggleButton}">
                <Grid Width="15"
                      Height="13"
                      Background="Transparent">
                    <Path x:Name="ExpandPath"
                          HorizontalAlignment="Left"
                          VerticalAlignment="Center"
                          Margin="1,1,1,1"
                          Fill="{StaticResource GlyphBrush}"
                          Data="M 4 0 L 8 4 L 4 8 Z" />
                </Grid>
                <ControlTemplate.Triggers>
                    <Trigger Property="IsChecked"
                             Value="True">
                        <Setter Property="Data"
                                TargetName="ExpandPath"
                                Value="M 0 4 L 8 4 L 4 8 Z" />
                    </Trigger>
                </ControlTemplate.Triggers>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

Here is the code behind in C#

private T FindAncestor<T>(DependencyObject depObject)
    where T : DependencyObject
{
    var parent = VisualTreeHelper.GetParent(depObject);
    if (parent == null) return null;

    var parentT = parent as T;
    return parentT ?? FindAncestor<T>(parent);
}

private void ToggleRowDetails(object sender, System.Windows.RoutedEventArgs e)
{
    var senderButton = sender as ToggleButton;
    DataGridRow toggledRow = FindAncestor<DataGridRow>(senderButton);

    if (toggledRow != null)
    {
        // If IsChecked is null, use false. If true, make details visible, otherwise collapse the details
        toggledRow.DetailsVisibility = ( senderButton.IsChecked ?? false) ? Visibility.Visible : Visibility.Collapsed;
    }
}

Solution

  • I had a similar problem and decided to add a toggle button column to manually show the row details.

    Style

    <SolidColorBrush x:Key="GlyphBrush" Color="#444" />
    
    <Style x:Key="ToggleExpandButtonStyle" TargetType="{x:Type ToggleButton}">
        <Setter Property="IsChecked" Value="False"/>
        <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type ToggleButton}">
                <Grid Width="15" Height="13" Background="Transparent">
                    <Path x:Name="ExpandPath" HorizontalAlignment="Left" VerticalAlignment="Center" 
                              Margin="1,1,1,1" Fill="{StaticResource GlyphBrush}" Data="M 4 0 L 8 4 L 4 8 Z"/>
                </Grid>
                <ControlTemplate.Triggers>
                    <Trigger Property="IsChecked" Value="True">
                        <Setter Property="Data" TargetName="ExpandPath" Value="M 0 4 L 8 4 L 4 8 Z"/>
                    </Trigger>
                </ControlTemplate.Triggers>
            </ControlTemplate>
        </Setter.Value>
        </Setter>
    </Style>
    

    Datagrid column

                <DataGridTemplateColumn Width="Auto" CanUserResize="False" CanUserSort="false" CanUserReorder="False">
                    <DataGridTemplateColumn.CellTemplate>
                        <DataTemplate>
                        <ToggleButton Click="ToggleButton_Click" Style="{StaticResource ToggleExpandButtonStyle}"/>
                        </DataTemplate>
                    </DataGridTemplateColumn.CellTemplate>
                </DataGridTemplateColumn>
    

    Click event (in VB)

        Public Shared Sub ToggleButton_Click(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs)
    
        Dim senderButton As Primitives.ToggleButton
        senderButton = CType(sender, Primitives.ToggleButton)
        Dim clickedRow As DataGridRow = CType(GetVisualParent(senderButton, GetType(DataGridRow)), DataGridRow)
    
        If clickedRow IsNot Nothing Then
            If senderButton.IsChecked Then
                clickedRow.DetailsVisibility = Windows.Visibility.Visible
                clickedRow.BringIntoView()
            Else
                clickedRow.DetailsVisibility = Windows.Visibility.Collapsed
            End If
        End If
      End Sub