How to associate a complex object with a DataGridView?

I have a class that follows the form:

public class Cat { public string Name { get; set; } public string Description {get; set; } public List<Cheezburger> Cheezbugers { get; private set; } }; public class Cheezburger { public int PattyCount { get; set; } public bool CanHaz { get; set; } }; 

I want to be able to display the List for Cats in a DataGridView as follows:

 --------------------------------------------------------------------------------- | Name | Description | PattyCount | CanHaz | PattyCount | CanHaz | etc -------------------------------------------------------------------------------- | Felix | Classic Cat | 1 | true | 3 | false | etc | Garfield | Fat,Lazy Cat | 2 | false | 7 | true | etc 

And so on ... The goal is to list all Cat Cheezbuger on the same line. If you just try to bind a Cat list, you will not get this behavior.

The problem is that I cannot figure out how to make a complex source binding between the DataGridView and the individual items in the Cats.Cheezbugers list. For what it's worth, I know for sure that every Cat on the list has the same amount of Cheezbuger on its list.

Edit

I know that a complex DataGridView binding poses the same question, but the accepted answer only works if I know how many items in the list are in advance, and this is irrelevant. All I know is that all lists will be the same length.

+4
source share
2 answers

This is not just a “complex binding”, it is a Pivot where you want to convert the details of repeating data (list o "cheezburgers") into one row, and this row has an undefined number of columns.

I believe that your best option here is to write your own serializer, which will allow you to convert your data to strings in xml format, and then bind to it. Since the number of columns will be incompatible, xml will be more forgiving, although I'm not sure how the DataGridView will handle it.

EDIT FOLLOWING Since I did not "know" how the DataGridView processed the XML DataTable, I decided to write it and test it. I work as I expected, and I believe as you want.

  • Here are your cat and cheezburger classes (slightly modified)

     public class Cat { public string Name { get; set; } public string Description { get; set; } public List<Cheezburger> Cheezbugers { get; private set; } public void AddCheezburger(Cheezburger cheezburger) { if (this.Cheezbugers == null) this.Cheezbugers = new List<Cheezburger>(); this.Cheezbugers.Add(cheezburger); } }; public class Cheezburger { public int PattyCount { get; set; } public bool CanHaz { get; set; } }; 
  • Then you need to create a simple form with two buttons “bind to object” (button1) and “bind to datatable” (button2), while the DataGridView is bound to the base. and enter the form code:

// in the editor, this next line is in the code block, as soon as I save it, it’s not.

 public partial class Form1 : Form { List<Cat> cats = new List<Cat>(); public Form1() { InitializeComponent(); cats.Add(new Cat() { Name = "Felix", Description = "Classic Cat" }); cats.Add(new Cat() { Name = "Garfield", Description = "Fat,Lazy" }); cats.Add(new Cat() { Name = "Tom", Description = "Wanna-Be-Mouser" }); cats[0].AddCheezburger(new Cheezburger() { CanHaz = true, PattyCount = 1 }); cats[0].AddCheezburger(new Cheezburger() { CanHaz = false, PattyCount = 3 }); cats[1].AddCheezburger(new Cheezburger() { CanHaz = false, PattyCount = 2 }); cats[1].AddCheezburger(new Cheezburger() { CanHaz = true, PattyCount = 7 }); cats[1].AddCheezburger(new Cheezburger() { CanHaz = true, PattyCount = 99 }); cats[2].AddCheezburger(new Cheezburger() { CanHaz = true, PattyCount = 5 }); cats[2].AddCheezburger(new Cheezburger() { CanHaz = false, PattyCount = 14 }); } private void button1_Click(object sender, EventArgs e) { dataGridView1.DataSource = null; dataGridView1.DataSource = cats; } private void button2_Click(object sender, EventArgs e) { dataGridView1.DataSource = null; dataGridView1.DataSource = serializeCats(cats); } private DataTable serializeCats(List<Cat> cats) { DataTable returnTable = new DataTable("Cats"); returnTable.Columns.Add(new DataColumn("Name")); returnTable.Columns.Add(new DataColumn("Description")); int setID = 1; foreach (Cat cat in cats) { //If the row requires more columns than are present then add additional columns int totalColumnsRequired = (cat.Cheezbugers.Count * 2) + 2; while (returnTable.Columns.Count < totalColumnsRequired) { returnTable.Columns.Add(new DataColumn("Can Haz " + setID.ToString())); returnTable.Columns.Add(new DataColumn("Patty Count " + setID.ToString())); setID++; } returnTable.AcceptChanges(); DataRow row = returnTable.NewRow(); row[0] = cat.Name; row[1] = cat.Description; int cbi = 2; //cheezburger index foreach (Cheezburger cheezburger in cat.Cheezbugers) { row[cbi] = cheezburger.CanHaz; cbi++; row[cbi] = cheezburger.PattyCount; cbi++; } returnTable.Rows.Add(row); } return returnTable; } } 

Do not try to predefine the columns of the DataGridView, they will be created dynamically based on the data source. Binding to the list of cats will give you two columns (name / description). Linking to a DataTable gets 8 columns, name and description + 6 columns of cheezburger information, arranged as you think. Cats Haz Cheezeburgers

+6
source

If I read your question correctly, I think you are asking how to display a list of Cheezbugers items for each cat.

One way to conceptually accomplish this is to bind the Cheezbugers property to the Repeater using a custom column template in the grid. In other words, the repeater socket in the column of the special template table and bind it there. You can use a list template or other format to represent a list of Cheezbugers. It will support any number of items in the list.

EDIT: I just realized that you are developing on Windows forms, not asp.net. There is an equivalent or reasonable equivalent of a repeater in forms - DataRepeater . You may be able to meet your needs. The idea above is the same.

0
source

All Articles