在 DataGridView 中显示导航属性的属性(二级属性) [英] Show Properties of a Navigation Property in DataGridView (Second Level Properties)

查看:29
本文介绍了在 DataGridView 中显示导航属性的属性(二级属性)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试在

这是加载数据的代码

试试{_context.OrderSheets.Include(o => o.OrderSheetItems.Select(i => i.Product)).Load();orderSheetBindingSource.DataSource = _context.OrderSheets.Local.ToBindingList();}捕获(异常前)...

ProductId 位于单独的列中,仅用于试验,稍后将用作组合框.那么有没有办法将其他列绑定到 OrderSheetItem 的 Product 导航属性中的数据,或者我是否必须处理产品 id 上的 CellValueChanged 以物理设置其他列中的数据?如果有一种方法可以绑定列,那么是通过 OnLoad 中的代码还是网格视图列设计器中的某个位置?

TIA,迈克

解决方案

您可以使用以下任一选项:

  1. 使用DataGridViewComboBoxColumn
  2. 为子实体部分类添加相应的属性
  3. 使用 Linq
  4. 调整查询以包含导航属性的属性
  5. 使用 CellFormatting 事件获取子属性有界列的值
  6. 通过覆盖ToString()
  7. 来显示对象的字符串表示
  8. 使用自定义TypeDescriptor 来启用到子属性的数据绑定.

选项 1 - 使用 DataGridViewComboBoxColumn

用法:这种方法在您想要保持控件可编辑的情况下特别有用.

在这种方法中,您可以使用 DataGridViewComboBoxColumn 来显示导航属性的任何字段.要在网格中显示导航属性的多个字段子属性,请使用多个 DataGridViewComboBoxColumn 绑定到具有不同 DisplayMember

的相同导航属性

在这种方法中,除了 ProductId 列之外,还向网格添加更多 DataGridViewComboBoxColumn,然后为所有其他组合列执行这些设置:

  • 将它们的DataPropertyName设置为ProductId
  • 将它们的 DataSource 属性设置为与用于主 ProductId 列的数据源完全相同,例如 productBindingSource
  • 将它们的 ValueMember 设置为您为产品 ID 列设置的相同值成员,它是您的产品表的关键列.(ProductId)
  • 将每个的DisplayMember设置为您要显示的列,例如,将其中一个设置为名称.一个是价格,一个是尺寸,......这样您就可以显示相关的实体字段.
  • 将它们的 ReadOnly 属性设置为 true.它使单元格只读.
  • 如果你想让列成为只读 将它们的 DisplayStyle 属性设置为 Nothing.它删除了下拉样式.

如果您想保持 ProductId 可编辑,请将其 DisplayStyle 保持为 DropDownButton.这样,当您使用组合框更改 ProductId 列的值时,当您离开该行并移动到下一行时,您将看到该行的其他单元格,显示所选产品的其他属性.此外,由于其他组合框列是只读的并且没有组合框样式,因此用户无法更改它们的值,并且它们的作用就像只读文本框列一样,显示相关实体的其他属性.

选项 2 - 为子实体部分类添加相应的属性

用法: 当您不需要编辑值时,这种方法会很有用.

在这种方法中,您可以在子实体部分类中定义属性,返回父实体对应属性的值.例如对于产品名称,在订单项部分类中定义此属性:

public string ProductName{得到{如果 (this.Product != null)返回 this.Product.Name;别的返回字符串.空;}}

然后你可以在选择订单项时简单地包含产品并将网格列绑定到订单项的相应属性.

选项 3 - 调整查询以包含导航属性的属性

用法: 当您不需要编辑值时,这种方法会很有用.

您可以调整查询以包含导航属性的属性.您可以简单地使用匿名对象或查看模式,例如:

var list = db.OrderDetails.Include("Products").Where(x=>x.OrderId==1).Select(x=> new OrderDetailVM() {Id = x.Id,ProductId = x.ProductId,产品名称 = x.产品.名称,价格 = x.产品.价格}).ToList();

选项 4 - 使用 CellFormatting 事件获取子属性有界列的值

用法: 当您不需要编辑值时,这种方法会很有用.

在这种方法中,您可以使用 DataGridViewCellFormatting 事件.您可以根据列索引简单地设置 e.Value.例如:

void dataGridView1_CellFormatting(object sender, DataGridViewCellFormattingEventArgs e){//我假设您想在索引 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);}}

您可以使用不同的条件来处理不同的列并显示不同的子属性.

您还可以使用反射使其更具动态性和可重用性.您可以使用反射提取导航属性的子属性的值.为此,您应该创建列并将 DataPropertyName 设置为 Product.Name 等子属性,然后在 CellFormatting 事件中,使用反射,获取柱子.这是 Antonio Bello 撰写的一篇关于这种方法的好文章:

选项 5 - 通过覆盖 ToString()

显示对象的字符串表示

用法: 当您不需要编辑值时,这种方法会很有用.

如果你只想显示单列导航属性,你可以简单地覆盖导航属性类的ToString()方法并返回合适的值.这样,当在网格中显示该类型的属性时,您将看到一个友好的文本.例如在Product的部分类中,你可以这样写:

公共覆盖字符串 ToS​​tring(){返回 this.Name;}

选项 6 - 使用自定义 TypeDescriptor 启用到子属性的数据绑定

用法: 当您不需要编辑值时,这种方法会很有用.

在这种方法中,您可以创建一个自定义 TypeDescriptor,使您能够对二级属性执行数据绑定.下面是 Linda Liu 关于这种方法的一篇好文章:

I'm trying to display several properties from a related entity on a DataGridView in a app. It seems pretty ordinary to me but I'm having trouble finding examples. It's an order entry operation. OrderSheet data, the ID and the pickup date for the order, then the line items (OrderSheetItems in the model below) in the grid. The order lineitems have a navigation property, Product, based on the ProductId. I can use a DataGridViewComboBoxColumn with ProductId as ValueMember and another field as DisplayMember. But I want to include more data in other columns, size, color, material, etc.

Here's the code for loading the data

try
{
    _context.OrderSheets.Include(o => o.OrderSheetItems.Select(i => i.Product)).Load();
    orderSheetBindingSource.DataSource = _context.OrderSheets.Local.ToBindingList();
}
catch (Exception ex)...

The ProductId is in a separate column just for experimenting, that will be the combobox later. So is there a way to bind the other columns to the data in Product navigation property of the OrderSheetItem or do I have to handle CellValueChanged on the product id to physically set the data in the other columns? If there's a way to bind the columns then would that be via code in OnLoad or somewhere in the grid view columns designer?

TIA, Mike

解决方案

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:

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:

这篇关于在 DataGridView 中显示导航属性的属性(二级属性)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