82

I want to know how should we add columns and rows programmatically to a DataGrid in WPF. The way we used to do it in windows forms. create table columns and rows, and bind it to DataGrid.

I have No. of rows and columns which I need to draw in DataGrid so that user can edit the data in the cells.

ASh
  • 34,632
  • 9
  • 60
  • 82
Andy
  • 833
  • 1
  • 8
  • 5

8 Answers8

80

To programatically add a row:

DataGrid.Items.Add(new DataItem());

To programatically add a column:

DataGridTextColumn textColumn = new DataGridTextColumn(); 
textColumn.Header = "First Name"; 
textColumn.Binding = new Binding("FirstName"); 
dataGrid.Columns.Add(textColumn); 

Check out this post on the WPF DataGrid discussion board for more information.

Josiah Keller
  • 3,635
  • 3
  • 23
  • 35
John Myczek
  • 12,076
  • 4
  • 31
  • 44
  • 9
    -1: because you show the painfully easy part, but not how to actually bind data to those runtime added columns. I'm sure that Andy wants to build an application that displays some sort of data, not just column names. – JohnB Jan 19 '11 at 05:10
  • It is just a matter of setting properties on the DataGridTextColumn. I updated the answer. – John Myczek Jan 19 '11 at 14:19
  • +1: I confirmed that works, thanks. I also discovered that when you add the square brackets, i.e. `Binding("[FirstName]")` *see my previous answer below*, that it works, but it's significantly slower when binding large datasets. – JohnB Jan 19 '11 at 15:32
  • 1
    As `DataItem` is a object, how do it has to look like? – C4d May 20 '16 at 14:22
  • Is there any way to add multi column header using this code? – Denis Aug 10 '17 at 18:51
31

try this , it works 100 % : add columns and rows programatically : you need to create item class at first :

public class Item
{
    public int Num { get; set; }
    public string Start { get; set; }
    public string Finich { get; set; }
}

private void generate_columns()
{
    DataGridTextColumn c1 = new DataGridTextColumn();
    c1.Header = "Num";
    c1.Binding = new Binding("Num");
    c1.Width = 110;
    dataGrid1.Columns.Add(c1);
    DataGridTextColumn c2 = new DataGridTextColumn();
    c2.Header = "Start";
    c2.Width = 110;
    c2.Binding = new Binding("Start");
    dataGrid1.Columns.Add(c2);
    DataGridTextColumn c3 = new DataGridTextColumn();
    c3.Header = "Finich";
    c3.Width = 110;
    c3.Binding = new Binding("Finich");
    dataGrid1.Columns.Add(c3);

    dataGrid1.Items.Add(new Item() { Num = 1, Start = "2012, 8, 15", Finich = "2012, 9, 15" });
    dataGrid1.Items.Add(new Item() { Num = 2, Start = "2012, 12, 15", Finich = "2013, 2, 1" });
    dataGrid1.Items.Add(new Item() { Num = 3, Start = "2012, 8, 1", Finich = "2012, 11, 15" });
}
ASh
  • 34,632
  • 9
  • 60
  • 82
aminescm
  • 311
  • 3
  • 2
30

I had the same problem. Adding new rows to WPF DataGrid requires a trick. DataGrid relies on property fields of an item object. ExpandoObject enables to add new properties dynamically. The code below explains how to do it:

// using System.Dynamic;

DataGrid dataGrid;

string[] labels = new string[] { "Column 0", "Column 1", "Column 2" };

foreach (string label in labels)
{
    DataGridTextColumn column = new DataGridTextColumn();
    column.Header = label;
    column.Binding = new Binding(label.Replace(' ', '_'));

    dataGrid.Columns.Add(column);
}

int[] values = new int[] { 0, 1, 2 };

dynamic row = new ExpandoObject();

for (int i = 0; i < labels.Length; i++)
    ((IDictionary<String, Object>)row)[labels[i].Replace(' ', '_')] = values[i];

dataGrid.Items.Add(row);

//edit:

Note that this is not the way how the component should be used, however, it simplifies a lot if you have only programmatically generated data (eg. in my case: a sequence of features and neural network output).

13

I found a solution that adds columns at runtime, and binds to a DataTable.

Unfortunately, with 47 columns defined this way, it doesn't bind to the data fast enough for me. Any suggestions?

xaml

<DataGrid
  Name="dataGrid"
  AutoGenerateColumns="False"
  ItemsSource="{Binding}">
</DataGrid>

xaml.cs using System.Windows.Data;

if (table != null) // table is a DataTable
{
  foreach (DataColumn col in table.Columns)
  {
    dataGrid.Columns.Add(
      new DataGridTextColumn
      {
        Header = col.ColumnName,
        Binding = new Binding(string.Format("[{0}]", col.ColumnName))
      });
  }

  dataGrid.DataContext = table;
}
Community
  • 1
  • 1
