Repeater with a nested DataList

E

Erik

Good Afternoon, I am trying to nest a DataList in a Repeater ItemTemplate,
but am receiving a error stating that the datalist is not defined. The id is
set for the DataList. Is this possible?

The reason for this architecture is to dynamically create the column header
labels and items for the Repeater / DataList. I am trying to use the same
Repeater / DataList for several different dataset, all with different fields
and different number of fields. Any other ideas are also greatly appreciated.
 
P

Phillip Williams

Hi Erik,

The nested datalist that you described is only defined within the
RepeaterItem. You can access it through the RepeaterItem.Controls
collection, e.g.,

private void repeater1_ItemDataBound(object sender, RepeaterItemEventArgs e)
{
if (e.Item.ItemType == ListItemType.Item )
{
DataList dl = e.Item.FindControl ("DataList1");
if (dl !=null)
{
//write code to assign the datasource for the datalist
}
}
}

If you want to look at demos of using nested templated databound controls, I
have several samples: http://www.societopia.net/Samples/
 
E

Erik

Thank you for the response Phillip. I am new to Repeaters and DataLists, and
am encountering an error when using the code provided. The FindControl is
failing. Do you see something wrong with what I am doing? Below is the
code. Thanks again,
Erik

HTML:
<asp:Repeater id="DetailRepeater" runat="server">
<ItemTemplate>
<asp:DataList id="dlstDetail" runat="server"></asp:DataList>
</ItemTemplate>
</asp:Repeater>

VB.Net:
Private Sub DetailRepeater_ItemDataBound(ByVal sender As Object, ByVal e
As System.Web.UI.WebControls.RepeaterItemEventArgs) Handles
DetailRepeater.ItemDataBound
If e.Item.ItemType = ListItemType.Item Then
Dim dl As DataList

dl = New DataList

dl = e.Item.FindControl("dlstDetail")
If Not dl Is Nothing Then

'write code to assign the datasource for the datalist
dl.ItemTemplate = New DatalistLabelColumn

dl.DataSource = oData.Tables(0).DefaultView

dl.DataBind()

End If
End If

End Sub

Public Class DatalistLabelColumn

Implements ITemplate

Public Sub InstantiateIn(ByVal container As Control) Implements
System.Web.UI.ITemplate.InstantiateIn

Dim label1 As New Label

AddHandler label1.DataBinding, AddressOf Me.BindLabelColumn

container.Controls.Add(label1)

End Sub 'InstantiateIn


Public Sub BindLabelColumn(ByVal sender As Object, ByVal e As
EventArgs)

Dim lbl As Label = CType(sender, Label)

Dim container As DataListItem = CType(lbl.NamingContainer,
DataListItem)

Dim strVals As String = "<tr><td>" +
Convert.ToString(DataBinder.Eval(CType(container, DataListItem).DataItem,
"CustomerNo")) + "</td>" + _
"<td>" + Convert.ToString(DataBinder.Eval(CType(container,
DataListItem).DataItem, "ContactNo")) + "</td>" + _
"<td>" + Convert.ToString(DataBinder.Eval(CType(container,
DataListItem).DataItem, "CompanyName")) + "</td>" + _
"<td>" + Convert.ToString(DataBinder.Eval(CType(container,
DataListItem).DataItem, "ContactName")) + "</td>" + _
"<td>" + Convert.ToString(DataBinder.Eval(CType(container,
DataListItem).DataItem, "ACTContact.CustomerClass")) + "</td>" + _
"<td>" + Convert.ToString(DataBinder.Eval(CType(container,
DataListItem).DataItem, "Phone")) + "</td>" + _
"<td>" + Convert.ToString(DataBinder.Eval(CType(container,
DataListItem).DataItem, "CallFrequency")) + "</td></tr>"

lbl.Text = strVals

End Sub 'BindLabelColumn

End Class 'DatalistLabelColumn
 
E

Erik

I accidentally posted before I could ask if there is a better way to have a
dynamic repeater / datalist, headers and items?
 
P

Phillip Williams

Hi Erik,

I adjusted the code that you gave to me earlier

As for the question of "a better way", it really depends on the complexity
of the business case you are trying to implement. If the case is simple, I
would prepare my datasets to represent the structure I wish to display and
then use nested datagrids as I did in these 2 examples:
http://www.societopia.net/Samples/DataGrid_Hierarchy.aspx
http://www.societopia.net/Samples/DataList_Hierarchy.aspx

In more complex scenarios where there are ItemTemplates containing other
server controls, I usually have a certain number of possible scenarios for
which I design separate Templates, e.g.
case 1:
dl.ItemTemplate = New Template1(ListItemType.Item)
case 2:
dl.ItemTemplate = New Template2(ListItemType.Item)
and so on.
 
