Link to home
Start Free TrialLog in
Avatar of BYU-Studies
BYU-StudiesFlag for United States of America

asked on

Nested Repeater in ASP.NET

The current table of contents for this journal page has columns Title, Authored By, Section, and Page #. I am populating this table with a repeater, and the code-behind is written in ASP.NET. My problem is simple - I need to split this table up by section and make the section name the title for each table. Each journal may have different sections, so the section titles need to be dynamic.

I got the HTML part to work (I think), but I am now getting an error in the code-behind. It says 'column' argument cannot be null. The error is on the line where I do:

ds.Relations.Add("myrelation", ds.Tables("Sections").Columns("titleId"), ds.Tables("TOC").Columns("titleId")).

I'm not really sure what this line of code is doing. I copied it from http://support.microsoft.com/kb/306154. Do I have the right information in the columns argument?
'ON SHOWTITLE.ASPX.VB
'Create the connection and DataAdapter for the Authors table.
                Dim connString As String = Connection.getConnString
                Dim cnn As New SqlConnection
                cnn = New SqlConnection(connString)
                Dim cmd1 As New SqlDataAdapter("SELECT _Title.title AS title, _Section.name AS sectionName, _Title.titleId AS titleId, _Article.articleId, _Article.issueId, _Article.sectionId, _Article.page FROM _Article INNER JOIN _Section ON _Article.sectionId = _Section.sectionId INNER JOIN _Title ON _Article.titleIdFk = _Title.titleId WHERE (_Article.issueId = 167) ORDER BY _Article.sectionId, _Article.page", cnn)
 
                'Create and fill the DataSet.
                Dim ds As New DataSet()
                cmd1.Fill(ds, "sections")
 
                'Create a second DataAdapter for the Titles table.
                Dim cmd2 As New SqlDataAdapter("SELECT DISTINCT _Section.name AS sectionName FROM _Article INNER JOIN _Section ON _Article.sectionid = _Section.sectionId INNER JOIN _Title ON _Article.titleidfk = _Title.titleId WHERE(_Article.issueid = 167) ORDER BY sectionName", cnn)
                cmd2.Fill(ds, "TOC")
 
                'Create the relation bewtween the Sections and TOC tables.
                ds.Relations.Add("myrelation", ds.Tables("Sections").Columns("titleId"), ds.Tables("TOC").Columns("titleId"))
 
                'Bind the Authors table to the parent Repeater control, and call DataBind.
                parentRepeater.DataSource = ds.Tables("sections")
                Page.DataBind()
 
                cnn.Close()
 
 
 
<%--ON SHOWTITLE.ASPX--%>
        <div style="padding-bottom:25px;">
            <asp:Repeater ID="parentRepeater" runat="server">
                <ItemTemplate>
                    <b>
                        <%#DataBinder.Eval(Container.DataItem, "sectionName")%>
                    </b>
                    <br>
                    <asp:Repeater ID="child" DataSource='<%# DirectCast(Container.DataItem, System.Data.DataRowView).Row.GetChildRows("myrelation") %>'
                        runat="server">
                        <ItemTemplate>
                            <table>
                                <tr>
                                    <td>
                                        <a href='showTitle.aspx?title=<%# Container.DataItem("titleid") %>'><%# Container.DataItem("title") %></a>
                                    </td>
                                    <td align="center">
                                        <%#Container.DataItem("people")%>
                                    </td>
                                    <td align="center">
                                        <%#Container.DataItem("page")%>
                                    </td>
                                </tr>
                            </table>
                        </ItemTemplate>
                    </asp:Repeater>
                </ItemTemplate>
            </asp:Repeater>
        </div>

Open in new window

table-of-contents.jpg
table-of-contents-2.jpg
Avatar of rbradberry
rbradberry

I answered this in your last post.

your second SQL statement (cmd2) needs to return the column "titleId".  you can't make a relation to a column that isn't there
Avatar of BYU-Studies

ASKER

