I want to create a DataGrid in my WPF Application which has 2 rows, and 14 columns. I have defined a method which creates this data-grid during runtime.
The issue that I'm facing is that both the rows display the same text.

As can be seen in the attached image, both the rows are displaying coach numbers from C01 to C14. However, I want the first row to remain as it is, and the second row to display coach numbers from C15 to C28.
The method that I'm currently using is:
private void CreateCoachCompositionDataGrid()
{
// Clear the existing columns in the data grid
dataGridCoachComposition.Columns.Clear();
// Set the margin for the data grid
dataGridCoachComposition.Margin = new Thickness(16, 468, 230, 18);
// Hide the column headers
dataGridCoachComposition.HeadersVisibility = DataGridHeadersVisibility.None;
// Set the row height
dataGridCoachComposition.RowHeight = 50; // Set this to your desired row height
// Set the vertical alignment to top to remove empty space at the bottom
dataGridCoachComposition.VerticalAlignment = VerticalAlignment.Top;
// Create 14 columns
for (int i = 1; i <= 14; i++)
{
// Create a new DataGridTemplateColumn
DataGridTemplateColumn column = new DataGridTemplateColumn();
column.Width = new DataGridLength(1, DataGridLengthUnitType.Star); // Set the column width to fill the available space
// Create a DataTemplate for the cell template
DataTemplate cellTemplate = new DataTemplate();
// Create a StackPanel to hold the TextBlock and ComboBox
FrameworkElementFactory stackPanel = new FrameworkElementFactory(typeof(StackPanel));
stackPanel.SetValue(StackPanel.OrientationProperty, Orientation.Vertical);
// Create a TextBlock for the coach number
FrameworkElementFactory textBlock = new FrameworkElementFactory(typeof(TextBlock));
textBlock.SetValue(TextBlock.TextProperty, "C" + i.ToString("00"));
textBlock.SetValue(TextBlock.HorizontalAlignmentProperty, HorizontalAlignment.Center); // Center the text block horizontally
stackPanel.AppendChild(textBlock);
// Create a ComboBox for the coach type
FrameworkElementFactory comboBox = new FrameworkElementFactory(typeof(ComboBox));
comboBox.SetValue(ComboBox.ItemsSourceProperty, new string[] { "ENG", "HIN" });
comboBox.SetValue(FrameworkElement.MarginProperty, new Thickness(5)); // Set a margin around the ComboBox
stackPanel.AppendChild(comboBox);
// Set the cell template's visual tree to the StackPanel
cellTemplate.VisualTree = stackPanel;
// Set the column's cell template
column.CellTemplate = cellTemplate;
// Add the column to the data grid
dataGridCoachComposition.Columns.Add(column);
}
// Add two empty rows to the data grid
dataGridCoachComposition.Items.Add(new object());
dataGridCoachComposition.Items.Add(new object());
}
I tried something like this:
// Adjust the labels for each cell
for (int i = 0; i < 14; i++)
{
((TextBlock)((StackPanel)dataGridCoachComposition.Columns[i].GetCellContent(dataGridCoachComposition.Items[0])).Children[0]).Text = "C" + (i + 1).ToString("00");
((TextBlock)((StackPanel)dataGridCoachComposition.Columns[i].GetCellContent(dataGridCoachComposition.Items[1])).Children[0]).Text = "C" + (i + 15).ToString("00");
}
However, this was throwing a NullReferenceException. To fix this, I tried using the Dispatcher class to schedule the execution of the code.
The error still persists.
Update: Tried a different approach by creating a DataTable.
My XAML code:
<DataTemplate x:Key="ComboBoxTemplate">
<StackPanel>
<TextBlock Text="{Binding}" />
<ComboBox Margin="5">
<ComboBoxItem>ENG</ComboBoxItem>
<ComboBoxItem>HIN</ComboBoxItem>
</ComboBox>
</StackPanel>
</DataTemplate>
<DataGrid x:Name="dataGridCoachComposition" AutoGenerateColumns="True" CanUserAddRows="False" HeadersVisibility="None" Margin="16,468,14,0" VerticalAlignment="Top" RowHeight="50" ColumnWidth="*"/>
My SetupDataGrid() method:
private void SetupDataGrid()
{
DataTable dataTable = new DataTable();
// Define 14 columns
for (int i = 1; i <= 14; i++)
{
dataTable.Columns.Add($"C{i:D2}");
}
// Add two rows of data
string[] firstRow = new string[14];
string[] secondRow = new string[14];
for (int i = 0; i < 14; i++)
{
firstRow[i] = $"C{i + 1:D2}";
secondRow[i] = $"C{i + 15:D2}";
}
dataTable.Rows.Add(firstRow);
dataTable.Rows.Add(secondRow);
// Bind the DataTable to the DataGrid
dataGridCoachComposition.ItemsSource = dataTable.DefaultView;
// Clear auto generated columns
dataGridCoachComposition.AutoGenerateColumns = false;
dataGridCoachComposition.Columns.Clear();
// Add columns with ComboBox
for (int i = 0; i < 14; i++)
{
DataGridTemplateColumn column = new DataGridTemplateColumn();
column.Header = $"C{i + 1:D2}";
column.CellTemplate = this.FindResource("ComboBoxTemplate") as DataTemplate;
dataGridCoachComposition.Columns.Add(column);
}
}
This is displaying the coach numbers and combo-boxes. However, instead of displaying just the coach numbers, entire DataRow object is being displayed.
Something like this:

Any solutions?
This answer assumes that you have correctly created the
DataTablethat theDataGridis going to present.The following example shows how to achieve a fully dynamic
DataGridusing a sharedDataGridTemplateColumndefinition. To add more columns, simply create a newDataTableas data source.A higher degree of generalization is possible but requires a more complex logic.
The key is to enable auto column generation (enabled by default) and intercept the generated columns and replace them (if required). Then we bind the template internals to the parent
DataGridCell(the row item container) and finally extract the current cell's value with the help of aIValueConverter. TheDataGridCellis the object of choice because it provides full access to the column's context: the cell container (DataGridCell), the data context (DataRowView), the column (DataGridTemplateColumn) and via the column we get the column's index (DataGridColumn.DisplayIndex).MainWindow.xaml
MainWindow.xaml.cs
DataGridCellToCellValueConverter.cs