views:

269

answers:

2

Hi there,

I'm using a DataGridView binding its datasource to a List, and specifying the properties for each column.

An example would be:

 DataGridViewTextBoxColumn colConcept = new DataGridViewTextBoxColumn();
            DataGridViewCell cell4 = new DataGridViewTextBoxCell();
            colConcept.CellTemplate = cell4;
            colConcept.Name = "concept";
            colConcept.HeaderText = "Concept";
            colConcept.DataPropertyName = "Concept";
            colConcept.Width = 200;
            this.dataGridViewBills.Columns.Add(colConcept);

{... assign other colums...}

And finally

 this.dataGridViewBills.DataSource=billslist; //billslist is List<Bill>

Obviously Class Bill has a Property called Concept, as well as one Property for each column.

Well, now my problem, is that Bill should have and Array/List/whateverdynamicsizecontainer of strings called Years.

Let's assume that every Bill will have the same Years.Count, but this only known at runtime.Thus, I can't specify properties like Bill.FirstYear to obtain Bill.Years[0], Bill.SecondYear to obtain Bills.Years[1]... etc... and bind it to each column.

The idea, is that now I want to have a grid with dynamic number of colums (known at runtime), and each column filled with a string from the Bill.Years List. I can make a loop to add columns to the grid at runtime depending of Bill.Years.Count, but is possible to bind them to each of the strings that the Bill.Years List contains???

I'm not sure if I'm clear enough.

The result ideally would be something like this, for 2 bills on the list, and 3 years for each bill:

--------------------------------------GRID HEADER-------------------------------
NAME      CONCEPT         YEAR1              YEAR2                YEAR3   
--------------------------------------GRID VALUES-------------------------------
Bill1     Bill1.Concept   Bill1.Years[0]     Bill1.Years[1]       Bill1.Years[2]
Bill2     Bill2.Concept   Bill2.Years[0]     Bill2.Years[1]       Bill2.Years[2]


I can always forget the datasource, and write each cell manually, as the MSFlexGrid used to like, but if possible, I would like to use the binding capabilities of the DataGridView.

Any ideas? Thanks a lot.

A: 

You could use reflection to set up and fill the DataGridView. I've done this with a single type, but I don't see why it couldn't be extended to your data structure.

To set up the DataGridView:

// Create the columns based on the data in the album info - get by reflection
var ai = new AlbumInfo();
Type t = ai.GetType();

dataTable.TableName = t.Name;

foreach (PropertyInfo p in t.GetProperties())
{
    // IF TYPE IS AN ARRAY (OR LIST) THEN ADD A COLUMN FOR EACH ELEMENT

    var columnSpec = new DataColumn();

    // If nullable get the underlying type
    Type propertyType = p.PropertyType;
    if (IsNullableType(propertyType))
    {
        var nc = new NullableConverter(propertyType);
        propertyType = nc.UnderlyingType;
    }
    columnSpec.DataType = propertyType;
    columnSpec.ColumnName = p.Name;
    dataTable.Columns.Add(columnSpec);
}

dataGridView.DataSource = dataTable;

Then to populate the DataGridView:

// Add album info to table - add by reflection
var ai = new AlbumInfo();
Type t = ai.GetType();
// WOULD NEED TO INCREASE BY LENGTH OF ARRAY
var row = new object[t.GetProperties().Length];

int index = 0;
foreach (PropertyInfo p in t.GetProperties())
{
    // IF TYPE IS AN ARRAY (OR LIST) THEN ADD EACH ELEMENT

    row[index++] = p.GetValue(info, null);
}

dataTable.Rows.Add(row);

This is just the code I used, so you'll have to modify the code to handle your year array/list.

ChrisF
A: 

I recently ran into this same problem. I ended up using DataGridView's virtual mode instead of binding to a data source. It doesn't have exactly the same features as binding, but it's still a lot more powerful than populating each cell manually.

In virtual mode, the DataGridView will fire an event whenever it needs to display a cell, which essentially means you can populate the cell however you please:

    private void my_init_function() {
        datagridview.VirtualMode = true;
        datagridview.CellValueNeeded += new System.Windows.Forms.DataGridViewCellValueEventHandler(datagridview_CellValueNeeded);
    }

    private void datagridview_CellValueNeeded(object sender, DataGridViewCellValueEventArgs e)
    {
        e.Value = get_my_data(e.RowIndex, e.ColumnIndex);
    }
Daniel Stutzbach