Share via

How can I create a Datagrid with a nested Datagrid with multiple row in WPF and C#?

NicoCord 21 Reputation points
2026-05-12T18:15:14.1066667+00:00

I have 2 classes, the first one is Wire class and the second is Subwire Class. A Wire can have 1 to many Subwires so the nested datagrid of Subwire can have multiple rows for 1 Wire Row.

The datagrid shouyld show the Wire and when we click on an expander icon in the 1st column we can see the Subwires rows in the nested datagrid. Here is what to start with.

Here are the 2 classes:

public class Wire
{
    public int WIWireID { get; set; }
    public string WireNum { get; set; }
    public string Type { get; set; }
    public string Category { get; set; }
}
public class SubWire
{
    public int SubWireNum { get; set; }
    public string Printing { get; set; }
    public string Gauge { get; set; }
    public string Color { get; set; }    
}

Here is the Datagrid in XAML: (wich I don't know if it is good for the purpose I intend to use it for)

<DataGrid x:Name="dgvPreview" Grid.Column="0" Grid.Row="1" Width="1900" HorizontalContentAlignment="Center" AutoGenerateColumns="False" Margin="3,3,3,3" IsReadOnly="True"  FontSize="16"          SelectionMode="Single" SelectionUnit="Cell" SelectedCellsChanged="dgvPreview_SelectedCellsChanged">
    <DataGrid.Columns>
        <DataGridTemplateColumn Width="Auto">
            <DataGridTemplateColumn.CellTemplate>
                <DataTemplate>
                    <Expander Width="50" Expanded="Expander_Expanded" Collapsed="Expander_Collapsed" IsExpanded="False" />
                </DataTemplate>
            </DataGridTemplateColumn.CellTemplate>
        </DataGridTemplateColumn>
        <DataGridTextColumn Header="#" Binding="{Binding WIWireID}"  Width="Auto"/>
        <DataGridTextColumn Header="Wire" Binding="{Binding WireNum}" Width="Auto"/>
        <DataGridTextColumn Header="Type" Binding="{Binding Type}" Width="Auto"/>
        <DataGridTextColumn Header="Category" Binding="{Binding Category}" Width="Auto"/>                       
    </DataGrid.Columns>
    <DataGrid.RowDetailsTemplate>
        <DataTemplate>
            <DataGrid x:Name="dgvSubWire" IsReadOnly="True" Margin="15,8,8,8"  Width="399" AutoGenerateColumns="False" >
                <DataGrid.Columns>
                    <DataGridTextColumn Header="#" Binding="{Binding SubWireNum}" Width="Auto"/>
                    <DataGridTextColumn Header="Printing" Binding="{Binding Printing}" Width="Auto"/>
                    <DataGridTextColumn Header="Gauge" Binding="{Binding Gauge}" Width="Auto"/>
                    <DataGridTextColumn Header="Color" Binding="{Binding Color}" Width="Auto">                        
                </DataGrid.Columns>
            </DataGrid>
        </DataTemplate>
    </DataGrid.RowDetailsTemplate>
</DataGrid>

What would be te code-behind to make this works in C#?

Developer technologies | C#
Developer technologies | C#

An object-oriented and type-safe programming language that has its roots in the C family of languages and includes support for component-oriented programming.


Answer accepted by question author

Nancy Vo (WICLOUD CORPORATION) 4,765 Reputation points Microsoft External Staff Moderator
2026-05-13T03:46:29.5166667+00:00

Hello @NicoCord ,

Thanks for your question.

In WPF, inside RowDetailsTemplate, the DataContext is already the current row’s Wire object, so the nested grid should bind its ItemsSource to SubWires.

You can refer to following code example:

public class Wire
{
    public int WIWireID { get; set; }
    public string WireNum { get; set; }
    public string Type { get; set; }
    public string Category { get; set; }

    public List<SubWire> SubWires { get; set; } = new List<SubWire>();
}

public class SubWire
{
    public int SubWireNum { get; set; }
    public string Printing { get; set; }
    public string Gauge { get; set; }
    public string Color { get; set; }
}

Bind the parent grid ItemsSource to a list of Wire.

In RowDetailsTemplate, bind the child grid ItemsSource="{Binding SubWires}".

<DataGrid x:Name="dgvPreview"
          AutoGenerateColumns="False"
          IsReadOnly="True"
          RowDetailsVisibilityMode="Collapsed">
    <DataGrid.Columns>
        <DataGridTemplateColumn Width="Auto">
            <DataGridTemplateColumn.CellTemplate>
                <DataTemplate>
                    <Expander Expanded="Expander_Expanded"
                              Collapsed="Expander_Collapsed"/>
                </DataTemplate>
            </DataGridTemplateColumn.CellTemplate>
        </DataGridTemplateColumn>

        <DataGridTextColumn Header="#" Binding="{Binding WIWireID}"/>
        <DataGridTextColumn Header="Wire" Binding="{Binding WireNum}"/>
        <DataGridTextColumn Header="Type" Binding="{Binding Type}"/>
        <DataGridTextColumn Header="Category" Binding="{Binding Category}"/>
    </DataGrid.Columns>

    <DataGrid.RowDetailsTemplate>
        <DataTemplate>
            <DataGrid IsReadOnly="True"
                      AutoGenerateColumns="False"
                      ItemsSource="{Binding SubWires}"
                      Margin="15,8,8,8">
                <DataGrid.Columns>
                    <DataGridTextColumn Header="#" Binding="{Binding SubWireNum}"/>
                    <DataGridTextColumn Header="Printing" Binding="{Binding Printing}"/>
                    <DataGridTextColumn Header="Gauge" Binding="{Binding Gauge}"/>
                    <DataGridTextColumn Header="Color" Binding="{Binding Color}"/>
                </DataGrid.Columns>
            </DataGrid>
        </DataTemplate>
    </DataGrid.RowDetailsTemplate>
</DataGrid>

In code-behind, instead of adding directly to dgvPreview.Items, build a List<Wire> and assign it:

private void FillPreviewDgv(int workInstructionId)
{
    List<Wire> wires = new List<Wire>();

    viewWorkinstructionsDetailsTableAdapter.FillByWorkInstructionID(
        harnessesCostDataSet.ViewWorkinstructionsDetails,
        workInstructionId);

    var wiRows = (HarnessesCostDataSet.ViewWorkinstructionsDetailsRow[])
        harnessesCostDataSet.ViewWorkinstructionsDetails.Select("", "WireNum");

    Wire current = null;
    string lastWireNum = null;

    foreach (var wiRow in wiRows)
    {
        if (lastWireNum != wiRow.WireNum)
        {
            current = new Wire
            {
                WIWireID = wiRow.WIWireID,
                WireNum = wiRow.WireNum,
                Type = wiRow.Type,
                Category = wiRow.Category,
                SubWires = GetSubWire(wiRow.WIWireID)
            };

            wires.Add(current);
            lastWireNum = wiRow.WireNum;
        }
    }

    dgvPreview.ItemsSource = wires;
}

Child loader stays basically the same:

private List<SubWire> GetSubWire(int wireId)
{
    List<SubWire> subWires = new List<SubWire>();

    wiSubWireTableAdapter.FillByWIWireID(harnessesCostDataSet.WISubWire, wireId);

    var subWireRows = (HarnessesCostDataSet.WISubWireRow[])
        harnessesCostDataSet.WISubWire.Select("WIWireID = " + wireId);

    foreach (var row in subWireRows)
    {
        subWires.Add(new SubWire
        {
            SubWireNum = row.SubWireNum,
            Printing = row.Printing,
            Gauge = row.Gauge,
            Color = row.Color
        });
    }

    return subWires;
}

I hope this addresses your question. If this response was helpful, please consider following the guidance to provide feedback.

Was this answer helpful?

1 person found this answer helpful.

1 additional answer

Sort by: Most helpful
  1. AI answer

    2026-05-12T18:16:01.52+00:00

    Use the RowDetailsTemplate to host the nested DataGrid and bind it to a collection of SubWire objects on each Wire. The parent DataGrid binds to a collection of Wire.

    1. Extend the model so each Wire exposes its SubWires collection:
    public class Wire
    {
        public int WIWireID { get; set; }
        public string WireNum { get; set; }
        public string Type { get; set; }
        public string Category { get; set; }
    
        // Collection of SubWires for this Wire
        public ObservableCollection<SubWire> SubWires { get; set; } = new();
    }
    
    public class SubWire
    {
        public int SubWireNum { get; set; }
        public string Printing { get; set; }
        public string Gauge { get; set; }
        public string Color { get; set; }
    }
    
    1. Bind the main DataGrid to a collection of Wire in code-behind (or view model):
    public partial class MainWindow : Window
    {
        public ObservableCollection<Wire> Wires { get; } = new();
    
        public MainWindow()
        {
            InitializeComponent();
    
            // Sample data
            var w1 = new Wire
            {
                WIWireID = 1,
                WireNum = "W-001",
                Type = "TypeA",
                Category = "Cat1"
            };
            w1.SubWires.Add(new SubWire
            {
                SubWireNum = 1,
                Printing = "Print1",
                Gauge = "10",
                Color = "Red"
            });
            w1.SubWires.Add(new SubWire
            {
                SubWireNum = 2,
                Printing = "Print2",
                Gauge = "12",
                Color = "Blue"
            });
    
            var w2 = new Wire
            {
                WIWireID = 2,
                WireNum = "W-002",
                Type = "TypeB",
                Category = "Cat2"
            };
            w2.SubWires.Add(new SubWire
            {
                SubWireNum = 1,
                Printing = "Print3",
                Gauge = "8",
                Color = "Green"
            });
    
            Wires.Add(w1);
            Wires.Add(w2);
    
            // Bind DataGrid to Wires collection
            dgvPreview.ItemsSource = Wires;
    
            // Optional: show row details when row is selected or always
            dgvPreview.RowDetailsVisibilityMode = DataGridRowDetailsVisibilityMode.VisibleWhenSelected;
        }
    }
    
    1. Update XAML so the nested DataGrid binds its ItemsSource to SubWires of the current Wire row, and use RowDetailsTemplate for the nested grid. The expander in the first column can control RowDetailsVisibilityMode if desired, but the simplest pattern is to use RowDetailsTemplate directly:
    <DataGrid x:Name="dgvPreview"
              AutoGenerateColumns="False"
              IsReadOnly="True"
              SelectionMode="Single"
              SelectionUnit="FullRow">
        <DataGrid.Columns>
            <!-- Optional: expander column if manual control of details is needed -->
            <DataGridTemplateColumn Width="Auto">
                <DataGridTemplateColumn.CellTemplate>
                    <DataTemplate>
                        <Expander Width="20"
                                  IsExpanded="False"
                                  Expanded="Expander_Expanded"
                                  Collapsed="Expander_Collapsed" />
                    </DataTemplate>
                </DataGridTemplateColumn.CellTemplate>
            </DataGridTemplateColumn>
    
            <DataGridTextColumn Header="#" Binding="{Binding WIWireID}" Width="Auto" />
            <DataGridTextColumn Header="Wire" Binding="{Binding WireNum}" Width="Auto" />
            <DataGridTextColumn Header="Type" Binding="{Binding Type}" Width="Auto" />
            <DataGridTextColumn Header="Category" Binding="{Binding Category}" Width="Auto" />
        </DataGrid.Columns>
    
        <!-- Nested DataGrid for SubWires -->
        <DataGrid.RowDetailsTemplate>
            <DataTemplate>
                <DataGrid x:Name="dgvSubWire"
                          IsReadOnly="True"
                          AutoGenerateColumns="False"
                          ItemsSource="{Binding SubWires}"
                          Margin="15,8,8,8">
                    <DataGrid.Columns>
                        <DataGridTextColumn Header="#" Binding="{Binding SubWireNum}" Width="Auto" />
                        <DataGridTextColumn Header="Printing" Binding="{Binding Printing}" Width="Auto" />
                        <DataGridTextColumn Header="Gauge" Binding="{Binding Gauge}" Width="Auto" />
                        <DataGridTextColumn Header="Color" Binding="{Binding Color}" Width="Auto" />
                    </DataGrid.Columns>
                </DataGrid>
            </DataTemplate>
        </DataGrid.RowDetailsTemplate>
    </DataGrid>
    
    1. If the expander in the first column must control the visibility of the row details, handle its events and toggle RowDetailsVisibility for the row:
    private void Expander_Expanded(object sender, RoutedEventArgs e)
    {
        var expander = (Expander)sender;
        var row = FindParent<DataGridRow>(expander);
        if (row != null)
            row.DetailsVisibility = Visibility.Visible;
    }
    
    private void Expander_Collapsed(object sender, RoutedEventArgs e)
    {
        var expander = (Expander)sender;
        var row = FindParent<DataGridRow>(expander);
        if (row != null)
            row.DetailsVisibility = Visibility.Collapsed;
    }
    
    private static T FindParent<T>(DependencyObject child) where T : DependencyObject
    {
        while (child != null)
        {
            var parent = VisualTreeHelper.GetParent(child);
            if (parent is T typed)
                return typed;
            child = parent;
        }
        return null;
    }
    

    This uses the built-in RowDetailsTemplate of DataGrid to host a nested DataGrid bound to the SubWires collection of each Wire, giving multiple SubWire rows per Wire row.


    References:

    AI-generated content may be incorrect. Read our transparency notes for more information.

    Was this answer helpful?

Your answer

Answers can be marked as 'Accepted' by the question author and 'Recommended' by moderators, which helps users know the answer solved the author's problem.