Reputation: 7899
I have been trying to wrap my mind around this problem for way too long... I am really out of ideas.
The explanation of what I need to achieve is pretty simple. This is my object, which contains a list:
class MyObject
{
public string property1 { get; set; }
public string property2 { get; set; }
public List<string> list { get; set; }
}
Now, I need the columns of the DataGridView
to look like this:
[property1] | [property2] | [list_element1] | ... | [list_elementN]
The data binding should be as simple as possible (my real object has way more than two non-list properties).
I have even tried Reflection
to create a dynamic object with list_element1, ... , list_elementN
as properties. I kind of works, but there are certain issues with creating new rows of the DataGridView
so I abandoned this path.
Any ideas of what could my best option be?
Upvotes: 0
Views: 380
Reputation: 3306
I think I found a way for you to do it... (and I want to make clear that my code indeed is very error prone but I just want to show that there's a silver lining, so please don't pin me down on this)
Assuming you have your datasource like this:
List<MyObject> dataSource = new List<MyObject>
{
new MyObject
{
ID = 1,
Name = "Row1",
List = new List<string>() {"item1", "item2", "item3"}
},
new MyObject
{
ID = 2,
Name = "Row2",
List = new List<string>() {"item4", "item5", "item6"}
},
new MyObject
{
ID = 3,
Name = "Row3",
List = new List<string>() {"item7", "item8", "item9"}
}
};
You can go for a dynamic datasource created out of your existing MyObject. (I took ExpandoObject so I can add properties to it dynamically)
List<dynamic> dynamicSource = new List<dynamic>();
foreach (var data in dataSource)
{
dynamic o = new ExpandoObject();
// assigning properties we already know
o.ID = data.ID;
o.Name = data.Name;
int idx = 0; // just a counter. you could also do this with a for-loop of course
foreach (var item in data.List)
{
var dict = o as IDictionary<string, Object>; // cast expando object just to get the properties as key and value pairs in a dictionary
dict.Add("ItemNr" + idx, item);
idx++;
}
dynamicSource.Add(o);
}
Now there's a little extension method I found here which becomes very handy at this point
public static DataTable ToDataTable(this IEnumerable<dynamic> items)
{
var data = items.ToArray();
if (data.Count() == 0) return null;
var dt = new DataTable();
foreach (var key in ((IDictionary<string, object>)data[0]).Keys)
{
dt.Columns.Add(key);
}
foreach (var d in data)
{
dt.Rows.Add(((IDictionary<string, object>)d).Values.ToArray());
}
return dt;
}
Now we can add the datasource to our DataGridView like this:
var dataTable = Extensions.ToDataTable(dynamicSource);
dataGridView1.DataSource = dataTable;
It should be assigned correctly now. What I've tested, adding rows shouldn't be a problem now.
If you want to get back your DataSource you have to take following little method:
private List<MyObject> ConvertBackToObject(DataTable dataTable)
{
// basically the same procedure as assigning
List<MyObject> listMyDataSource = new List<MyObject>();
foreach (DataRow row in dataTable.Rows)
{
MyObject myObject = new MyObject
{
ID = Convert.ToInt16(row["ID"]),
Name = row["Name"].ToString(),
List = new List<string>()
};
foreach (var column in dataTable.Columns.Cast<DataColumn>().Where(col => col.ColumnName.StartsWith("ItemNr")))
{
myObject.List.Add(row[column].ToString());
}
listMyDataSource.Add(myObject);
}
return listMyDataSource;
}
which leads us to this (actually getting the datasource):
DataTable dataTable = dataGridView1.DataSource as DataTable;
// do some null checking here
List<MyObject> myObjects = ConvertBackToObject(dataTable);
This should give you the updated List of your MyObject (which you now can use for inserting data to the database).
Hope it is clear in some way and helped.
Upvotes: 1