JohnB
  • 18,046
  • 16
  • 98
  • 110
  • Did you find any solution for this back then?? – 321X Aug 11 '11 at 12:44
  • 2
    Why do you use "[{0}]" in the binding path? It doesn't work for me, but when I just use the name without square and curlies then it works? – smerlung Feb 11 '15 at 13:26
5

edit: sorry, I no longer have the code mentioned below. It was a neat solution, although complex.


I posted a sample project describing how to use PropertyDescriptor and lambda delegates with dynamic ObservableCollection and DynamicObject to populate a grid with strongly-typed column definitions.

Columns can be added/removed at runtime dynamically. If your data is not a object with known type, you could create a data structure that would enable access by any number of columns and specify a PropertyDescriptor for each "column".

For example:

IList<string> ColumnNames { get; set; }
//dict.key is column name, dict.value is value
Dictionary<string, string> Rows { get; set; }

You can define columns this way:

var descriptors= new List<PropertyDescriptor>();
//retrieve column name from preprepared list or retrieve from one of the items in dictionary
foreach(var columnName in ColumnNames)
    descriptors.Add(new DynamicPropertyDescriptor<Dictionary, string>(ColumnName, x => x[columnName]))
MyItemsCollection = new DynamicDataGridSource(Rows, descriptors) 

Or even better, in case of some real objects

public class User 
{
    public string FirstName { get; set; }
    public string LastName{ get; set; }
    ...
}

You can specify columns strongly typed (related to your data model):

var propertyDescriptors = new List<PropertyDescriptor>
{
    new DynamicPropertyDescriptor<User, string>("First name", x => x.FirstName ),
    new DynamicPropertyDescriptor<User, string>("Last name", x => x.LastName ),
    ...
}

var users = retrieve some users

Users = new DynamicDataGridSource<User>(users, propertyDescriptors, PropertyChangedListeningMode.Handler);

Then you just bind to Users collections and columns are autogenerated as you speficy them. Strings passed to property descriptors are names for column headers. At runtime you can add more PropertyDescriptors to 'Users' add another column to the grid.

doblak
  • 3,036
  • 1
  • 27
  • 22
  • @doblak I find this is exactly what I need. Do you still have the code of your solution? – zhangz Jul 22 '16 at 11:20
  • @zhangz updated the answer, see this post for DynamicPropertyDescriptor idea: https://jopinblog.wordpress.com/2007/05/12/dynamic-propertydescriptors-with-anonymous-methods/ – doblak Aug 03 '16 at 15:39
3

If you already have the databinding in place John Myczek answer is complete.
If not you have at least 2 options I know of if you want to specify the source of your data. (However I am not sure whether or not this is in line with most guidelines, like MVVM)

option 1: like JohnB said. But I think you should use your own defined collection instead of a weakly typed DataTable (no offense, but you can't tell from the code what each column represents)

xaml.cs

DataContext = myCollection;

//myCollection is a `ICollection<YourType>` preferably
`ObservableCollection<YourType>

 - option 2) Declare the name of the Datagrid in xaml

        <WpfToolkit:DataGrid Name=dataGrid}>

in xaml.cs

CollectionView myCollectionView = 
      (CollectionView)CollectionViewSource.GetDefaultView(yourCollection);
dataGrid.ItemsSource = myCollectionView;

If your type has a property FirstName defined, you can then do what John Myczek pointed out.

DataGridTextColumn textColumn = new DataGridTextColumn(); 
dataColumn.Header = "First Name"; 
dataColumn.Binding = new Binding("FirstName"); 
dataGrid.Columns.Add(textColumn); 

This obviously doesn't work if you don't know properties you will need to show in your dataGrid, but if that is the case you will have more problems to deal with, and I believe that's out of scope here.

akjoshi
  • 15,374
  • 13
  • 103
  • 121
HCP
  • 1,012
  • 2
  • 11
  • 18
0

If you already have the databinding in place John Myczek answer is complete. If not you have at least 2 options I know of if you want to specify the source of your data. (However I am not sure whether or not this is in line with most guidelines, like MVVM)

Then you just bind to Users collections and columns are autogenerated as you speficy them. Strings passed to property descriptors are names for column headers. At runtime you can add more PropertyDescriptors to 'Users' add another column to the grid.

Senar
  • 11
0

To Bind the DataTable into the DataGridTextColumn in CodeBehind xaml

<DataGrid
  Name="TrkDataGrid"
  AutoGenerateColumns="False"
  ItemsSource="{Binding}">
</DataGrid>

xaml.cs

  foreach (DataColumn col in dt.Columns)
  {
    TrkDataGrid.Columns.Add(
      new DataGridTextColumn
      {
        Header = col.ColumnName,
        Binding = new Binding(string.Format("[{0}]", col.ColumnName))
      });
  }

  TrkDataGrid.ItemsSource= dt.DefaultView;