Great. I am getting the results on the page without any errors. My last problem is that I have multiple of the same section heading and only 1 row per heading. I need to group the results by section. How do I do that?
'Create the connection and DataAdapter for the Authors table.
                Dim connString As String = Connection.getConnString
                Dim cnn As New SqlConnection
                cnn = New SqlConnection(connString)
                Dim cmd1 As New SqlDataAdapter("SELECT DISTINCT _Section.name AS sectionName, _Title.title AS title, _Title.titleId AS titleId, _Article.page AS page FROM _Article INNER JOIN _Section ON _Article.sectionid = _Section.sectionId INNER JOIN _Title ON _Article.titleidfk = _Title.titleId WHERE(_Article.issueid = 167) ORDER BY sectionName", cnn)
 
                'Create and fill the DataSet.
                Dim ds As New DataSet()
                cmd1.Fill(ds, "sections")
 
                'Create a second DataAdapter for the Titles table.
                Dim cmd2 As New SqlDataAdapter("SELECT _Title.title AS title, _Section.name AS sectionName, _Title.titleId AS titleId, _Article.articleId, _Article.issueId, _Article.sectionId, _Article.page FROM _Article INNER JOIN _Section ON _Article.sectionId = _Section.sectionId INNER JOIN _Title ON _Article.titleIdFk = _Title.titleId WHERE (_Article.issueId = 167) ORDER BY _Article.sectionId, _Article.page", cnn)
                cmd2.Fill(ds, "TOC")
                ds.Tables("TOC").Columns.Add("people", GetType(String))
                Dim TOCRow As DataRow
                For Each TOCRow In ds.Tables("TOC").Rows
                    Dim aTitle As New Title
                    aTitle.load(TOCRow("titleid"))
                    TOCRow("people") = aTitle.getPeopleString
                    aTitle.dispose()
                Next
 
                'Create the relation bewtween the Sections and TOC tables.
                ds.Relations.Add("myrelation", ds.Tables("Sections").Columns("titleId"), ds.Tables("TOC").Columns("titleId"))
 
                'Bind the Authors table to the parent Repeater control, and call DataBind.
                parentRepeater.DataSource = ds.Tables("sections")
                Page.DataBind()
 
                cnn.Close()
 
 
 
 
 
<%--ON SHOWTITLE.ASPX--%>
        <div style="padding-bottom: 25px;">
            <asp:Repeater ID="parentRepeater" runat="server">
                <ItemTemplate>
                    <b>
                        <%#DataBinder.Eval(Container.DataItem, "sectionName")%>
                    </b>
                    <br>
                    <asp:Repeater ID="child" DataSource='<%# DirectCast(Container.DataItem, System.Data.DataRowView).Row.GetChildRows("myrelation") %>'
                        runat="server">
                        <HeaderTemplate>
                            <table width="720px" border="0" cellpadding="3">
                                <tr bgcolor="#dddddd">
                                    <td align="center">
                                        <b>Title</b>
                                    </td>
                                    <td align="center" width="200px">
                                        <b>Authored By</b>
                                    </td>
                                    <td align="center" width="50px">
                                        <b>Page #</b>
                                    </td>
                                </tr>
                        </HeaderTemplate>
                        <ItemTemplate>
                            <tr>
                                <td>
                                    <a href='showTitle.aspx?title=<%# Container.DataItem("titleid") %>'>
                                        <%# Container.DataItem("title") %>
                                    </a>
                                </td>
                                <td align="center">
                                    <%#Container.DataItem("people")%>
                                </td>
                                <td align="center">
                                    <%#Container.DataItem("page")%>
                                </td>
                            </tr>
                        </ItemTemplate>
                        <AlternatingItemTemplate>
                            <tr bgcolor="#dddddd">
                                <td>
                                    <a href='showTitle.aspx?title=<%# Container.DataItem("titleid") %>'>
                                        <%# Container.DataItem("title") %>
                                    </a>
                                </td>
                                <td width="200px" align="center">
                                    <%#Container.DataItem("people")%>
                                </td>
                                <td width="50px" align="center">
                                    <%#Container.DataItem("page")%>
                                </td>
                            </tr>
                        </AlternatingItemTemplate>
                        <FooterTemplate>
                            </table>
                        </FooterTemplate>
                    </asp:Repeater>
                </ItemTemplate>
            </asp:Repeater>
        </div>

Open in new window

table-of-contents-3.jpg
your code displaying the section name as well as the code displaying "title", "authored by" and "page #" should all be in the HeaderTemplate of the Parent Repeater
I did that, and I lost all of the section headings. I'm wondering if the parent repeater is working at all. What am I doing wrong? Do you think it's in the HTML, or could it be in the code-behind?
<%--ON SHOWTITLE.ASPX--%>
        <div style="padding-bottom: 25px;">
            <asp:Repeater ID="parentRepeater" runat="server">
                <HeaderTemplate>
                    <b>
                        <%#DataBinder.Eval(Container.DataItem, "sectionName")%>
                    </b>
                    <br>
                    <table width="720px" border="0" cellpadding="3">
                        <tr bgcolor="#dddddd">
                            <td align="center">
                                <b>Title</b>
                            </td>
                            <td align="center" width="200px">
                                <b>Authored By</b>
                            </td>
                            <td align="center" width="50px">
                                <b>Page #</b>
                            </td>
                        </tr>
                </HeaderTemplate>
                <ItemTemplate>
                    <asp:Repeater ID="child" DataSource='<%# DirectCast(Container.DataItem, System.Data.DataRowView).Row.GetChildRows("myrelation") %>'
                        runat="server">
                        <ItemTemplate>
                            <tr>
                                <td>
                                    <a href='showTitle.aspx?title=<%# Container.DataItem("titleid") %>'>
                                        <%# Container.DataItem("title") %>
                                    </a>
                                </td>
                                <td align="center">
                                    <%#Container.DataItem("people")%>
                                </td>
                                <td align="center">
                                    <%#Container.DataItem("page")%>
                                </td>
                            </tr>
                        </ItemTemplate>
                        <AlternatingItemTemplate>
                            <tr bgcolor="#dddddd">
                                <td>
                                    <a href='showTitle.aspx?title=<%# Container.DataItem("titleid") %>'>
                                        <%# Container.DataItem("title") %>
                                    </a>
                                </td>
                                <td width="200px" align="center">
                                    <%#Container.DataItem("people")%>
                                </td>
                                <td width="50px" align="center">
                                    <%#Container.DataItem("page")%>
                                </td>
                            </tr>
                        </AlternatingItemTemplate>
                    </asp:Repeater>
                </ItemTemplate>
                <FooterTemplate>
                    </table>
                </FooterTemplate>
            </asp:Repeater>
        </div>

Open in new window

table-of-contents-4.jpg
try putting the parentRepeater header template into the childRepeater header template
That's kind of like what I had before, but it's still not displaying the section headers. Here's what I'm getting:
<%--ON SHOWTITLE.ASPX--%>
        <div style="padding-bottom: 25px;">
            <asp:Repeater ID="parentRepeater" runat="server">
                <ItemTemplate>
                    <asp:Repeater ID="child" DataSource='<%# DirectCast(Container.DataItem, System.Data.DataRowView).Row.GetChildRows("myrelation") %>'
                        runat="server">
                        <HeaderTemplate>
                            <b>
                                <%#DataBinder.Eval(Container.DataItem, "sectionName")%>
                            </b>
                            <br>
                            <table width="720px" border="0" cellpadding="3">
                                <tr bgcolor="#dddddd">
                                    <td align="center">
                                        <b>Title</b>
                                    </td>
                                    <td align="center" width="200px">
                                        <b>Authored By</b>
                                    </td>
                                    <td align="center" width="50px">
                                        <b>Page #</b>
                                    </td>
                                </tr>
                        </HeaderTemplate>
                        <ItemTemplate>
                            <tr>
                                <td>
                                    <a href='showTitle.aspx?title=<%# Container.DataItem("titleid") %>'>
                                        <%# Container.DataItem("title") %>
                                    </a>
                                </td>
                                <td align="center">
                                    <%#Container.DataItem("people")%>
                                </td>
                                <td align="center">
                                    <%#Container.DataItem("page")%>
                                </td>
                            </tr>
                        </ItemTemplate>
                        <AlternatingItemTemplate>
                            <tr bgcolor="#dddddd">
                                <td>
                                    <a href='showTitle.aspx?title=<%# Container.DataItem("titleid") %>'>
                                        <%# Container.DataItem("title") %>
                                    </a>
                                </td>
                                <td width="200px" align="center">
                                    <%#Container.DataItem("people")%>
                                </td>
                                <td width="50px" align="center">
                                    <%#Container.DataItem("page")%>
                                </td>
                            </tr>
                        </AlternatingItemTemplate>
                    </asp:Repeater>
                </ItemTemplate>
                <FooterTemplate>
                    </table>
                </FooterTemplate>
            </asp:Repeater>
        </div>

Open in new window

table-of-contents-5.jpg
so it seems the header is supposed to be in the parent repeater.
check to be sure that your parent dataset is returning one row per section

also, if you can post what the return data looks like for both datasets, that will also be helpful
How do I check if my parent dataset is returning one row per section? Here's the return data for both datasets.
sections-dataset.jpg
TOC-dataset.jpg
ok, your problem is that your parent dataset is returning too many records.  instead of creating the relationship on titleId create the relationship on sectionId.  in that SQL Query you want to only return sectionName and sectionID, maybe something like this:

SELECT DISTINCT name AS sectionName, Id AS sectionId FROM _Section

Then the child select statement might look something like this:

SELECT _Title.title AS title, _Title.sectionID as titleSectionId, _Title.titleId AS titleId, _Article.articleId, _Article.issueId, _Article.sectionId, _Article.page FROM _Article INNER JOIN _Title ON _Article.titleIdFk = _Title.titleId WHERE (_Article.issueId = 167) ORDER BY _Article.sectionId, _Article.page

then your relationship might be something like:
ds.Relations.Add("myrelation", ds.Tables("Sections").Columns("sectionID"), ds.Tables("TOC").Columns("titleSectionID"))

with each data set you only want to return the columns you require.  the relationship is based on the sectionID not the titleID.  This is why it repeats itself, because your parent dataset is returning a record for every title when you want it to return a record for every section.
I see what you're saying. There's still a problem with the second SQL statement because sectionId is not in the title table, so it gave a compilation error. I attached a picture of the class diagram for these tables to show the relationships.

I tried changing the SQL to:
SELECT     _Title.title, _Article.sectionid, _Title.titleId, _Article.articleid, _Article.issueid, _Article.sectionid AS Expr1, _Article.page
FROM         _Title INNER JOIN
                      _Article ON _Title.titleId = _Article.titleidfk INNER JOIN
                      _Section ON _Article.sectionid = _Section.sectionId
WHERE     (_Article.issueid = 167)
ORDER BY _Article.sectionid, _Article.page

and then I changed the relationship to:
ds.Relations.Add("myrelation", ds.Tables("Sections").Columns("sectionID"), ds.Tables("TOC").Columns("sectionID"))

and it came out like the picture shown. Any ideas?


DatabaseDiagram.jpg
table-of-contents-6.jpg
did you put the headertemplate back into the parent repeater?
No, I didn't put the headertemplate back into the parent repeater. I just did that, but the section titles still do not appear. In fact, I can put whatever I want in place of "sectionName" in the headertemplate in the parent repeater and it doesn't do anything. It doesn't throw any errors and it doesn't change the output. However, if I do anything to the Title, Authored By, Section, Page # part, I see changes. Here's the current code and output.

I included the sectionName column back into the table to make it easy to see where the divisions should take place. I find it interesting that one of the rows toward the bottom doesn't alternate colors.
<%--ON SHOWTITLE.ASPX--%>
        <div style="padding-bottom: 25px;">
            <asp:Repeater ID="parentRepeater" runat="server">
                <HeaderTemplate>
                    <b>
                        <%#DataBinder.Eval(Container.DataItem, "sectionName")%>
                    </b>
                    <br>
                    <table width="720px" border="0" cellpadding="3">
                        <tr bgcolor="#dddddd">
                            <td align="center">
                                <b>Title</b>
                            </td>
                            <td align="center" width="200px">
                                <b>Authored By</b>
                            </td>
                            <td align="center" width="100px">
                                <b>Section</b>
                            </td>
                            <td align="center" width="50px">
                                <b>Page #</b>
                            </td>
                        </tr>
                </HeaderTemplate>
                <ItemTemplate>
                    <asp:Repeater ID="child" DataSource='<%# DirectCast(Container.DataItem, System.Data.DataRowView).Row.GetChildRows("myrelation") %>'
                        runat="server">
                        <ItemTemplate>
                            <tr>
                                <td>
                                    <a href='showTitle.aspx?title=<%# Container.DataItem("titleid") %>'>
                                        <%# Container.DataItem("title") %>
                                    </a>
                                </td>
                                <td align="center">
                                    <%#Container.DataItem("people")%>
                                </td>
                                <td align="center">
                                    <%#Container.DataItem("sectionName")%>
                                </td>
                                <td align="center">
                                    <%#Container.DataItem("page")%>
                                </td>
                            </tr>
                        </ItemTemplate>
                        <AlternatingItemTemplate>
                            <tr bgcolor="#dddddd">
                                <td>
                                    <a href='showTitle.aspx?title=<%# Container.DataItem("titleid") %>'>
                                        <%# Container.DataItem("title") %>
                                    </a>
                                </td>
                                <td width="200px" align="center">
                                    <%#Container.DataItem("people")%>
                                </td>
                                <td align="center">
                                    <%#Container.DataItem("sectionName")%>
                                </td>
                                <td width="50px" align="center">
                                    <%#Container.DataItem("page")%>
                                </td>
                            </tr>
                        </AlternatingItemTemplate>
                    </asp:Repeater>
                </ItemTemplate>
                <FooterTemplate>
                    </table>
                </FooterTemplate>
            </asp:Repeater>
        </div>
 
 
 
'ON SHOWTITLE.ASPX.VB
'Create the connection and DataAdapter for the Authors table.
                Dim connString As String = Connection.getConnString
                Dim cnn As New SqlConnection
                cnn = New SqlConnection(connString)
                Dim cmd1 As New SqlDataAdapter("SELECT DISTINCT name AS sectionName, sectionId AS sectionId FROM _Section ORDER BY sectionName", cnn)
 
                'Create and fill the DataSet.
                Dim ds As New DataSet()
                cmd1.Fill(ds, "sections")
 
                'Create a second DataAdapter for the Titles table.
                Dim cmd2 As New SqlDataAdapter("SELECT _Title.title, _section.name as sectionName, _Article.sectionid, _Title.titleId, _Article.articleid, _Article.issueid, _Article.sectionid AS Expr1, _Article.page FROM _Title INNER JOIN _Article ON _Title.titleId = _Article.titleidfk INNER JOIN _Section ON _Article.sectionid = _Section.sectionId WHERE(_Article.issueid = 167) ORDER BY _section.sectionName", cnn)
                cmd2.Fill(ds, "TOC")
                ds.Tables("TOC").Columns.Add("people", GetType(String))
                Dim TOCRow As DataRow
                For Each TOCRow In ds.Tables("TOC").Rows
                    Dim aTitle As New Title
                    aTitle.load(TOCRow("titleid"))
                    TOCRow("people") = aTitle.getPeopleString
                    aTitle.dispose()
                Next
 
                'Create the relation bewtween the Sections and TOC tables.
                ds.Relations.Add("myrelation", ds.Tables("Sections").Columns("sectionID"), ds.Tables("TOC").Columns("sectionID"))
 
                'Bind the Authors table to the parent Repeater control, and call DataBind.
                parentRepeater.DataSource = ds.Tables("sections")
                Page.DataBind()
 
                cnn.Close()

Open in new window

table-of-contents-7.jpg
my apologies, the reason it is not showing is that there is no data when the header is initialized.  try putting the header information in the itemtemplate of the parent repeater.  i dont mean to run you around, im am just trying to help while troubleshooting other stuff at work. haha
Thanks. It's so close to working now, but the section headers are off by 1.
<%--ON SHOWTITLE.ASPX--%>
        <div style="padding-bottom: 25px;">
            <asp:Repeater ID="parentRepeater" runat="server">
                <ItemTemplate>
                    <br />
                    <b>
                        <%#DataBinder.Eval(Container.DataItem, "sectionName")%>
                    </b>
                    <br>
                    <table width="720px" border="0" cellpadding="3">
                        <tr bgcolor="#dddddd">
                            <td align="center">
                                <b>Title</b>
                            </td>
                            <td align="center" width="200px">
                                <b>Authored By</b>
                            </td>
                            <td align="center" width="100px">
                                <b>Section</b>
                            </td>
                            <td align="center" width="50px">
                                <b>Page #</b>
                            </td>
                        </tr>
                    <asp:Repeater ID="child" DataSource='<%# DirectCast(Container.DataItem, System.Data.DataRowView).Row.GetChildRows("myrelation") %>'
                        runat="server">
                        <ItemTemplate>
                            <tr>
                                <td>
                                    <a href='showTitle.aspx?title=<%# Container.DataItem("titleid") %>'>
                                        <%# Container.DataItem("title") %>
                                    </a>
                                </td>
                                <td align="center">
                                    <%#Container.DataItem("people")%>
                                </td>
                                <td align="center">
                                    <%#Container.DataItem("sectionName")%>
                                </td>
                                <td align="center">
                                    <%#Container.DataItem("page")%>
                                </td>
                            </tr>
                        </ItemTemplate>
                        <AlternatingItemTemplate>
                            <tr bgcolor="#dddddd">
                                <td>
                                    <a href='showTitle.aspx?title=<%# Container.DataItem("titleid") %>'>
                                        <%# Container.DataItem("title") %>
                                    </a>
                                </td>
                                <td width="200px" align="center">
                                    <%#Container.DataItem("people")%>
                                </td>
                                <td align="center">
                                    <%#Container.DataItem("sectionName")%>
                                </td>
                                <td width="50px" align="center">
                                    <%#Container.DataItem("page")%>
                                </td>
                            </tr>
                        </AlternatingItemTemplate>
                    </asp:Repeater>
                </ItemTemplate>
                <FooterTemplate>
                    </table>
                </FooterTemplate>
            </asp:Repeater>
        </div>

Open in new window

table-of-contents-8.jpg
I also made a slight change to the 1st SQL query in the code-behind so that it would only get the sections for issueId 167. I'll go back and get it to pull the table of contents for whatever journal the person is actually looking at - I just did that for testing purposes. Just in case you need it, here's the latest code-behind.
'Create the connection and DataAdapter for the Authors table.
                Dim connString As String = Connection.getConnString
                Dim cnn As New SqlConnection
                cnn = New SqlConnection(connString)
                Dim cmd1 As New SqlDataAdapter("SELECT DISTINCT name AS sectionName, _Section.sectionId AS sectionId FROM _Section INNER JOIN _Article ON _Article.sectionId = _Section.sectionId WHERE _Article.issueId = 167 ORDER BY sectionName", cnn)
 
                'Create and fill the DataSet.
                Dim ds As New DataSet()
                cmd1.Fill(ds, "sections")
 
                'Create a second DataAdapter for the Titles table.
                Dim cmd2 As New SqlDataAdapter("SELECT _Title.title, _section.name as sectionName, _Article.sectionid, _Title.titleId, _Article.articleid, _Article.issueid, _Article.sectionid AS Expr1, _Article.page FROM _Title INNER JOIN _Article ON _Title.titleId = _Article.titleidfk INNER JOIN _Section ON _Article.sectionid = _Section.sectionId WHERE(_Article.issueid = 167) ORDER BY _section.sectionName", cnn)
                cmd2.Fill(ds, "TOC")
                ds.Tables("TOC").Columns.Add("people", GetType(String))
                Dim TOCRow As DataRow
                For Each TOCRow In ds.Tables("TOC").Rows
                    Dim aTitle As New Title
                    aTitle.load(TOCRow("titleid"))
                    TOCRow("people") = aTitle.getPeopleString
                    aTitle.dispose()
                Next
 
                'Create the relation bewtween the Sections and TOC tables.
                ds.Relations.Add("myrelation", ds.Tables("Sections").Columns("sectionID"), ds.Tables("TOC").Columns("sectionID"))
 
                'Bind the Authors table to the parent Repeater control, and call DataBind.
                parentRepeater.DataSource = ds.Tables("sections")
                Page.DataBind()
 
                cnn.Close()

Open in new window

Any ideas? I still haven't figured it out...we're so close.
ASKER CERTIFIED SOLUTION
Avatar of rbradberry
rbradberry

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Thanks for all of your help. Your workaround did the trick.