Search code examples
wpfgridviewdata-binding

How to display a table with dynamic columns in WPF MVVM


I have a list of object, each object contains user defined attributes. The keys and values for these attributes are free string, so they are not necessarily consistent between objects. Regardless, I would like to display all my objects in a table.

One object per row, one attribute key per column, one attribute value per cell like so :

name age favourite fruit favourite color city
john 21 blue london
jane ananas red paris
paul 22 banana green
patricia 23 ananas istanbul

I have a custom objects responsible for reading the attribute and storing them in a Dictionary<string, string> and my model view stores a list of such dictionaries. I would like to link this list of dictionary to my WPF control.

Later, column would need to be sortable and groupable by attribute value (like excel subtotals)

I don't even know where to start ? Custom dataGridView ? Custom ListView?


Solution

  • A DataTable object is the way to go for dynamic data. You will have to convert it by doing something similar to what is below, but instead iterating through your objects.

    Window xaml.cs

        public partial class DataGridFromDictionary : Window
        {
            public DataTable Table { get; set; }
            public DataGridFromDictionary()
            {
                var attributeDictionaries = new List<Dictionary<string, string>>()
                {
                    new Dictionary<string, string>()
                    {
                        {"name", "john" },
                        {"age", "21" },
                        {"favourite color", "blue" },
                        {"city", "london" },
                    },                
                    new Dictionary<string, string>()
                    {
                        {"name", "jane" },
                        {"favourite fruit", "ananas" },
                        {"favourite color", "red" },
                        {"city", "paris" },
                    },
                };
    
                Table = CovertToDataTable(attributeDictionaries);
    
                InitializeComponent();
                DataContext = this;
            }
        }
    
    

    Dictionary to DataTable converter

    This will allow you to convert your data into something that the DataGrid can consume.

            public DataTable CovertToDataTable(List<Dictionary<string, string>> dynamicObjects)
            {
                DataTable table = new DataTable();
    
                foreach(var attributeDictionary in dynamicObjects)
                {
                    // add any missing columns
                    foreach (var attribute in attributeDictionary.Keys)
                    {
                        if (!table.Columns.Contains(attribute))
                        {
                            table.Columns.Add(attribute);
                        }
                    }
    
                    // create the new row
                    var dr = table.NewRow();
    
                    foreach (var attribute in attributeDictionary)
                    {
                        dr[attribute.Key] = attribute.Value;
                    }
    
                    table.Rows.Add(dr);
                }
    
                return table;
            }
    

    xaml

        <Grid>
            <DataGrid ItemsSource="{Binding Table}" />
        </Grid>
    

    Result:

    enter image description here