如何将复杂对象绑定到DataGridView?

5

我有一个遵循以下形式的类:

public class Cat
{
    public string Name { get; set; }
    public string Description {get; set; }
    public List<Cheezburger> Cheezbugers { get; private set; }
};

public class Cheezburger
{
    public int PattyCount { get; set; }
    public bool CanHaz { get; set; }
};

我希望能够在 DataGridView 中以以下方式显示 List 的猫:
---------------------------------------------------------------------------------
| Name     | Description  |  PattyCount | CanHaz   | PattyCount   | CanHaz  | etc
--------------------------------------------------------------------------------
| Felix    | Classic Cat  |  1          | true     | 3            | false   | etc
| Garfield | Fat,Lazy Cat |  2          | false    | 7            | true    | etc

等等……目标是将所有CatCheezbuger列在同一行中。如果您只尝试绑定Cat列表,您将无法获得此行为。

问题在于我无法弄清如何在DataGridViewCats.Cheezbugers列表中的各个项目之间进行复杂的源绑定。值得一提的是,我知道列表中的每个Cat都具有相同数量的Cheezbuger

编辑

我知道DataGridView complex binding也提出了同样的问题,但接受的答案仅在我事先知道列表中有多少项时才起作用,而这并不是情况。我所知道的是所有列表的长度都相同。

2个回答

6
这不仅是一个“复杂绑定”,而是一个Pivot,您想要将重复的详细数据(cheezburgers列表)转换为一行,并且该行具有不确定数量的列。
我认为你最好的选择是编写一个自定义序列化器,使你可以将数据转换为xml数据表中的行,然后进行绑定。由于你的列数不一致,xml会更加宽容,但我不确定DataGridView会如何处理它。
编辑如下: 由于我不知道DataGridView会如何处理XML DataTable,所以我决定编写并测试它。它按照我预期的方式工作,我相信这也是你想要的。
  1. Here are your cat & cheezburger classes (slightly modified)

    public class Cat
    {
        public string Name { get; set; }
        public string Description { get; set; }
        public List<Cheezburger> Cheezbugers { get; private set; }
    
        public void AddCheezburger(Cheezburger cheezburger)
        {
            if (this.Cheezbugers == null)
                this.Cheezbugers = new List<Cheezburger>();
            this.Cheezbugers.Add(cheezburger); 
        }
    };
    
    public class Cheezburger
    {
        public int PattyCount { get; set; }
        public bool CanHaz { get; set; }
    };
    
  2. Then you need to create simple form with two buttons "bind to object" (button1) and "bind to datatable" (button2), with a DataGridView anchored to the bottom. and code up the form like:

//在编辑器中,下一行是在代码块中,一旦我保存它,它就不在了...
public partial class Form1 : Form
{

    List<Cat> cats = new List<Cat>();

    public Form1()
    {
        InitializeComponent();

        cats.Add(new Cat() { Name = "Felix", Description = "Classic Cat" });
        cats.Add(new Cat() { Name = "Garfield", Description = "Fat,Lazy" });
        cats.Add(new Cat() { Name = "Tom", Description = "Wanna-Be-Mouser" });
        cats[0].AddCheezburger(new Cheezburger() { CanHaz = true, PattyCount = 1 });
        cats[0].AddCheezburger(new Cheezburger() { CanHaz = false, PattyCount = 3 });
        cats[1].AddCheezburger(new Cheezburger() { CanHaz = false, PattyCount = 2 });
        cats[1].AddCheezburger(new Cheezburger() { CanHaz = true, PattyCount = 7 });
        cats[1].AddCheezburger(new Cheezburger() { CanHaz = true, PattyCount = 99 });
        cats[2].AddCheezburger(new Cheezburger() { CanHaz = true, PattyCount = 5 });
        cats[2].AddCheezburger(new Cheezburger() { CanHaz = false, PattyCount = 14 });

    }

    private void button1_Click(object sender, EventArgs e)
    {
        dataGridView1.DataSource = null;
        dataGridView1.DataSource = cats;
    }

    private void button2_Click(object sender, EventArgs e)
    {
        dataGridView1.DataSource = null;
        dataGridView1.DataSource = serializeCats(cats);
    }

    private DataTable serializeCats(List<Cat> cats)
    {

        DataTable returnTable = new DataTable("Cats");
        returnTable.Columns.Add(new DataColumn("Name"));
        returnTable.Columns.Add(new DataColumn("Description"));
        int setID = 1;
        foreach (Cat cat in cats)
        {
            //If the row requires more columns than are present then add additional columns
            int totalColumnsRequired = (cat.Cheezbugers.Count * 2) + 2;

            while (returnTable.Columns.Count < totalColumnsRequired)
            {
                returnTable.Columns.Add(new DataColumn("Can Haz " + setID.ToString()));
                returnTable.Columns.Add(new DataColumn("Patty Count " + setID.ToString()));
                setID++;
            }
            returnTable.AcceptChanges();
            DataRow row = returnTable.NewRow();
            row[0] = cat.Name;
            row[1] = cat.Description;
            int cbi = 2; //cheezburger index
            foreach (Cheezburger cheezburger in cat.Cheezbugers)
            {
                row[cbi] = cheezburger.CanHaz;
                cbi++;
                row[cbi] = cheezburger.PattyCount;
                cbi++;
            }

            returnTable.Rows.Add(row);
        }
        return returnTable;
    }
}

不要尝试预定义DataGridView列,它们将根据数据源动态创建。绑定到猫列表会得到两列(名称/描述),绑定到数据表会获得8列,名称和描述以及6列Cheezburger信息,排列方式如您所希望的(我认为)。Cats Haz Cheezeburgers

很棒的答案,表述得非常好。 - Anthony

0

如果我正确理解了你的问题,我认为你想知道如何显示每只猫的Cheezbugers清单。

一个概念上实现这个的方法是将Cheezbugers属性绑定到带有网格自定义列模板的Repeater中。换句话说,在自定义模板网格列中嵌套一个重复器并在那里进行绑定。您可以使用列表项模式或其他格式来呈现Cheezbugers列表。它将支持列表中的任意数量的项目。

编辑:我刚刚意识到你正在开发Windows表单,而不是ASP.NET。在表单中有一个等效或合理等效于重复器的{{link1:DataRepeater}}。可能能够满足您的需求。以上的想法是相同的。


是的,你理解我的问题正确。我以前从未听说过“Repeater”,现在要去了解一下它。 - Anthony
我在意识到你正在使用Windows表单而不是ASP.NET后编辑了答案。Repeater方法仍然可以满足您的需求(请参见答案)。 - KP.

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