使用复杂 object 的嵌套 asp 中继器

Nested asp repeaters using complex object

我有一个复杂的 object,其中嵌套了 collection 个 object,需要绑定到 table 中的 repeater/shown。 Object 显示 14 个项目:

我想这样显示数据:

Objects 看起来像:

我需要在 table/column header 中显示“FieldName”,在 table 行中显示“FieldValue”。注意第 3 项和第 5 项都是“Phone Number”FieldName。所以我需要 PhoneNumber 显示一次(在 header 列中)然后会有 2 行显示 2 个电话号码字段值(如 table 屏幕截图中所示)。因此 collection 中可能有多组项目。 N 个联系人等。我不确定如何设置中继器(或使用多少个)和/或如何设置中继器 OnItemDataBound。任何指向正确方向的想法或伪代码都会非常有帮助。

更新: 调用 API 响应 returns 列表(即联系人)

属性objects(示例):

1st item: FieldName = "Name" FieldValue = "Mike Jones"

2nd item: FieldName = "Phone Number" FieldValue = "999-999-9999"

3rd item: FieldName = "Address" FieldValue = "123 Main St"

4th item: FieldName = "Name" FieldValue = "Mary Price"

5th item: FieldName = "Phone Number" FieldValue = "888-999-7777"

6th item: FieldName = "Address" FieldValue = "789 Broadway St"

标记需要显示 table:

-------------------------------------------------
|Name       |   Phone Number |  Address         |
-------------------------------------------------
|Mike Jones |   999-999-9999 |  123 Main St     |
-------------------------------------------------
|Mary Price |   888-999-7777 |  789 Broadway St |
-------------------------------------------------

好的,所以问题很简单:

我有一个名字列表,对于每个名字,我可能有 child table 个 phone 个数字。他们可能没有 phone 个号码,他们可能有 5 个 phone 个号码。

什么是显示此主记录的好方法,然后是每行的重复 child 记录?

有很多方法可以做到这一点。我经常建议对于行的主要显示,我们使用列表视图,然后为 child 行嵌套一个网格视图。

然而,一如既往,哪条路将取决于多少数据,以及child数据显示必须有多少行(复杂)?

但是,让我们使用一个简单的网格。由于这样的设置变得更加复杂,因此我强烈建议使用列表视图。列表视图更好,因为它在布局方面更加灵活。虽然“设置”是更多的标记,但为每一行添加新列和复杂控件要好得多。在较长的 运行 列表视图中,实际上可以减少整体标记。

但是,我们只有两列额外的“child 重复”数据。

所以我们实际上是这样的:

 table people  - our list of contacts/people
 table Phones  - our child list of phone numbers for each "people".

那么,让我们与 table 人一起组建一个网格。我会经常启动并让向导创建那个网格。然后我在网页中清除(删除)数据源,然后开始使用删除键删除多余的垃圾。

所以,我们现在有了这个标记:

<div style="width:30%">
    <asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="False"
        DataKeyNames="ID" CssClass="table">
        <Columns>
            <asp:BoundField DataField="Firstname" HeaderText="Firstname"  />
            <asp:BoundField DataField="LastName" HeaderText="LastName" />
            <asp:BoundField DataField="City" HeaderText="City"  />
        </Columns>
    </asp:GridView>
</div>

我们填充这个网格的代码是这样的:

    protected void Page_Load(object sender, EventArgs e)
    {
        if (!IsPostBack)
            LoadGrid();
    }

    void LoadGrid()
    {
        DataTable rst = MyRst("SELECT * from People Order by FirstName");
        GridView1.DataSource = rst;
        GridView1.DataBind();
    }

    public DataTable MyRst(string strSQL)
    {
        DataTable rstData = new DataTable();
        using (SqlCommand cmdSQL = new SqlCommand(strSQL,
            new SqlConnection(Properties.Settings.Default.TEST4)))
        {
            cmdSQL.Connection.Open();
            rstData.Load(cmdSQL.ExecuteReader());
        }
        return rstData;
    }

我们的输出是这样的:

好的,所以我们当然有这个设置:

现在,我们可以进行查询连接,但话又说回来,这将重复每个 child 行的主行(然后我们将不得不“隐藏”它。

所以,让我们转到标记并添加两行。 (电话类型和电话号码)。

所以我们现在有这个:

<div style="width:40%">
    <asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="False"
        DataKeyNames="ID" CssClass="table">
        <Columns>
            <asp:BoundField DataField="Firstname" HeaderText="Firstname"  />
            <asp:BoundField DataField="LastName" HeaderText="LastName" />
            <asp:BoundField DataField="City" HeaderText="City"  />

            <asp:TemplateField HeaderText="Type">
                <ItemTemplate>
                    <asp:label ID="txtType" runat="server" Text = '<%# Eval("PhoneType") %>'></asp:label>
                </ItemTemplate>
            </asp:TemplateField>

            <asp:TemplateField HeaderText="Phone">
                <ItemTemplate>
                    <asp:label ID="txtPhone" runat="server" Text = '<%# Eval("Phone") %>' ></asp:label>
                </ItemTemplate>
            </asp:TemplateField>
        </Columns>  
    </asp:GridView>
</div>

(这就是我使用 gridview 的意思 - 放入 nice plane jane asp.net 控件(在这种情况下是一个标签),你必须用 itemtemplate 包围它 - 我发现有点如果你有“很多”列会很痛苦。所以如果你需要很多自定义列,那么列表视图会更好。但到目前为止我们还不错。

所以,我们的代码现在变成了这样: 我们只需拉取每个 child 行的数据,并将此 child table 的结果推入我们添加的两个新列中。

这个:

   void LoadGrid()
    {
        DataTable rst = MyRst("SELECT * from People Order by FirstName");

        rst.Columns.Add("PhoneType", typeof(string));
        rst.Columns.Add("Phone", typeof(string));

        foreach (DataRow OneRow in rst.Rows)
        {
            // get child rows for this main row
            DataTable ChildRows = MyRst("SELECT * from Phones where People_ID = " + OneRow["ID"]);

            foreach (DataRow ChildRow in ChildRows.Rows)
            {
                if (OneRow["PhoneType"].ToString() != "")
                {
                    // start new line in this cell
                    OneRow["PhoneType"] += "<br/>";
                    OneRow["Phone"] += "<br/>";
                }
                OneRow["PhoneType"] += ChildRow["PhoneType"].ToString();
                OneRow["Phone"] += ChildRow["PhoneNumber"].ToString();
            }
        }

        GridView1.DataSource = rst;
        GridView1.DataBind();
    }

所以这是一些额外的循环代码!事实上,在大多数情况下,如果我们正在编写循环代码,那么我们往往会以错误的方式这样做。

我们现在有这个:

这也取决于我们想在这里做什么。假设我们需要每个 child phone 号码的点击按钮(可能还包括一封电子邮件)。所以如果我们必须点击 child email/phone 来采取行动,那么显然上述方法有点快速和肮脏,但不会让我们能够点击特定的 phone 编号行到 select.

所以,如果出于某种原因我们想要一个点击事件,或者按钮到 select 给定的 child 行?

然后我会转向嵌套 child 网格。因此,我们不需要循环来填充 child 数据 - 但实际上会以类似的方式将新网格绑定到我们构建主网格的方式。

如前所述,我可能会跳转到主网格的列表视图。

如果您愿意,我可以并且会 post 一个在列表视图中嵌套网格视图的工作示例 - 比如说用一个额外的按钮点击 child 行作为“我们可能想采取的行动”。