Show Properties of a Navigation Property in DataGridView (Second Level Properties)

You can use either of these options:

  1. Use DataGridViewComboBoxColumn
  2. Add corresponding properties to child entity partial class
  3. Shape the query to include properties of navigation property using Linq
  4. Use CellFormatting event to get value for sub property bounded columns
  5. Show string representation of object by overriding ToString()
  6. Use a custom TypeDescriptor to enable data binding to sub properties.

Option 1 - Use DataGridViewComboBoxColumn

Usage: This approach would be useful specially in cases which you want to keep the control editable.

In this approach you can use DataGridViewComboBoxColumn to show any field of navigationn property. To show multiple field sub properties of navigation property in grid, use multiple DataGridViewComboBoxColumn bound to same navigation property with different DisplayMember

In this approach, additional to your ProductId column, add more DataGridViewComboBoxColumn to the grid and then perform these settings for all additional combo columns:

  • Set DataPropertyName of them to ProductId
  • Set the DataSource property of them, to exactly the same data source you used for main ProductId column, for example productBindingSource
  • Set ValueMember of them to the same value member you set for product id column, it's the key column of your product table.(ProductId)
  • Set DisplayMember for each of them to a column that you want to show, for example, set one of them to Name. one to Price, one to Size, ... . This way you can show related entity fields.
  • Set ReadOnly property of them to true. It makes the cell read only.
  • If you want to make columns readonly Set DisplayStyle property of them to Nothing. It removes dropdown style.

If you want to keep ProductId editable, keep the DisplayStyle of it to DropDownButton. This way when you change the value of ProductId column using combobox, when you leave the row and moved to next row, you will see other cells of row, shows other properties of the selected product. Also since the other combobox columns are read only and have no combobox style, the user can not change the value of them and they act only like a read only text box column that show other properties from related entity.

Option 2 - Add corresponding properties to child entity partial class

Usage: This approach would be useful when you don't need to edit values.

In this approach, You can define properties in child entity partial class return value of corresponding property of parent entity. For example for product name, define this property in order item partial class:

public string ProductName
{
    get
    {
        if (this.Product != null)
            return this.Product.Name;
        else 
            return string.Empty;
    }
}

Then you can simply include products when selecting order items and bind the grid column to corresponding properties of order item.

Option 3 - Shape the query to include properties of navigation property

Usage: This approach would be useful when you don't need to edit values.

You can shape the query to include properties of navigation property. You can use an anonymous object or a View Mode simply, for example:

var list = db.OrderDetails.Include("Products").Where(x=>x.OrderId==1)
             .Select(x=> new OrderDetailVM() { 
                 Id = x.Id, 
                 ProductId = x.ProductId, 
                 ProductName = x.Product.Name,     
                 Price = x.Product.Price
              }).ToList();       

Option 4 - Use CellFormatting event to get value for sub property bounded columns

Usage: This approach would be useful when you don't need to edit values.

In this approach you can use CellFormatting event of DataGridView. You can simply set e.Value based on column index. For example:

void dataGridView1_CellFormatting(object sender, DataGridViewCellFormattingEventArgs e)
{
    //I Suppose you want to show product name in column at index 3
    if(e.RowIndex>=0 && e.ColumnIndex==3)
    {
        var orderLineItem= (OrderLineItem)(this.dataGridView1.Rows[e.RowIndex]
            .DataBoundItem);
        if (order!= null && orderLineItem.Product != null)
            e.Value = orderLineItem.Product.Name);
    }
}

You can use different criteria to handle different columns and show different sub properties.

Also you can make it more dynamic and reusable using reflection. You can extract the value of sub property of navigation property using reflection. To do so you should create column and set DataPropertyName to sub properties like Product.Name then in CellFormatting event, using reflection, get the value for column. Here is a good article by Antonio Bello about this approach:

  • DataGridView: How to Bind Nested Objects

Option 5 - Show string representation of object by overriding ToString()

Usage: This approach would be useful when you don't need to edit values.

If you want to show only a single column of navigation property, you can simply override ToString() method of navigation property class and return suitable value. This way, when showing a property of that type in grid, you will see a friendly text. For example in partial class of Product, you can write:

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

Option 6 - Use a custom TypeDescriptor to enable data binding to sub properties

Usage: This approach would be useful when you don't need to edit values.

In this approach you can create a custom TypeDescriptor that enables you to perform data binding to second-level properties. Here is a good article by Linda Liu about this approach:

  • How to bind a DataGridView column to a second-level property of a data source