How to let a wpf datagrid generate columns dynamically based on the content of a list?












1














I have a collection of objects and each object contains a property of the type collection. My goal is to generate dynamically the datagrid columns based on the collection content and generate also the columns for the remaining properties, which are base types. It is important that a bool is displayed as a CheckBox.



My problem is: The resulting cell content of the dynamically generated columns would be an object (Trait in my object structure), and I want one of this objects properties to be displayed (Trait.Value). When I change content of a cell, the object behind should update.




  • I thought of a DataTable, but when I add a row I need the column key and the value. When I set the value to a custom object, I couldn't see any possibility to display and edit a single property of the custom object.

  • Second approach would be using dynamic objects, like in the following article:
    Auto-Generating DataGrid Columns From DynamicObjects
    , but I see the same proplem like DataTable
    Additional Information:

  • I am using mvvm (when it's necessary I would break this pattern)

  • the datagrid should be editable


My object structure:



public class Model
{
//ItemsSource
public ObservableCollection<Person> Persons { get; set; }
}

public class Person
{
public string Name { get; set; }

//Generate Treats.Count columns
public ObservableCollection<Treat> Treats { get; set; }
}

public class Treat
{
//column header name
public string Name { get; set; }

//value that should be displayed
public string Value { get; set; }
}


My ViewModel.cs with sample data:



public class ViewModel
{
public Model Model { get; set; }

public ViewModel()
{
#region Sample Data
Model = new Model()
{
Persons = new ObservableCollection<Person>()
{
new Person()
{
Name = "Peter",
Treats = new ObservableCollection<Treat>()
{
new Treat()
{
Name = "Look1",
Value = "Nice"
},
new Treat()
{
Name = "Look2",
Value = "Super Nice"
}
}
},
new Person()
{
Name = "Manuel",
Treats = new ObservableCollection<Treat>()
{
new Treat()
{
Name = "Look1",
Value = "Bad"
},
new Treat()
{
Name = "Look2",
Value = "Super Bad"
}
}
}
}
};
#endregion
}
}


Information to class Model.cs:




  • property Persons is the binding collection, which should be used as the ItemsSource

  • the columns of the datagrid should be generated based on the object Person. One column for the Name and n columns for the collection Treats.


The result based on my sample data is something like this:
Resulting DataGrid










share|improve this question






















  • It's is not a duplicate. The marked question was about adding a CheckBoxColumn to a DataGridView in WinForms. Additionally the columns are known before the start and will be generated based on the properties. I have to generate columns out of a collection, which I get at runtime.
    – D.Weder
    Nov 23 '18 at 8:54












  • The number of columns are still unknown. In your marked question the number of columns are fixed, three properties results in three columns. I have two properties, but second one is a collection. Out of the collection I want to generate the additional columns. A collection with 4 entries would result in a DataGrid with 5 columns. (one column for the first property and 4 columns for my collection entires.
    – D.Weder
    Nov 23 '18 at 9:08










  • Hello, would using a datatemplate in your datagrid be useful in your case?
    – Dark Templar
    Nov 23 '18 at 9:37










  • Maybe. How would the template look like? I can't imagine.
    – D.Weder
    Nov 23 '18 at 9:54










  • How does your current xaml look like?
    – Daniel W.
    Nov 23 '18 at 10:37
















1














I have a collection of objects and each object contains a property of the type collection. My goal is to generate dynamically the datagrid columns based on the collection content and generate also the columns for the remaining properties, which are base types. It is important that a bool is displayed as a CheckBox.



My problem is: The resulting cell content of the dynamically generated columns would be an object (Trait in my object structure), and I want one of this objects properties to be displayed (Trait.Value). When I change content of a cell, the object behind should update.




  • I thought of a DataTable, but when I add a row I need the column key and the value. When I set the value to a custom object, I couldn't see any possibility to display and edit a single property of the custom object.

  • Second approach would be using dynamic objects, like in the following article:
    Auto-Generating DataGrid Columns From DynamicObjects
    , but I see the same proplem like DataTable
    Additional Information:

  • I am using mvvm (when it's necessary I would break this pattern)

  • the datagrid should be editable


My object structure:



public class Model
{
//ItemsSource
public ObservableCollection<Person> Persons { get; set; }
}

public class Person
{
public string Name { get; set; }

//Generate Treats.Count columns
public ObservableCollection<Treat> Treats { get; set; }
}

public class Treat
{
//column header name
public string Name { get; set; }

//value that should be displayed
public string Value { get; set; }
}


My ViewModel.cs with sample data:



public class ViewModel
{
public Model Model { get; set; }

public ViewModel()
{
#region Sample Data
Model = new Model()
{
Persons = new ObservableCollection<Person>()
{
new Person()
{
Name = "Peter",
Treats = new ObservableCollection<Treat>()
{
new Treat()
{
Name = "Look1",
Value = "Nice"
},
new Treat()
{
Name = "Look2",
Value = "Super Nice"
}
}
},
new Person()
{
Name = "Manuel",
Treats = new ObservableCollection<Treat>()
{
new Treat()
{
Name = "Look1",
Value = "Bad"
},
new Treat()
{
Name = "Look2",
Value = "Super Bad"
}
}
}
}
};
#endregion
}
}


Information to class Model.cs:




  • property Persons is the binding collection, which should be used as the ItemsSource

  • the columns of the datagrid should be generated based on the object Person. One column for the Name and n columns for the collection Treats.


The result based on my sample data is something like this:
Resulting DataGrid










share|improve this question






















  • It's is not a duplicate. The marked question was about adding a CheckBoxColumn to a DataGridView in WinForms. Additionally the columns are known before the start and will be generated based on the properties. I have to generate columns out of a collection, which I get at runtime.
    – D.Weder
    Nov 23 '18 at 8:54












  • The number of columns are still unknown. In your marked question the number of columns are fixed, three properties results in three columns. I have two properties, but second one is a collection. Out of the collection I want to generate the additional columns. A collection with 4 entries would result in a DataGrid with 5 columns. (one column for the first property and 4 columns for my collection entires.
    – D.Weder
    Nov 23 '18 at 9:08










  • Hello, would using a datatemplate in your datagrid be useful in your case?
    – Dark Templar
    Nov 23 '18 at 9:37










  • Maybe. How would the template look like? I can't imagine.
    – D.Weder
    Nov 23 '18 at 9:54










  • How does your current xaml look like?
    – Daniel W.
    Nov 23 '18 at 10:37














1












1








1







I have a collection of objects and each object contains a property of the type collection. My goal is to generate dynamically the datagrid columns based on the collection content and generate also the columns for the remaining properties, which are base types. It is important that a bool is displayed as a CheckBox.



My problem is: The resulting cell content of the dynamically generated columns would be an object (Trait in my object structure), and I want one of this objects properties to be displayed (Trait.Value). When I change content of a cell, the object behind should update.




  • I thought of a DataTable, but when I add a row I need the column key and the value. When I set the value to a custom object, I couldn't see any possibility to display and edit a single property of the custom object.

  • Second approach would be using dynamic objects, like in the following article:
    Auto-Generating DataGrid Columns From DynamicObjects
    , but I see the same proplem like DataTable
    Additional Information:

  • I am using mvvm (when it's necessary I would break this pattern)

  • the datagrid should be editable


My object structure:



public class Model
{
//ItemsSource
public ObservableCollection<Person> Persons { get; set; }
}

public class Person
{
public string Name { get; set; }

//Generate Treats.Count columns
public ObservableCollection<Treat> Treats { get; set; }
}

public class Treat
{
//column header name
public string Name { get; set; }

//value that should be displayed
public string Value { get; set; }
}


My ViewModel.cs with sample data:



public class ViewModel
{
public Model Model { get; set; }

public ViewModel()
{
#region Sample Data
Model = new Model()
{
Persons = new ObservableCollection<Person>()
{
new Person()
{
Name = "Peter",
Treats = new ObservableCollection<Treat>()
{
new Treat()
{
Name = "Look1",
Value = "Nice"
},
new Treat()
{
Name = "Look2",
Value = "Super Nice"
}
}
},
new Person()
{
Name = "Manuel",
Treats = new ObservableCollection<Treat>()
{
new Treat()
{
Name = "Look1",
Value = "Bad"
},
new Treat()
{
Name = "Look2",
Value = "Super Bad"
}
}
}
}
};
#endregion
}
}


Information to class Model.cs:




  • property Persons is the binding collection, which should be used as the ItemsSource

  • the columns of the datagrid should be generated based on the object Person. One column for the Name and n columns for the collection Treats.


The result based on my sample data is something like this:
Resulting DataGrid










share|improve this question













I have a collection of objects and each object contains a property of the type collection. My goal is to generate dynamically the datagrid columns based on the collection content and generate also the columns for the remaining properties, which are base types. It is important that a bool is displayed as a CheckBox.



My problem is: The resulting cell content of the dynamically generated columns would be an object (Trait in my object structure), and I want one of this objects properties to be displayed (Trait.Value). When I change content of a cell, the object behind should update.




  • I thought of a DataTable, but when I add a row I need the column key and the value. When I set the value to a custom object, I couldn't see any possibility to display and edit a single property of the custom object.

  • Second approach would be using dynamic objects, like in the following article:
    Auto-Generating DataGrid Columns From DynamicObjects
    , but I see the same proplem like DataTable
    Additional Information:

  • I am using mvvm (when it's necessary I would break this pattern)

  • the datagrid should be editable


My object structure:



public class Model
{
//ItemsSource
public ObservableCollection<Person> Persons { get; set; }
}

public class Person
{
public string Name { get; set; }

//Generate Treats.Count columns
public ObservableCollection<Treat> Treats { get; set; }
}

public class Treat
{
//column header name
public string Name { get; set; }

//value that should be displayed
public string Value { get; set; }
}


My ViewModel.cs with sample data:



public class ViewModel
{
public Model Model { get; set; }

public ViewModel()
{
#region Sample Data
Model = new Model()
{
Persons = new ObservableCollection<Person>()
{
new Person()
{
Name = "Peter",
Treats = new ObservableCollection<Treat>()
{
new Treat()
{
Name = "Look1",
Value = "Nice"
},
new Treat()
{
Name = "Look2",
Value = "Super Nice"
}
}
},
new Person()
{
Name = "Manuel",
Treats = new ObservableCollection<Treat>()
{
new Treat()
{
Name = "Look1",
Value = "Bad"
},
new Treat()
{
Name = "Look2",
Value = "Super Bad"
}
}
}
}
};
#endregion
}
}


Information to class Model.cs:




  • property Persons is the binding collection, which should be used as the ItemsSource

  • the columns of the datagrid should be generated based on the object Person. One column for the Name and n columns for the collection Treats.


The result based on my sample data is something like this:
Resulting DataGrid







c# wpf datagrid wpfdatagrid






share|improve this question













share|improve this question











share|improve this question




share|improve this question










asked Nov 23 '18 at 8:29









D.WederD.Weder

573




573












  • It's is not a duplicate. The marked question was about adding a CheckBoxColumn to a DataGridView in WinForms. Additionally the columns are known before the start and will be generated based on the properties. I have to generate columns out of a collection, which I get at runtime.
    – D.Weder
    Nov 23 '18 at 8:54












  • The number of columns are still unknown. In your marked question the number of columns are fixed, three properties results in three columns. I have two properties, but second one is a collection. Out of the collection I want to generate the additional columns. A collection with 4 entries would result in a DataGrid with 5 columns. (one column for the first property and 4 columns for my collection entires.
    – D.Weder
    Nov 23 '18 at 9:08










  • Hello, would using a datatemplate in your datagrid be useful in your case?
    – Dark Templar
    Nov 23 '18 at 9:37










  • Maybe. How would the template look like? I can't imagine.
    – D.Weder
    Nov 23 '18 at 9:54










  • How does your current xaml look like?
    – Daniel W.
    Nov 23 '18 at 10:37


















  • It's is not a duplicate. The marked question was about adding a CheckBoxColumn to a DataGridView in WinForms. Additionally the columns are known before the start and will be generated based on the properties. I have to generate columns out of a collection, which I get at runtime.
    – D.Weder
    Nov 23 '18 at 8:54












  • The number of columns are still unknown. In your marked question the number of columns are fixed, three properties results in three columns. I have two properties, but second one is a collection. Out of the collection I want to generate the additional columns. A collection with 4 entries would result in a DataGrid with 5 columns. (one column for the first property and 4 columns for my collection entires.
    – D.Weder
    Nov 23 '18 at 9:08










  • Hello, would using a datatemplate in your datagrid be useful in your case?
    – Dark Templar
    Nov 23 '18 at 9:37










  • Maybe. How would the template look like? I can't imagine.
    – D.Weder
    Nov 23 '18 at 9:54










  • How does your current xaml look like?
    – Daniel W.
    Nov 23 '18 at 10:37
















It's is not a duplicate. The marked question was about adding a CheckBoxColumn to a DataGridView in WinForms. Additionally the columns are known before the start and will be generated based on the properties. I have to generate columns out of a collection, which I get at runtime.
– D.Weder
Nov 23 '18 at 8:54






It's is not a duplicate. The marked question was about adding a CheckBoxColumn to a DataGridView in WinForms. Additionally the columns are known before the start and will be generated based on the properties. I have to generate columns out of a collection, which I get at runtime.
– D.Weder
Nov 23 '18 at 8:54














The number of columns are still unknown. In your marked question the number of columns are fixed, three properties results in three columns. I have two properties, but second one is a collection. Out of the collection I want to generate the additional columns. A collection with 4 entries would result in a DataGrid with 5 columns. (one column for the first property and 4 columns for my collection entires.
– D.Weder
Nov 23 '18 at 9:08




The number of columns are still unknown. In your marked question the number of columns are fixed, three properties results in three columns. I have two properties, but second one is a collection. Out of the collection I want to generate the additional columns. A collection with 4 entries would result in a DataGrid with 5 columns. (one column for the first property and 4 columns for my collection entires.
– D.Weder
Nov 23 '18 at 9:08












Hello, would using a datatemplate in your datagrid be useful in your case?
– Dark Templar
Nov 23 '18 at 9:37




Hello, would using a datatemplate in your datagrid be useful in your case?
– Dark Templar
Nov 23 '18 at 9:37












Maybe. How would the template look like? I can't imagine.
– D.Weder
Nov 23 '18 at 9:54




Maybe. How would the template look like? I can't imagine.
– D.Weder
Nov 23 '18 at 9:54












How does your current xaml look like?
– Daniel W.
Nov 23 '18 at 10:37




How does your current xaml look like?
– Daniel W.
Nov 23 '18 at 10:37












1 Answer
1






active

oldest

votes


















1














Since, you have said you are okay to break MVVM pattern, please try the below approach.



OVERVIEW:




  1. Create a IvalueConverter to convert your itemsource to list of expandoobejcts


  2. In Code behind of the DataGrid (Loaded Event or SourceChanged event), add a code to generate columns manually



CODES:



Create Converter: PART 1 First we need to get the List of all possible columsn that might pop in (since we don't know the collections yet)



        ObservableCollection<Person> inputlist = (ObservableCollection<Person>)value;
List<string> PossibleColumnList = new List<string>();
PossibleColumnList.Add(nameof(Person.Name)); //since we need name header first.
List<string> TempColumnList = new List<string>();
foreach (Person P in inputlist)
{
foreach(Treat T in P.Treats)
{
if (TempColumnList.Contains(T.Name) == false) TempColumnList.Add(T.Name);
}
}
TempColumnList.Sort();
PossibleColumnList.AddRange(TempColumnList); //This way we get Name first and rest of the columns in sorted out manner


Create Converter: PART 2. Now create an IDictionary Object with all available colum headers



 IDictionary<string, object> ColumnHeaderDictionary = new Dictionary<string, object>(); 
foreach (string columnheader in PossibleColumnList)
{
if (ColumnHeaderDictionary.ContainsKey(columnheader) == false) ColumnHeaderDictionary.Add(columnheader, new object());
}


Create Converter: PART 3 Now iterate through all persons and create a IDictionary for each person model. Convert idictionary to expando object and store in final list



List<ExpandoObject> FinalList = new List<ExpandoObject>();

foreach (Person p in inputlist)
{
ExpandoObject tempExpando = new ExpandoObject();
IDictionary<string, object> TempDictionary = tempExpando as IDictionary<string, object>;
foreach (var kvp in ColumnHeaderDictionary)
{
TempDictionary.Add(kvp);
}
TempDictionary[nameof(Person.Name)] = p.Name;
foreach(Treat t in p.Treats)
{
TempDictionary[t.Name] = t.Value;
}

FinalList.Add(tempExpando);
}
return FinalList;


XAML CODE:



 <Window.DataContext>
<local:ViewModel/>
</Window.DataContext>
<Grid x:Name="grdMain" DataContext="{Binding}">
<DataGrid x:Name="dgMain" ItemsSource="{Binding ElementName=grdMain,Path=DataContext.Model.Persons,Converter={StaticResource NewConverter}}" Loaded="dgMain_Loaded" />
</Grid>


CODE BEHIND: TO MANUALLY CREATE COLUMNS



private void dgMain_Loaded(object sender, RoutedEventArgs e)
{
DataGrid workinggrid = sender as DataGrid;
ExpandoObject SingleExpando = (workinggrid.ItemsSource as List<ExpandoObject>).FirstOrDefault();
if (workinggrid == null) workinggrid = new DataGrid();


List<string> ColumHeaders = (SingleExpando as IDictionary<string, object>).ToList().Select(p => p.Key).ToList();

foreach (string ColumnName in ColumHeaders)
{
var newcolumn = new DataGridTextColumn() { Header = ColumnName, Binding = new Binding(ColumnName) };
workinggrid.Columns.Add(newcolumn);
}
}


FINAL OUTPUT:
enter image description here






share|improve this answer





















    Your Answer






    StackExchange.ifUsing("editor", function () {
    StackExchange.using("externalEditor", function () {
    StackExchange.using("snippets", function () {
    StackExchange.snippets.init();
    });
    });
    }, "code-snippets");

    StackExchange.ready(function() {
    var channelOptions = {
    tags: "".split(" "),
    id: "1"
    };
    initTagRenderer("".split(" "), "".split(" "), channelOptions);

    StackExchange.using("externalEditor", function() {
    // Have to fire editor after snippets, if snippets enabled
    if (StackExchange.settings.snippets.snippetsEnabled) {
    StackExchange.using("snippets", function() {
    createEditor();
    });
    }
    else {
    createEditor();
    }
    });

    function createEditor() {
    StackExchange.prepareEditor({
    heartbeatType: 'answer',
    autoActivateHeartbeat: false,
    convertImagesToLinks: true,
    noModals: true,
    showLowRepImageUploadWarning: true,
    reputationToPostImages: 10,
    bindNavPrevention: true,
    postfix: "",
    imageUploader: {
    brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
    contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
    allowUrls: true
    },
    onDemand: true,
    discardSelector: ".discard-answer"
    ,immediatelyShowMarkdownHelp:true
    });


    }
    });














    draft saved

    draft discarded


















    StackExchange.ready(
    function () {
    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53443049%2fhow-to-let-a-wpf-datagrid-generate-columns-dynamically-based-on-the-content-of-a%23new-answer', 'question_page');
    }
    );

    Post as a guest















    Required, but never shown

























    1 Answer
    1






    active

    oldest

    votes








    1 Answer
    1






    active

    oldest

    votes









    active

    oldest

    votes






    active

    oldest

    votes









    1














    Since, you have said you are okay to break MVVM pattern, please try the below approach.



    OVERVIEW:




    1. Create a IvalueConverter to convert your itemsource to list of expandoobejcts


    2. In Code behind of the DataGrid (Loaded Event or SourceChanged event), add a code to generate columns manually



    CODES:



    Create Converter: PART 1 First we need to get the List of all possible columsn that might pop in (since we don't know the collections yet)



            ObservableCollection<Person> inputlist = (ObservableCollection<Person>)value;
    List<string> PossibleColumnList = new List<string>();
    PossibleColumnList.Add(nameof(Person.Name)); //since we need name header first.
    List<string> TempColumnList = new List<string>();
    foreach (Person P in inputlist)
    {
    foreach(Treat T in P.Treats)
    {
    if (TempColumnList.Contains(T.Name) == false) TempColumnList.Add(T.Name);
    }
    }
    TempColumnList.Sort();
    PossibleColumnList.AddRange(TempColumnList); //This way we get Name first and rest of the columns in sorted out manner


    Create Converter: PART 2. Now create an IDictionary Object with all available colum headers



     IDictionary<string, object> ColumnHeaderDictionary = new Dictionary<string, object>(); 
    foreach (string columnheader in PossibleColumnList)
    {
    if (ColumnHeaderDictionary.ContainsKey(columnheader) == false) ColumnHeaderDictionary.Add(columnheader, new object());
    }


    Create Converter: PART 3 Now iterate through all persons and create a IDictionary for each person model. Convert idictionary to expando object and store in final list



    List<ExpandoObject> FinalList = new List<ExpandoObject>();

    foreach (Person p in inputlist)
    {
    ExpandoObject tempExpando = new ExpandoObject();
    IDictionary<string, object> TempDictionary = tempExpando as IDictionary<string, object>;
    foreach (var kvp in ColumnHeaderDictionary)
    {
    TempDictionary.Add(kvp);
    }
    TempDictionary[nameof(Person.Name)] = p.Name;
    foreach(Treat t in p.Treats)
    {
    TempDictionary[t.Name] = t.Value;
    }

    FinalList.Add(tempExpando);
    }
    return FinalList;


    XAML CODE:



     <Window.DataContext>
    <local:ViewModel/>
    </Window.DataContext>
    <Grid x:Name="grdMain" DataContext="{Binding}">
    <DataGrid x:Name="dgMain" ItemsSource="{Binding ElementName=grdMain,Path=DataContext.Model.Persons,Converter={StaticResource NewConverter}}" Loaded="dgMain_Loaded" />
    </Grid>


    CODE BEHIND: TO MANUALLY CREATE COLUMNS



    private void dgMain_Loaded(object sender, RoutedEventArgs e)
    {
    DataGrid workinggrid = sender as DataGrid;
    ExpandoObject SingleExpando = (workinggrid.ItemsSource as List<ExpandoObject>).FirstOrDefault();
    if (workinggrid == null) workinggrid = new DataGrid();


    List<string> ColumHeaders = (SingleExpando as IDictionary<string, object>).ToList().Select(p => p.Key).ToList();

    foreach (string ColumnName in ColumHeaders)
    {
    var newcolumn = new DataGridTextColumn() { Header = ColumnName, Binding = new Binding(ColumnName) };
    workinggrid.Columns.Add(newcolumn);
    }
    }


    FINAL OUTPUT:
    enter image description here






    share|improve this answer


























      1














      Since, you have said you are okay to break MVVM pattern, please try the below approach.



      OVERVIEW:




      1. Create a IvalueConverter to convert your itemsource to list of expandoobejcts


      2. In Code behind of the DataGrid (Loaded Event or SourceChanged event), add a code to generate columns manually



      CODES:



      Create Converter: PART 1 First we need to get the List of all possible columsn that might pop in (since we don't know the collections yet)



              ObservableCollection<Person> inputlist = (ObservableCollection<Person>)value;
      List<string> PossibleColumnList = new List<string>();
      PossibleColumnList.Add(nameof(Person.Name)); //since we need name header first.
      List<string> TempColumnList = new List<string>();
      foreach (Person P in inputlist)
      {
      foreach(Treat T in P.Treats)
      {
      if (TempColumnList.Contains(T.Name) == false) TempColumnList.Add(T.Name);
      }
      }
      TempColumnList.Sort();
      PossibleColumnList.AddRange(TempColumnList); //This way we get Name first and rest of the columns in sorted out manner


      Create Converter: PART 2. Now create an IDictionary Object with all available colum headers



       IDictionary<string, object> ColumnHeaderDictionary = new Dictionary<string, object>(); 
      foreach (string columnheader in PossibleColumnList)
      {
      if (ColumnHeaderDictionary.ContainsKey(columnheader) == false) ColumnHeaderDictionary.Add(columnheader, new object());
      }


      Create Converter: PART 3 Now iterate through all persons and create a IDictionary for each person model. Convert idictionary to expando object and store in final list



      List<ExpandoObject> FinalList = new List<ExpandoObject>();

      foreach (Person p in inputlist)
      {
      ExpandoObject tempExpando = new ExpandoObject();
      IDictionary<string, object> TempDictionary = tempExpando as IDictionary<string, object>;
      foreach (var kvp in ColumnHeaderDictionary)
      {
      TempDictionary.Add(kvp);
      }
      TempDictionary[nameof(Person.Name)] = p.Name;
      foreach(Treat t in p.Treats)
      {
      TempDictionary[t.Name] = t.Value;
      }

      FinalList.Add(tempExpando);
      }
      return FinalList;


      XAML CODE:



       <Window.DataContext>
      <local:ViewModel/>
      </Window.DataContext>
      <Grid x:Name="grdMain" DataContext="{Binding}">
      <DataGrid x:Name="dgMain" ItemsSource="{Binding ElementName=grdMain,Path=DataContext.Model.Persons,Converter={StaticResource NewConverter}}" Loaded="dgMain_Loaded" />
      </Grid>


      CODE BEHIND: TO MANUALLY CREATE COLUMNS



      private void dgMain_Loaded(object sender, RoutedEventArgs e)
      {
      DataGrid workinggrid = sender as DataGrid;
      ExpandoObject SingleExpando = (workinggrid.ItemsSource as List<ExpandoObject>).FirstOrDefault();
      if (workinggrid == null) workinggrid = new DataGrid();


      List<string> ColumHeaders = (SingleExpando as IDictionary<string, object>).ToList().Select(p => p.Key).ToList();

      foreach (string ColumnName in ColumHeaders)
      {
      var newcolumn = new DataGridTextColumn() { Header = ColumnName, Binding = new Binding(ColumnName) };
      workinggrid.Columns.Add(newcolumn);
      }
      }


      FINAL OUTPUT:
      enter image description here






      share|improve this answer
























        1












        1








        1






        Since, you have said you are okay to break MVVM pattern, please try the below approach.



        OVERVIEW:




        1. Create a IvalueConverter to convert your itemsource to list of expandoobejcts


        2. In Code behind of the DataGrid (Loaded Event or SourceChanged event), add a code to generate columns manually



        CODES:



        Create Converter: PART 1 First we need to get the List of all possible columsn that might pop in (since we don't know the collections yet)



                ObservableCollection<Person> inputlist = (ObservableCollection<Person>)value;
        List<string> PossibleColumnList = new List<string>();
        PossibleColumnList.Add(nameof(Person.Name)); //since we need name header first.
        List<string> TempColumnList = new List<string>();
        foreach (Person P in inputlist)
        {
        foreach(Treat T in P.Treats)
        {
        if (TempColumnList.Contains(T.Name) == false) TempColumnList.Add(T.Name);
        }
        }
        TempColumnList.Sort();
        PossibleColumnList.AddRange(TempColumnList); //This way we get Name first and rest of the columns in sorted out manner


        Create Converter: PART 2. Now create an IDictionary Object with all available colum headers



         IDictionary<string, object> ColumnHeaderDictionary = new Dictionary<string, object>(); 
        foreach (string columnheader in PossibleColumnList)
        {
        if (ColumnHeaderDictionary.ContainsKey(columnheader) == false) ColumnHeaderDictionary.Add(columnheader, new object());
        }


        Create Converter: PART 3 Now iterate through all persons and create a IDictionary for each person model. Convert idictionary to expando object and store in final list



        List<ExpandoObject> FinalList = new List<ExpandoObject>();

        foreach (Person p in inputlist)
        {
        ExpandoObject tempExpando = new ExpandoObject();
        IDictionary<string, object> TempDictionary = tempExpando as IDictionary<string, object>;
        foreach (var kvp in ColumnHeaderDictionary)
        {
        TempDictionary.Add(kvp);
        }
        TempDictionary[nameof(Person.Name)] = p.Name;
        foreach(Treat t in p.Treats)
        {
        TempDictionary[t.Name] = t.Value;
        }

        FinalList.Add(tempExpando);
        }
        return FinalList;


        XAML CODE:



         <Window.DataContext>
        <local:ViewModel/>
        </Window.DataContext>
        <Grid x:Name="grdMain" DataContext="{Binding}">
        <DataGrid x:Name="dgMain" ItemsSource="{Binding ElementName=grdMain,Path=DataContext.Model.Persons,Converter={StaticResource NewConverter}}" Loaded="dgMain_Loaded" />
        </Grid>


        CODE BEHIND: TO MANUALLY CREATE COLUMNS



        private void dgMain_Loaded(object sender, RoutedEventArgs e)
        {
        DataGrid workinggrid = sender as DataGrid;
        ExpandoObject SingleExpando = (workinggrid.ItemsSource as List<ExpandoObject>).FirstOrDefault();
        if (workinggrid == null) workinggrid = new DataGrid();


        List<string> ColumHeaders = (SingleExpando as IDictionary<string, object>).ToList().Select(p => p.Key).ToList();

        foreach (string ColumnName in ColumHeaders)
        {
        var newcolumn = new DataGridTextColumn() { Header = ColumnName, Binding = new Binding(ColumnName) };
        workinggrid.Columns.Add(newcolumn);
        }
        }


        FINAL OUTPUT:
        enter image description here






        share|improve this answer












        Since, you have said you are okay to break MVVM pattern, please try the below approach.



        OVERVIEW:




        1. Create a IvalueConverter to convert your itemsource to list of expandoobejcts


        2. In Code behind of the DataGrid (Loaded Event or SourceChanged event), add a code to generate columns manually



        CODES:



        Create Converter: PART 1 First we need to get the List of all possible columsn that might pop in (since we don't know the collections yet)



                ObservableCollection<Person> inputlist = (ObservableCollection<Person>)value;
        List<string> PossibleColumnList = new List<string>();
        PossibleColumnList.Add(nameof(Person.Name)); //since we need name header first.
        List<string> TempColumnList = new List<string>();
        foreach (Person P in inputlist)
        {
        foreach(Treat T in P.Treats)
        {
        if (TempColumnList.Contains(T.Name) == false) TempColumnList.Add(T.Name);
        }
        }
        TempColumnList.Sort();
        PossibleColumnList.AddRange(TempColumnList); //This way we get Name first and rest of the columns in sorted out manner


        Create Converter: PART 2. Now create an IDictionary Object with all available colum headers



         IDictionary<string, object> ColumnHeaderDictionary = new Dictionary<string, object>(); 
        foreach (string columnheader in PossibleColumnList)
        {
        if (ColumnHeaderDictionary.ContainsKey(columnheader) == false) ColumnHeaderDictionary.Add(columnheader, new object());
        }


        Create Converter: PART 3 Now iterate through all persons and create a IDictionary for each person model. Convert idictionary to expando object and store in final list



        List<ExpandoObject> FinalList = new List<ExpandoObject>();

        foreach (Person p in inputlist)
        {
        ExpandoObject tempExpando = new ExpandoObject();
        IDictionary<string, object> TempDictionary = tempExpando as IDictionary<string, object>;
        foreach (var kvp in ColumnHeaderDictionary)
        {
        TempDictionary.Add(kvp);
        }
        TempDictionary[nameof(Person.Name)] = p.Name;
        foreach(Treat t in p.Treats)
        {
        TempDictionary[t.Name] = t.Value;
        }

        FinalList.Add(tempExpando);
        }
        return FinalList;


        XAML CODE:



         <Window.DataContext>
        <local:ViewModel/>
        </Window.DataContext>
        <Grid x:Name="grdMain" DataContext="{Binding}">
        <DataGrid x:Name="dgMain" ItemsSource="{Binding ElementName=grdMain,Path=DataContext.Model.Persons,Converter={StaticResource NewConverter}}" Loaded="dgMain_Loaded" />
        </Grid>


        CODE BEHIND: TO MANUALLY CREATE COLUMNS



        private void dgMain_Loaded(object sender, RoutedEventArgs e)
        {
        DataGrid workinggrid = sender as DataGrid;
        ExpandoObject SingleExpando = (workinggrid.ItemsSource as List<ExpandoObject>).FirstOrDefault();
        if (workinggrid == null) workinggrid = new DataGrid();


        List<string> ColumHeaders = (SingleExpando as IDictionary<string, object>).ToList().Select(p => p.Key).ToList();

        foreach (string ColumnName in ColumHeaders)
        {
        var newcolumn = new DataGridTextColumn() { Header = ColumnName, Binding = new Binding(ColumnName) };
        workinggrid.Columns.Add(newcolumn);
        }
        }


        FINAL OUTPUT:
        enter image description here







        share|improve this answer












        share|improve this answer



        share|improve this answer










        answered Nov 23 '18 at 23:39









        Senguttuvan MahalingamSenguttuvan Mahalingam

        764




        764






























            draft saved

            draft discarded




















































            Thanks for contributing an answer to Stack Overflow!


            • Please be sure to answer the question. Provide details and share your research!

            But avoid



            • Asking for help, clarification, or responding to other answers.

            • Making statements based on opinion; back them up with references or personal experience.


            To learn more, see our tips on writing great answers.




            draft saved


            draft discarded














            StackExchange.ready(
            function () {
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53443049%2fhow-to-let-a-wpf-datagrid-generate-columns-dynamically-based-on-the-content-of-a%23new-answer', 'question_page');
            }
            );

            Post as a guest















            Required, but never shown





















































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown

































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown







            Popular posts from this blog

            Contact image not getting when fetch all contact list from iPhone by CNContact

            Insert data from modal to MySQL (multiple modal on website)

            count number of partitions of a set with n elements into k subsets