Home > front end >  C# WPF - How can I dynamically make a DataGrid with dynamic columns (the existence of each column de
C# WPF - How can I dynamically make a DataGrid with dynamic columns (the existence of each column de

Time:04-16

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

User input:

ABCDE

Table generated:

type A B C D E
x 1.1 4.6 5.7 8.8 10.1
y 14.5 9.8 8.1 4.3 2.4

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

CodePudding user response:

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>

CodePudding user response:

(Code-behind)

//clear rows & columns
DataGrid.Items.Clear();
DataGrid.Columns.Clear();

//add type Column
DataGridTextColumn typeColumn = new DataGridTextColumn();
typeColumn.Header = "Type";
typeColumn.Binding = new Binding("Type");
DataGrid.Columns.Add(typeColumn);

//Define rows
var xRow = new ExpandoObject() as IDictionary<string, object>;
var yRow = new ExpandoObject() as IDictionary<string, object>;
xRow.Add("Type", "X");
yRow.Add("Type", "Y");

//Get user input
string input = UserInput.Text;

//Add columns
for (int i = 0; i < input.Length; i  )
{
    DataGridTextColumn column = new DataGridTextColumn();
    column.Header = input[I];
    column.Binding = new Binding(input[i].ToString());
    DataGrid.Columns.Add(column);

    //fill data
    xRow.Add(input[i].ToString(), 1.1);
    yRow.Add(input[i].ToString(), 1.1);
}

//Add rows
DataGrid.Items.Add(xRow);
DataGrid.Items.Add(yRow);

The problem with this approach is that the user can't add the same letter/column twice because of the Dictionary but you can Adapt and change the code to fit your needing.

  • Related