能否将复杂类型属性绑定到数据网格中?

21

我该如何将以下对象Car绑定到GridView?

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

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

基本类型很容易绑定,但是我发现无法显示Maker的任何内容。我希望它显示Manufacturer.Name。这是否可能?

有什么办法可以实现?我需要在Car中存储ManufacturerId,并设置lookupEditRepository与制造商列表吗?

8个回答

29

好的各位...这个问题发布的时间很久远,但我刚刚发现一种相当不错且简单的方法,可以使用反射在单元格格式化事件中检索嵌套属性。

操作如下:

    private void Grid_CellFormatting(object sender, DataGridViewCellFormattingEventArgs e)
    {

        DataGridView grid = (DataGridView)sender;
        DataGridViewRow row = grid.Rows[e.RowIndex];
        DataGridViewColumn col = grid.Columns[e.ColumnIndex];
        if (row.DataBoundItem != null && col.DataPropertyName.Contains("."))
        {
            string[] props = col.DataPropertyName.Split('.');
            PropertyInfo propInfo = row.DataBoundItem.GetType().GetProperty(props[0]);
            object val = propInfo.GetValue(row.DataBoundItem, null);
            for (int i = 1; i < props.Length; i++)
            {
                propInfo = val.GetType().GetProperty(props[i]);
                val = propInfo.GetValue(val, null);
            }
            e.Value = val;
        }
    }

就是这样!您现在可以在列的DataPropertyName中使用熟悉的语法“ParentProp.ChildProp.GrandChildProp”了。


工作得很好!谢谢分享。 - Quantamax
这个应该得到更多的赞!虽然完全不知道发生了什么,但是它确实有效。感谢分享!!! - Stephen Ho

12

1
实现这个的复杂度相当高。也许我应该再有一组可绑定类,将我的业务对象转换成它们? - Xerx
这是一个设计决策。你要么为绑定目的创建一堆新类,要么使用我在帖子中提供的方法。个人而言,我不喜欢用“助手”来混乱我的类层次结构,所以我会选择后者。 - Ben Hoffstein

9
我在最近的一个应用程序中采用的方法是创建自己的DataGridViewColumn和DataGridViewCell类,继承现有的类之一,例如DataGridViewTextBoxColumn和DataGridViewTextBoxCell。
根据所需的单元格类型,您可以使用其他类型,例如Button、Checkbox、ComboBox等。只需查看System.Windows.Forms中提供的类型即可。
单元格将其值作为对象处理,因此您将能够将您的Car类传递到单元格的值中。
通过覆盖SetValue和GetValue,您将能够处理任何额外的逻辑以处理该值。
例如:
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 "";
        }
    }
}

在列类上,您需要做的主要是将CellTemplate设置为自定义单元格类。

public class CarColumn : System.Windows.Forms.DataGridViewTextBoxColumn
{
    public CarColumn(): base()
    {
        CarCell c = new CarCell();
        base.CellTemplate = c;
    }
}

通过使用这些自定义的DataGridView列/单元格,您可以为DataGridView添加许多额外的功能。我使用它们来通过覆盖GetFormattedValue来对字符串值应用自定义格式来更改显示格式。我还重写了Paint,以便根据值条件进行自定义单元格突出显示,并根据值将单元格Style.BackColor更改为所需的颜色。

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

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

重写 toString 方法。


如果你只需要一个属性,那么这是可以的,但如果你需要该对象的多个属性,则无法正常工作。 - Gustavo Cardoso

2

我还有另一种可行的选择:

<asp:TemplateColumn
    HeaderText="Maker">
    <ItemTemplate>
          <%#Eval("Maker.Name")%>
    </ItemTemplate>
</asp:TemplateColumn>

可能是ASP.NET 4.0特有的,但它的效果非常好!

2

只需使用一个列表,将DataMember设置为字符串“Maker.Name”,如果您想使用汽车的ID作为DataKeyField,只需将其设置为“ID”即可。

dataGrid.DataSource = carList;
dataGrid.DataMember = "Maker.Name";
dataGrid.DataKeyField = "ID";
dataGrid.DataBind();

我知道这在转发器控件中可以运作,至少...


2
如果您想将特定的嵌套属性作为绑定目标公开,那么Ben Hoffstein的回答(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)非常好。所引用的文章有点晦涩,但它有效。
如果您只想将列绑定到一个复杂属性(例如Manufacturer)并覆盖渲染逻辑,则可以按照ManiacXZ建议的方法进行操作,或者只需子类化BoundField并提供FormatDataValue()的自定义实现。这类似于重写ToString();您获得了一个对象引用,并返回您希望在网格中显示的字符串。
像这样:
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);
        }
    }
}

只需在您的网格中添加一个 ManufacturerField,将数据字段指定为“Manufacturer”,然后就可以开始使用了。

1
我认为你可以这样做:
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;}
}

1
并不是人们通常想做的事情,但它似乎更符合Demeter法则。 - jpierson

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接