繁体   English   中英

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

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

我正在尝试在应用程序中的DataGridView上显示来自相关实体的多个属性。 这对我来说似乎很普通,但我很难找到例子。 这是一个订单输入操作。 OrderSheet 数据、订单的 ID 和取货日期,然后是网格中的行项目(以下模型中的 OrderSheetItems)。 订单行项目具有基于 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 位于单独的列中,仅用于试验,稍后将用作组合框。 那么有没有办法将其他列绑定到 OrderSheetItem 的 Product 导航属性中的数据,或者我是否必须处理产品 id 上的 CellValueChanged 以物理设置其他列中的数据? 如果有一种方法可以绑定列,那么是通过 OnLoad 中的代码还是网格视图列设计器中的某个地方?

TIA,迈克

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

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

选项 1 - 使用 DataGridViewComboBoxColumn

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

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

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

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

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

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

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

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

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

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

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

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

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

选项 5 - 通过覆盖ToString()显示对象的字符串表示

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

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

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

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

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

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

使用 CellFormatting 和 CellParsing 在 DataGridView 中显示和编辑嵌套属性

特征:

  • 支持任何级别的嵌套。
  • 支持可编辑和只读

这个怎么运作:

  • 每列都有“。” DataPropertyName中将被视为嵌套属性。
  • 将使用递归函数处理CellFormatting事件以获取嵌套属性的值。
  • CellParsing事件将被处理以使用递归函数设置嵌套属性的值。

以下是方法:

public object GetPropertyValue(object source, string name)
{
    if (name.Contains("."))
    {
        var nameParts = name.Split(new[] { '.' }, 2);
        return GetPropertyValue(GetPropertyValue(source, nameParts[0]), nameParts[1]);
    }
    else
    {
        var property = TypeDescriptor.GetProperties(source)[name];
        return property.GetValue(source);
    }
}
public void SetPropertyValue(object source, string name, object value)
{
    if (name.Contains("."))
    {
        var nameParts = name.Split(new[] { '.' }, 2);
        SetPropertyValue(GetPropertyValue(source, nameParts[0]), nameParts[1], value);
    }
    else
    {
        var property = TypeDescriptor.GetProperties(source)[name];
        property.SetValue(source, value);
    }
}

这是事件处理程序:

private void CellFormatting(object sender, DataGridViewCellFormattingEventArgs e)
{
    if (e.ColumnIndex < 0 || e.RowIndex < 0) return;
    var dg = (DataGridView)sender;
    var propertyName = dg.Columns[e.ColumnIndex].DataPropertyName;
    if (propertyName.Contains("."))
    {
        var dataObject = dg.Rows[e.RowIndex].DataBoundItem;
        e.Value = GetPropertyValue(dataObject, propertyName);
    }
}

private void CellParsing(object sender, DataGridViewCellParsingEventArgs e)
{
    var dg = (DataGridView)sender;
    var propertyName = dg.Columns[e.ColumnIndex].DataPropertyName;
    if (propertyName.Contains("."))
    {
        var dataObject = dg.Rows[e.RowIndex].DataBoundItem;
        SetPropertyValue(dataObject, propertyName, e.Value);
    }
}

这是示例:

var categories = new List<Category>() {
    new Category{ Id= 1, Name = "C1"},
    new Category{ Id= 2, Name = "C2"}
};
var products = new List<Product>() {
    new Product(){ Id = 1, Name ="P1", Category = categories[0]},
    new Product(){ Id = 2, Name ="P2", Category = categories[0]},
    new Product(){ Id = 3, Name ="P3", Category = categories[1]},
};
var dg = new DataGridView();
dg.AutoGenerateColumns = false;
dg.Columns.Add(new DataGridViewTextBoxColumn()
{
    HeaderText = "Id",
    DataPropertyName = "Id"
});
dg.Columns.Add(new DataGridViewTextBoxColumn()
{
    HeaderText = "Name",
    DataPropertyName = "Name"
});
dg.Columns.Add(new DataGridViewTextBoxColumn()
{
    HeaderText = "CategoryId",
    DataPropertyName = "Category.Id"
});
dg.Columns.Add(new DataGridViewTextBoxColumn()
{
    HeaderText = "CategoryName",
    DataPropertyName = "Category.Name"
});
dg.Dock = DockStyle.Fill;
dg.DataSource = products;
this.Controls.Add(dg);
dg.CellFormatting += CellFormatting;
dg.CellParsing += CellParsing;

在此处输入图片说明

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM