Home > Net >  Nested asp repeaters using complex object
Nested asp repeaters using complex object

Time:10-29

I have a complex object with nested collection of objects that need to be binded to a repeater/shown in a table. Object shows 14 items:

enter image description here

I would like to display data as such:

enter image description here

Objects looks like:

enter image description here

I need to show "FieldName" in table/column header and "FieldValue" in the table rows. Notice item 3 and 5 are both "Phone Number" FieldName. So I need PhoneNumber to show once (in column header) then there would be 2 rows showing 2 phonenumber FieldValues (like in the table screenshot). So there could be multiple sets of items in the collection..e.g. N number of Contacts, etc. I am not sure how to set up the repeaters (or how many to use) and /or how to set up repeaters OnItemDataBound. Any ideas or pseudo code pointed in the right direction would be super helpful.

Update: Calling API response returns List (i.e. Contacts)

Attributes objects (examples):

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"

Markup needs to show table:

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

CodePudding user response:

Ok, so the question is simple this:

I have a list of names, and for each name, I may have a child table of phone numbers. They might have no phone numbers, they might have 5 phone numbers.

What is a good way to display this master reocrd, and then a repeating child record for each row?

There are LOT of ways to do this. I would often suggest that for the main display of rows, we use a listview, and then nest for the child rows a gridview.

However, as always, which road will depend on how much data, and how many rows (complex) the child data display has to be?

But, lets go with a simple grid. As such a setup becomes more complex, then I STRONG suggest going with a listview. The listview is better since it FAR more flexiblilty in terms of layout. and while the "set up" is a bit more markup, adding new columns and complex controls for each row is MUCH better. In the long run the listview in fact can wind up being less overall markup.

However, we only have two extra columns of "child repeating" data.

So we have in effect this:

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

So, lets whip up a grid with the table people. I will often fire up and let the wizard create that grid. I THEN blow out (delete) the data source in the web page, and then start using the delete keys to remove extra junk.

So, we now have this markup:

<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>

And our code to fill this grid is this:

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

And our output is this:

enter image description here

Ok, so we have this setup of course:

enter image description here

now, we could have done a query join, but then again, that would repeat the main row for each child row (and then we would have to "hide" that.

So, lets go to the markup and add the two rows. (PhoneType and PhoneNumber).

So we now have this:

<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>

(this is what I mean by using gridview - to drop in nice plane jane asp.net controls (a label in this case), you have to surround it with the itemtemplate - I find that a bit of pain if you have "lots" of columns. so if you need lots of custom columns then a list view is better. but we not too bad so far.

So, our code now becomes this: We simply pull the data for each child row, and shove the results of this child table into our two new columns we add.

this:

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

So this was a bit of extra code to loop! In fact, in MOST cases, if we are writing looping code, then we tend to be doing this the wrong way.

We now have this:

enter image description here

And it also depends on what we want to do here. Say we needed a click button for each of the child phone numbers (maybe a email also included). So if we had to click on the child email/phone to take action, then obvious the above approach is a bit quick and dirty, but would not give us the ability to click on a particular phone number row to select.

So, say if for some reason we wanted a click event, or button to select the given child row from this display?

Then I would then move over to nesting a child grid. We would not thus need a loop to fill the child data - but in fact would bind a new grid in a simular fashion to how we built the main grid.

and as noted, I would probably jump over to a list view for the main grid.

If you wish, I can and will post a working example of a nesting a grid view inside of list view - say with a extra button to click on the child row as a "action" we might want to take.

  • Related