views:

1257

answers:

7

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 Manufacturer
{
   long Id {get; set;}
   String Name {get; set;}
}

The primitive types get bound easy but I have found no way of displaying anything for Maker. I would like for it to display the Manufacturer.Name. Is it even possible?

What would be a way to do it? Would I have to store ManufacturerId in Car as well and then setup an lookupEditRepository with list of Manufacturers?

A: 
    public class Manufacturer
    {
       long Id {get; set;}
       String Name {get; set;}

       public override string ToString()
       {
          return Name;
       }
    }

Override the to string method.

HollyStyles
A: 

You could try to bind DataGrid property to Maker.Name

i hope i help you!

stefano m
+6  A: 

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

Ben Hoffstein
The level of complexity to achive this is pretty high. Should I have another set of bindable-classes that i convert my business objects to perhaps?
Xerx
It's a design decision. Either you have to create a bunch of new classes simply for binding purposes or use the recipe provided in my post. Personally, I hate cluttering up my class hierarchy with 'helpers', so I'd go with the latter.
Ben Hoffstein
+1  A: 

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...

Seb Nilsson
+1  A: 

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.

ManiacZX
A: 

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.

Seth Petry-Johnson
A: 

I would assume you could do the following:

public class Car
{
    public long Id {get; set;}
    public Manufacturer Maker {private get; set;}

    public string ManufacturerName
    {
       get { return Maker != null ? Maker.Name : ""; }
    }
}

public class Manufacturer
{
   long Id {get; set;}
   String Name {get; set;}
}
Spear
Not really what people normally want to do but it does seem to jive more with the Law of Demeter.
jpierson