E

Erik

Thank you again for the post and willingness to help me out. I keep running
into errors when trying to adapt the sample provided. I am new to Repeaters
/ DataLists, and can't use a DataGrid because of ViewState. Unfortunately, I
also don't have experience with custom classes.

Here is my adapted code. As you can see, everything for the datalist is
driven by control / screen values and the dataset. The number of Columns,
Column Names, and record count will vary.

When I trace through in debug, everything appears to be assigned properly.
However, when the page is rendered, an error is presented. The current error
is that the sql connection sqlconctxcontrol has not been initialized in
BindLabelColumn.

HTML: As provided.

VB.Net:
Public Function BuildContacts(ByRef sRMWCUniqueId As String, ByRef
sCustomerClass As String, ByRef sRecordType As String) As String
Dim strSQL As String

strSQL = "SELECT CustomerNo, ContactNo, CompanyName, ContactName,
ACTContact.CustomerClass, Phone, CallFrequency FROM ACTContact " & _
"WHERE RecordManagerWCUniqueID = '" & sRMWCUniqueId.Trim & "' "
& _
"AND RecordType = '" & sRecordType.Trim & "' "

If sCustomerClass.Trim <> "TOTAL" Then
strSQL &= "AND ACTContact.CustomerClass = '" &
sCustomerClass.Trim & "' "
End If

strSQL &= "ORDER BY ACTContact.CustomerClass, ACTContact.CompanyName "

Return strSQL

End Function

Private Function Data_Table() As DataTable
Dim dt As DataTable
If Session("DataGrid_ChildControls") Is Nothing Then
Dim dr As DataRow
Dim i As Integer

'create a DataTable
dt = New DataTable

Dim sSQL As String

sSQL = BuildContacts(lblHiddenRMWCUniqueId.Text,
lblHiddenCustomerClass.Text, lblHiddenRecordType.Text)

Dim oAdpt As SqlDataAdapter
oData = New DataSet

oAdpt = New SqlDataAdapter(sSQL, sqlconCTXControl)
oAdpt.Fill(oData, "ACTContact")

Dim dc As DataColumn

For Each dc In oData.Tables(0).Columns
dt.Columns.Add(New DataColumn(dc.ColumnName))
Next

'Make some rows and put some sample data in
Dim currRows() As DataRow = oData.Tables(0).Select(Nothing,
Nothing, DataViewRowState.CurrentRows)
Dim drDataRow As DataRow

For Each dr In currRows
drDataRow = dt.NewRow()
For i = 0 To oData.Tables(0).Columns.Count - 1
drDataRow(i) = dr(i).ToString.Trim()
Next
dt.Rows.Add(drDataRow)
Next

Session("DataGrid_ChildControls") = dt
Else
dt = CType(Session("DataGrid_ChildControls"), DataTable)
End If

Return dt
End Function

Public Class DatalistLabelColumn
Inherits frmCTXDrillDownRptGrid
Implements ITemplate

Dim TemplateType As ListItemType

Sub New(ByVal type As ListItemType)
TemplateType = type
End Sub
Public Sub InstantiateIn(ByVal container As Control) Implements
System.Web.UI.ITemplate.InstantiateIn

Select Case TemplateType
Case ListItemType.Header
Dim lc As New Literal
lc.Text = "This the table header"
container.Controls.Add(lc)
Case ListItemType.Item
Dim label1 As New Label

AddHandler label1.DataBinding, AddressOf
Me.BindLabelColumn

container.Controls.Add(label1)
Case ListItemType.Footer
Dim lc As New Literal
lc.Text = "This is the table footer"
container.Controls.Add(lc)
End Select

End Sub 'InstantiateIn


Public Sub BindLabelColumn(ByVal sender As Object, ByVal e As
EventArgs)

Dim lbl As Label = CType(sender, Label)

Dim container As DataListItem = CType(lbl.NamingContainer,
DataListItem)

Dim sSQL As String

'sSQL = BuildContacts(lblHiddenCustomerClass.Text,
lblHiddenCustomerClass.Text, lblHiddenRecordType.Text)
sSQL = BuildContacts("eMfmaapV_cac", "D", "Account")

Dim oAdpt As SqlDataAdapter
oData = New DataSet

oAdpt = New SqlDataAdapter(sSQL, sqlconCTXControl)
oAdpt.Fill(oData, "ACTContact")

Dim dc As DataColumn
Dim strvals As String

strvals = "<tr>"

For Each dc In oData.Tables(0).Columns
strvals &= "<td>" +
Convert.ToString(DataBinder.Eval(CType(container, DataListItem).DataItem,
dc.ColumnName)) + "</td>"
Next

