How would I go about binding the following object, Car, to a gridview?
public class Car { long Id {get; set;} Manufacturer Maker {get; set;} } public class
The way that I approached this in a recent application was to create my own DataGridViewColumn and DataGridViewCell classes inheriting off of one of the existing ones such as DataGridViewTextBoxColumn and DataGridViewTextBoxCell.
Depending on the type of cell you want, you could use others such as Button, Checkbox, ComboBox, etc. Just take a look at the types available in System.Windows.Forms.
The cells deal with their value's as objects so you will be able to pass your Car class into the cell's value.
Overriding SetValue and GetValue will allow you to have any additional logic you need to handle the value.
For example:
public class CarCell : System.Windows.Forms.DataGridViewTextBoxCell
{
protected override object GetValue(int rowIndex)
{
Car car = base.GetValue(rowIndex) as Car;
if (car != null)
{
return car.Maker.Name;
}
else
{
return "";
}
}
}
On the column class the main thing you need to do is set the CellTemplate to your custom cell class.
public class CarColumn : System.Windows.Forms.DataGridViewTextBoxColumn
{
public CarColumn(): base()
{
CarCell c = new CarCell();
base.CellTemplate = c;
}
}
By using these custom Column/Cells on the DataGridView it allows you to add a lot of extra functionality to your DataGridView.
I used them to alter the displayed formatting by overriding GetFormattedValue to apply custom formatting to the string values.
I also did an override on Paint so that I could do custom cell highlighting depending on value conditions, altering the cells Style.BackColor to what I wanted based on the value.
Just use a List and set the DataMember to the string "Maker.Name" and if you want the DataKeyField to use car's ID just set that to "ID".
dataGrid.DataSource = carList;
dataGrid.DataMember = "Maker.Name";
dataGrid.DataKeyField = "ID";
dataGrid.DataBind();
I know that works in the repeater-control, at least...
Here's another option I got working:
<asp:TemplateColumn
HeaderText="Maker">
<ItemTemplate>
<%#Eval("Maker.Name")%>
</ItemTemplate>
</asp:TemplateColumn>
Might be ASP.NET 4.0 specific but it works like a charm!
public class Manufacturer
{
long Id {get; set;}
String Name {get; set;}
public override string ToString()
{
return Name;
}
}
Override the to string method.
Yes, you can create a TypeDescriptionProvider to accomplish nested binding. Here is a detailed example from an MSDN blog:
http://blogs.msdn.com/msdnts/archive/2007/01/19/how-to-bind-a-datagridview-column-to-a-second-level-property-of-a-data-source.aspx
If you want to expose specific, nested properties as binding targets, then Ben Hoffstein's answer (http://blogs.msdn.com/msdnts/archive/2007/01/19/how-to-bind-a-datagridview-column-to-a-second-level-property-of-a-data-source.aspx) is pretty good. The referenced article is a bit obtuse, but it works.
If you just want to bind a column to a complex property (e.g. Manufacturer) and override the rendering logic, then either do what ManiacXZ recommended, or just subclass BoundField and provide a custom implementation of FormatDataValue(). This is similar to overriding ToString(); you get an object reference, and you return the string you want displayed in your grid.
Something like this:
public class ManufacturerField : BoundField
{
protected override string FormatDataValue(object dataValue, bool encode)
{
var mfr = dataValue as Manufacturer;
if (mfr != null)
{
return mfr.Name + " (ID " + mfr.Id + ")";
}
else
{
return base.FormatDataValue(dataValue, encode);
}
}
}
Just add a ManufacturerField to your grid, specifying "Manufacturer" as the data field, and you're good to go.