在DataGridView中显示导航属性的属性(二级属性)

我想在一个WinForms应用程序的DataGridView上显示相关实体的几个属性。 对我来说这似乎很普通,但是我很难find例子。 这是一个订单input操作。 OrderSheet数据,订单的ID和提货date,然后是网格中的订单项(以下模型中的OrderSheetItems)。 订单的lineitems具有基于ProductId的导航属性Product。 我可以使用DataGridViewComboBoxColumn与ProductId作为ValueMember和另一个字段作为DisplayMember。 但是我想在其他栏目中包含更多的数据,大小,颜色,材质等

OrderSheet和OrderSheetItems与相关产品

订单输入

这里是加载数据的代码

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

ProductId是在一个单独的列中进行实验,稍后将是combobox。 那么是否有办法将其他列绑定到OrderSheetItem的Product navigation属性中的数据,还是必须处理产品ID上的CellValueChanged以物理设置其他列中的数据? 如果有一种方法来绑定列,那么会通过OnLoad中的代码或网格视图列devise器中的某个地方?

TIA,Mike

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

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

选项1 – 使用DataGridViewComboBoxColumn

用法:这种方法特别适用于您希望保持控件可编辑的情况。

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

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

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

如果要保持ProductId可编辑,请将其DisplayStyle保持为DropDownButton 。 通过这种方式,当您使用combobox更改ProductId列的值时,当您离开行并移动到下一行时,您将看到行的其他单元格,显示所选产品的其他属性。 另外,由于其他combobox的列是只读的,没有combobox样式,用户不能改变它们的值,它们的作用就像一个只读的文本框列,显示相关实体的其他属性。

选项2 – 将相应的属性添加到子实体部分类

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

在这种方法中,您可以在子实体的子实体部分类的返回值中定义父实体相应属性的属性。 例如,对于产品名称,请按顺序项目部分类定义此属性:

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

然后,您可以在select订单项目时简单包含产品,并将网格列绑定到订单项目的相应属性。

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

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

您可以塑造查询以包含导航属性的属性。 您可以简单地使用匿名对象或视图模式,例如:

 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(); 

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

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

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

 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); } } 

您可以使用不同的标准来处理不同的列,并显示不同的子属性。

你也可以使用reflection使其更加dynamic和可重用。 您可以使用reflection提取导航属性的子属性的值。 为此,您应该创build列并将DataPropertyName设置为Product.Name等子属性,然后在CellFormatting事件中使用reflection,获取列的值。 这是Antonio Bello关于这种方法的一篇很好的文章:

  • DataGridView:如何绑定嵌套的对象

选项5 – 通过覆盖ToString()显示对象的string表示forms

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

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

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

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

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

在这种方法中,您可以创build一个自定义的TypeDescriptor,使您可以执行数据绑定到二级属性。 这是琳达刘关于这种方法的一篇很好的文章:

  • 如何将DataGridView列绑定到数据源的二级属性