strvals &= "</tr>"

lbl.Text = strvals

oAdpt.Dispose()
oData.Dispose()
dc.Dispose()

End Sub 'BindLabelColumn

End Class 'DatalistLabelColumn

Protected Sub DetailRepeater_ItemDataBound(ByVal sender As Object, ByVal
e As System.Web.UI.WebControls.RepeaterItemEventArgs)
If e.Item.ItemType = ListItemType.Item Then
Dim dl As DataList

dl = New DataList

dl = e.Item.FindControl("dlstDetail")
If Not dl Is Nothing Then

'write code to assign the datasource for the datalist
'dl.HeaderTemplate = New
DatalistLabelColumn(ListItemType.Header)
dl.ItemTemplate = New DatalistLabelColumn(ListItemType.Item)
'dl.FooterTemplate = New
DatalistLabelColumn(ListItemType.Footer)
dl.DataSource = New DataView(Data_Table)

dl.DataBind()

End If
End If

End Sub
 
P

Phillip Williams

You are welcome Erik. The error you get though has nothing to do with the
repeater/datalist. The error is saying the connection sqlconCTXControl has
not been initialized. By looking at the code you posted I do not see the
step where you initialized the connection object. Usually one would do
something like this:
Dim sqlconCTXControl As New SqlConnection(connStr)

For further detail on the connection string refer to this link
http://msdn.microsoft.com/library/d...ystemdatasqlclientsqlconnectionclasstopic.asp
 
E

Erik

Shouldn't the connection be inherited from frmCTXDrillDownRptGrid?
Public Class DatalistLabelColumn
Inherits frmCTXDrillDownRptGrid

The connection is initialized in InitializeComponent for class
frmCTXDrillDownRptGrid.

Thanks,
Erik
 
P

Phillip Williams

BTW, one more note about the way you created your data. You do not need to
create a new datatable and copy to it the data that you got from the
dataadapter. You could have simply replaced all that code with

'create a DataTable
Dim sSQL As String
sSQL = BuildContacts(lblHiddenRMWCUniqueId.Text,
lblHiddenCustomerClass.Text, lblHiddenRecordType.Text)
Dim oAdpt As SqlDataAdapter
oData = New DataSet
oAdpt = New SqlDataAdapter(sSQL, sqlconCTXControl)
oAdpt.Fill(oData, "ACTContact")
dt = oData.Tables(0)
Session("DataGrid_ChildControls") = dt
 
P

Phillip Williams

Step through the code using the debugger with break points set on the
connection object initialization step and just before you call the
DataAdapter.Fill method step.

To avoid running out of connections you might consider opening the
connection just before the oAdapt.Fill step and close it immediately after.

Consider also using Try...Catch...Finally statements to trap errors in a
nicely presentable way that gives you meaningful messages about the location
of the error. For more detail on try...catch refer to this link:
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vblr7/html/vastmtrycatchfinally.asp
 
E

Erik

Good Morning Phillip,
Would you please tell me which procedure / function you are referring to
below? Does Session("DataGrid_ChildControls") = dt remain in memory for the
entire session, accessible to everything, or is this stateless?

Also, I set breakpoints for the connection object initialization and before
DataAdapter.Fill is called in Sub BindLabelColumn. The connection object is
being initialized properly, but has a value of Nothing in Sub
BindLabelColumn.

Public Class DatalistLabelColumn is defined in Public Class
frmCTXDrillDownRptGrid. Is this correct?

Thanks again,
Erik
 
E

Erik

Good Morning Phillip,
I have everything working at a basic level with the header column name and
data being displayed based off of the dataset. I also was able to figure out
your suggestion below. Thank you for the suggestion.

One item I can't figure out is the Sub DetailRepeater_ItemDataBound binding
the datalist for each row in the dataset. So if I have 29 rows, the datalist
is bound 29 times, displaying the data 29 times in the dataset.

Do you have any ideas how I can remedy this?

Thanks,
Erik
 
P

Phillip Williams

Hello Erik,

I notice that the method named DetailRepeater_ItemDataBound is binding the
DataList to the same data that you have in the repeater in this line:

dl.DataSource = New DataView(Data_Table)

While this might be a proof of concept for answering the question of this
thread, for the real application, you have to design a data layer with
methods that return data for the child datalist instead of this line of code.
Then you would call this method with different parameter values upon binding
each item in the repeater to get different data.
 
E

Erik

Good Morning Phillip,

Thank you for all your help. I have everything working, and am enhancing
the current version.

Erik
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,580
Members
45,054
Latest member
TrimKetoBoost

Latest Threads

Top