How to let a wpf datagrid generate columns dynamically based on the content of a list?
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 theName
and n columns for the collectionTreats
.
The result based on my sample data is something like this:
c# wpf datagrid wpfdatagrid
|
show 1 more comment
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 theName
and n columns for the collectionTreats
.
The result based on my sample data is something like this:
c# wpf datagrid wpfdatagrid
It's is not a duplicate. The marked question was about adding aCheckBoxColumn
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 aDataGrid
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
|
show 1 more comment
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 theName
and n columns for the collectionTreats
.
The result based on my sample data is something like this:
c# wpf datagrid wpfdatagrid
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 theName
and n columns for the collectionTreats
.
The result based on my sample data is something like this:
c# wpf datagrid wpfdatagrid
c# wpf datagrid wpfdatagrid
asked Nov 23 '18 at 8:29
D.WederD.Weder
573
573
It's is not a duplicate. The marked question was about adding aCheckBoxColumn
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 aDataGrid
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
|
show 1 more comment
It's is not a duplicate. The marked question was about adding aCheckBoxColumn
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 aDataGrid
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
|
show 1 more comment
1 Answer
1
active
oldest
votes
Since, you have said you are okay to break MVVM pattern, please try the below approach.
OVERVIEW:
Create a IvalueConverter to convert your itemsource to list of expandoobejcts
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:
add a comment |
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
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
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
Since, you have said you are okay to break MVVM pattern, please try the below approach.
OVERVIEW:
Create a IvalueConverter to convert your itemsource to list of expandoobejcts
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:
add a comment |
Since, you have said you are okay to break MVVM pattern, please try the below approach.
OVERVIEW:
Create a IvalueConverter to convert your itemsource to list of expandoobejcts
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:
add a comment |
Since, you have said you are okay to break MVVM pattern, please try the below approach.
OVERVIEW:
Create a IvalueConverter to convert your itemsource to list of expandoobejcts
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:
Since, you have said you are okay to break MVVM pattern, please try the below approach.
OVERVIEW:
Create a IvalueConverter to convert your itemsource to list of expandoobejcts
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:
answered Nov 23 '18 at 23:39
![](https://i.stack.imgur.com/AFPNG.png?s=32&g=1)
![](https://i.stack.imgur.com/AFPNG.png?s=32&g=1)
Senguttuvan MahalingamSenguttuvan Mahalingam
764
764
add a comment |
add a comment |
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.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
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
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
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
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