Search code examples
c#wpfxamldatatabledatagrid

C# WPF - How can I dynamically make a DataGrid with dynamic columns (the existence of each column depends on the user input)


Here's the kind of table I am trying to make on xaml:

The user input will always be a string of char, the table will always have 2 rows, the data will always be double. The number of columns changes depending on the length of the string that the user inputs. The user won't be able to edit the table through the interface.

I'm new to WPF and C#, and I hit a very sizeable roadblock here, all help is appreciated


Solution

  • If you need dynamic columns, you should always use a DataTable as data source. This way you can avoid to build the DataGrid explicitly in C#/code-behind and instead use the auto-generation feature.

    The two helper methods AddColumn and AddRow used in the example are candidates to be implemented as extension methods for the DataTable.

    MainWindow.xaml.cs

    private void OnSendButtonClick(object sender, EventArgs e)
    {
      var dataGridSource = new DataTable();
    
      AddColumn<string>(dataGridSource, "type");
      foreach (char character in this.TextInput.Text)
      {
        AddColumn<double>(dataGridSource, character.ToString());
      }
    
      // Generate 2 rows of pseudo data
      var doubleGenerator = new Random();
      for (int rowIndex = 0; rowIndex < 2; rowIndex++)
      {
        var columnValues = new List<object>();
        columnValues.Add(rowIndex < 1 ? "x" : "y");
        for (int columnIndex = 0; columnIndex < dataGridSource.Columns.Count; columnIndex++)
        {
          columnValues.Add(doubleGenerator.NextDouble());
        }
        AddRow(dataGridSource, columnValues);
      }
    
      this.Table.ItemsSource = dataGridSource.DefaultView;
    }
    
    private void AddColumn<TData>(DataTable dataTable, string columnName, int columnIndex = -1)
    {
      DataColumn newColumn = new DataColumn(columnName, typeof(TData));
    
      dataTable.Columns.Add(newColumn);
      if (columnIndex > -1)
      {
        newColumn.SetOrdinal(columnIndex);
      }
    
      int newColumnIndex = dataTable.Columns.IndexOf(newColumn);
    
      // Initialize existing rows with default value for the new column
      foreach (DataRow row in dataTable.Rows)
      {
        row[newColumnIndex] = default(TData);
      }
    }
    
    private void AddRow(DataTable dataTable, IList<object> columnValues)
    {
      DataRow rowModelWithCurrentColumns = dataTable.NewRow();
      dataTable.Rows.Add(rowModelWithCurrentColumns);
    
      for (int columnIndex = 0; columnIndex < dataTable.Columns.Count; columnIndex++)
      {
        rowModelWithCurrentColumns[columnIndex] = columnValues[columnIndex];
      }
    }
    

    MainWindow.xaml

    <Window>
      <StackPanel>
        <TextBox x:Name="TextInput" />
        <Button Content="Send"
                Click="OnSendButtonClick" />
        <DataGrid x:Name="Table" />
      </StackPanel>
    </Window>