How do I best populate an HTML table in ASP.NET? - asp.net

This is what I've got. It works. But, is there a simpler or better way?
ASPX Page…
<asp:Repeater ID="RepeaterBooks" runat="server">
<HeaderTemplate>
<table class="report">
<tr>
<th>Published</th>
<th>Title</th>
<th>Author</th>
<th>Price</th>
</tr>
</HeaderTemplate>
<ItemTemplate>
<tr>
<td><asp:Literal ID="LiteralPublished" runat="server" /></td>
<td><asp:Literal ID="LiteralTitle" runat="server" /></td>
<td><asp:Literal ID="LiteralAuthor" runat="server" /></td>
<td><asp:Literal ID="LiteralPrice" runat="server" /></td>
</tr>
</ItemTemplate>
<FooterTemplate>
</table>
</FooterTemplate>
</asp:Repeater>
ASPX.VB Code Behind…
Protected Sub Page_Load( ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
Dim db As New BookstoreDataContext
RepeaterBooks.DataSource = From b In db.Books _
Order By b.Published _
Select b
RepeaterBooks.DataBind()
End Sub
Sub RepeaterBooks_ItemDataBound( ByVal sender As Object, ByVal e As System.Web.UI.WebControls.RepeaterItemEventArgs) Handles RepeaterBooks.ItemDataBound
If e.Item.ItemType = ListItemType.Item Or e.Item.ItemType = ListItemType.AlternatingItem Then
Dim b As Book = DirectCast(e.Item.DataItem, Book)
DirectCast(e.Item.FindControl("LiteralPublished"), Literal).Text = "<nobr>" + b.Published.ToShortDateString + "</nobr>"
DirectCast(e.Item.FindControl("LiteralTitle"), Literal).Text = "<nobr>" + TryNbsp(HttpContext.Current.Server.HtmlEncode(b.Title)) + "</nobr>"
DirectCast(e.Item.FindControl("LiteralAuthor"), Literal).Text = "<nobr>" + TryNbsp(HttpContext.Current.Server.HtmlEncode(b.Author)) + "</nobr>"
DirectCast(e.Item.FindControl("LiteralPrice"), Literal).Text = "<nobr>" + Format(b.Price, "c") + "</nobr>"
End If
End Sub
Function TryNbsp(ByVal s As String) As String
If s = "" Then
Return " "
Else
Return s
End If
End Function

#Geoff
That sort of Eval statement was actually added in 2.0, but if performance is important Eval should be avoided since it uses Reflection.
The repeater is a pretty good way of doing it, although it might be faster to generate the table in code:
ASPX Page:
<table class="report" id="bookTable" runat="server">
<tr>
<th>Published</th>
<th>Title</th>
<th>Author</th>
<th>Price</th>
</tr>
</table>
Code Behind:
Protected Sub Page_Load( ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
If Not Page.IsPostback Then
BuildTable()
End If
End Sub
Private Sub BuildTable()
Dim db As New BookstoreDataContext
Dim bookCollection = from b in db.Books _
Order By b.Published _
Select b
Dim row As HtmlTableRow
Dim cell As HtmlTableCell
For Each book As Books In bookCollection
row = New HtmlTableRow()
cell = New HtmlTableCell With { .InnerText = b.Published.ToShortDateString }
row.Controls.Add(cell)
cell = New HtmlTableCell With { .InnerText = TryNbsp(HttpContext.Current.Server.HtmlEncode(b.Title)) }
row.Controls.Add(cell)
cell = New HtmlTableCell With { .InnerText = TryNbsp(HttpContext.Current.Server.HtmlEncode(b.Author))
row.Controls.Add(cell)
cell = New HtmlTableCell With { .InnerText = Format(b.Price, "c") }
row.Controls.Add(cell)
bookTable.Controls.Add(row)
Next
I guess it depends on how important speed is to you. For simplicity's sake I think I would go with the Repeater.

The ListView control introduced with framework 3.5 might be a little bit better solution. Your markup would look like this:
<asp:ListView runat="server" ID="ListView1"
DataSourceID="SqlDataSource1">
<LayoutTemplate>
<table runat="server" id="table1" runat="server" >
<tr runat="server" id="itemPlaceholder" ></tr>
</table>
</LayoutTemplate>
<ItemTemplate>
<tr runat="server">
<td runat="server">
<asp:Label ID="NameLabel" runat="server"
Text='<%#Eval("Name") %>' />
</td>
</tr>
</ItemTemplate>
</asp:ListView>
You'll want to set your data source ID from a public or private property in the code-behind class.

In .Net 3.0+ you can replace your ItemDataBound to the asp:Literal by doing something like this:
<ItemTemplate>
<tr>
<td><%# Eval("published") %></td>
...
where "published" is the name of a field in the data you have bound to the repeater
Edit:
#Alassek: I think the performance hit of reflection is often over-emphasized. Obviously you need to benchmark performance of your app, but the hit of the Eval is likely measured in milliseconds. Unless your app is serving many concurrent hits, this probably isn't an issue, and the simplicity of the code using Eval, along with it being a good separation of the presentation, make it a good solution.

This is what the GridView is for.
<asp:GridView runat="server" DataSourceID="SqlDataSource1">
<Columns>
<asp:BoundField HeaderText="Published" DataField="Published" />
<asp:BoundField HeaderText="Author" DataField="Author" />
</Columns>
</asp:GridView>

I would use a GridView (or DataGrid, if you are using an older version of ASP.NET).
<asp:GridView ID="gvBooks" runat="server" AutoGenerateColumns="False">
<Columns>
<asp:BoundField HeaderText="Published" DataField="Published" />
<asp:BoundField HeaderText="Title" DataField="Title" />
<asp:BoundField HeaderText="Author" DataField="Author" />
<asp:BoundField HeaderText="Price" DataField="Price" />
</Columns>
</asp:GridView>
With some code-behind:
Private Sub gvBooksRowDataBound(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.GridViewRowEventArgs) Handles gvBooks.RowDataBound
Select Case e.Row.RowType
Case DataControlRowType.DataRow
''' Your code here '''
End Select
End Sub
You can bind it in a similar way. The RowDataBound event is what you need.

I agree with Geoff, the only time we use Literals is if we want to do something different with the data.
For example, we might want a DueDate field to say "Today" or "Yesterday" instead of the actual date.

ALassek wrote:
…generate the table in code…
I like the look of that! It seems MUCH less likely to produce a run-time exception due to a typo or field name change.

If you don't need ASP.NET handled edit cabilities I would stay away from the DataGrid and the GridView ... they provide unnecessary bloat.

Related

Pass Text From a List View Controller to a OnClick Sub VB.Net

Is there a way to pass text from a List View controller to a OnClick Subroutine? For example, there's a label in the List View and I've attached a Button in the List View and attached an OnClick Subroutine to it. Now I want to pass the text from the Label over to the OnClick Subroutine. I hope this make sense. Down below is my code:
VB.Net Code:
Protected Sub AmButtonClick(ByVal sender As Object, ByVal e As System.EventArgs)
Dim url As String = CType(Me.AmListViewDetails.FindControl("merchantLink"), Label).Text
Dim sb As New StringBuilder()
sb.Append("<script type = 'text/javascript'>"_
sb.Append("window.open('")
sb.Append(url)
sb.Append("');")
sb.Append("</script>")
ClientScript.RegisterStartupScript(Me.GetType(), "script", sb.ToString())
End Sub
ASP.Net:
<asp:ListView runtat="server" ID="AmListViewDetails" ...>
<ItemTemplate>
<table runat="server">
<tr runat="server">
<td runat="server">
<asp:Label ID="merchantLink" runat="server" Text='<%Eval("Link")%>' />
</td>
</tr>
<tr runat="server">
<td runat="server">
<asp:Button ID="AmBtn" runat="server" Text="Checkout" OnClick="AmButtonClick"/>
</td>
</tr>
</table>
</ItemTemplate>
</asp:ListView>
Add a CommandArgument property to the button. That's going to be the value passed to the event handler. You don't need an OnClick property.
<asp:Button ID="AmBtn" runat="server" Text="Checkout" CommandArgument="???" />
Your event handler would be
Sub AmListViewDetails_ItemCommand(object sender, ListViewCommandEventArgs e)
Dim valuePassedFromListView = Cstr(e.CommandArgument)
End Sub

Unable to Get Element with ID in VB.Net

I have the below code to retrieve the table element when the checkbox is checked.
With this, I could get the table ID. When I try to get the HtmlTable with the ID, it throws the null reference exception.
Once I get the table element with its ID, I need to loop through the rows of that table. Could anyone help me out?
ASPX.VB
Protected Sub CheckBox2_CheckedChanged(sender As Object, e As EventArgs)
Dim tab As String
Dim check As CheckBox = CType(sender, CheckBox)
tab = check.Parent.Parent.Parent.ID
Dim tabb As HtmlControl = Me.FindControl(tab)
Dim myTab As HtmlTable = CType(tabb, HtmlTable)
For Each cell As HtmlTableRow In myTab.Rows
//My Code Here
Next
End Sub
ASPX:
<table id="itemlist" class="mel-table" runat="server">
<tr>
<td>
<asp:CheckBox runat="server" ID="CheckBox2" OnCheckedChanged="CheckBox2_CheckedChanged" AutoPostBack="true" />
</td>
</tr>
</table>
Use the parent element ID under which the table is nested like below.
Protected Sub CheckBox2_CheckedChanged(sender As Object, e As EventArgs)
Dim tab As String
Dim check As CheckBox = CType(sender, CheckBox)
tab = check.Parent.Parent.Parent.ID
Dim myTab As HtmlTable = CType(mydiv.FindControl(tab), HtmlTable)
For Each cell As HtmlTableRow In myTab.Rows
//My Code Here
Next
End Sub
<div id="mydiv" runat="server">
<table id="itemlist" class="mel-table" runat="server">
<tr>
<td>
<asp:CheckBox runat="server" ID="CheckBox2" OnCheckedChanged="CheckBox2_CheckedChanged" AutoPostBack="true" />
</td>
</tr>
</table>
</div>

Update panel automatically clears textboxes

I'm trying to combine an event of automatic printing of messages in case the passwords will not match. For that purpose I'm using an Update panel.
The Error message prints perfectly My problem is that both text-boxes automatically created after it. even thought I don't specify it in the code. I can't understand what have I done wrong.
This is the code for front end:
<asp:TextBox ID="NonPass1" runat="server" TextMode="Password"></asp:TextBox>
<asp:TextBox ID="NonPass2" runat="server" TextMode="Password" autopostback="True"></asp:TextBox>
<asp:UpdatePanel ID="UpdatePanel6" runat="server">
<ContentTemplate>
<asp:Panel ID="Panel6" runat="server">
<asp:Label ID="Label1" class="errorMess" runat="server" Text="The Passwords do not match!!!"></asp:Label>
</asp:Panel>
</ContentTemplate>
<Triggers>
<asp:AsyncPostBackTrigger ControlID="NonPass2" EventName="TextChanged" />
</Triggers>
</asp:UpdatePanel>
this is the back end code(I'm using VB):
Protected Sub NonPass2_TextChanged(ByVal sender As Object, ByVal e As EventArgs) Handles NonPass2.TextChanged
If NonPass1.Text <> NonPass2.Text Then
Panel3.Visible = False
Panel6.Visible = True
Else
Panel3.Visible = False
Panel6.Visible = False
End If
End Sub
maybe you can use javascript functions.
<script>
var t1 = false; // textbox1 onfocus triggered = true;
var t2 = false; // textbox2 onfocus triggered = true;
function clearTBox() {
if (t1 && t2) {
if (document.getElementById("textbox1Name").value != document.getElementById("textbox1Name2").value) {
alert("Insert your code here");
}
}
}
</script>
Heres what i use in c# for this, maybe you can inherit the technique
private void ClearTextBoxes()
{
Action<Control.ControlCollection> func = null;
func = (controls) =>
{
foreach (Control control in controls)
if (control is TextBox)
(control as TextBox).Clear();
else
func(control.Controls);
};
func(Controls);
}
then call cleartextboxes();
Hope that helped :)
what do you mean :
My problem is that both text-boxes automatically created after it
please, make your question more clearance
Try not to use updatepanel,
try this
<asp:TextBox ID="NonPass1" runat="server" TextMode="Password"></asp:TextBox>
<asp:TextBox ID="NonPass2" runat="server" TextMode="Password" autopostback="True"></asp:TextBox>
<div id="Div_Error" runat="server" visible="false" style="width:100%">
<asp:Label ID="Label1" class="errorMess" runat="server" Text="The Passwords do not match!!!"></asp:Label>
and use this in the code behind:
Protected Sub NonPass2_TextChanged(ByVal sender As Object, ByVal e As EventArgs) Handles NonPass2.TextChanged
If NonPass1.Text <> NonPass2.Text Then
Div_Error.visible=true;
Else
Div_Error.visible=false;
End If
End Sub
The only logical reason for the behavior as you described is perhaps, you put the above password boxes inside another UpdatePanel.
Therefore, the password boxes will be reloaded on postbacks (textchanged event), and TextBox of type Password do not retain its value after postbacks for security reason.
Though, if security is not a concern for you, there is a workaround to 'avoid' the password textboxes from being cleared on postbacks, by reassigning their values every time postbacks occur. Just include the following codes in the page Load event.
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
NonPass1.Attributes.Add("value", NonPass1.Text)
NonPass2.Attributes.Add("value", NonPass2.Text)
End Sub
this is a simple example:
<table class="mytable" cellspacing="0" style="width: 100%">
<tr>
<td>
<asp:TextBox ID="Txt_Pass" runat="server" ></asp:TextBox>
</td>
<td>
<asp:TextBox ID="Txt_Re_Pass" runat="server" ></asp:TextBox>
</td>
<td width="66%" align="left">
<asp:Button ID="Btn_Filter" runat="server" Text="" Height="22px" />
</td>
</tr>
</table>
<br />
<div id="Div_Error" runat="server" visible="false" style="width:100%">
<asp:Label ID="lbl_Error" runat="server" class="msg">
</asp:Label>
</div>
and in the code behind , use this:
Protected Sub Btn_Filter_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles Btn_Filter.Click
If Trim(Txt_Re_Pass.Text) <> "" Then
Div_Error.Visible = False
if Txt_Pass.Text <> Txt_Re_Pass.Text then
Div_Error.Visible = True
lbl_Error.text="The Passwords do not match!!!""
else
Div_Error.Visible = False
End if
Else
Div_Error.Visible = True
lbl_Error.text="Please re enter your password"
End If
End Sub

how to make a label in a nested repeater visible programmatically

I have two repeaters, one nested in the other. I'm outputting a list of course titles in the parent repeater and the dates of those courses in the child repeater. This part is working fine. But, not all the course titles necessarily have dates available at any given time, so I want to be able to put a message up under each Course title that currently has no dates saying to check back regularly, blah, blah, blah. I've put the label in the footer template of the nested repeater with visibility=false and am trying to set the visibility=true at the appropriate time in the ItemDataBound Sub. Unfortunately, this is not working. I'm not getting any errors, the label just isn't showing up. I'm really hoping someone can help me out with my code or suggest an alternate way to do this. I'm kinda new to asp.net(VB) and am still struggling a bit. I've never tried nesting repeaters before. Here is my code:
.aspx
<asp:Repeater ID="rptTech" runat="server" >
<HeaderTemplate>
<table class="tableCourses">
</HeaderTemplate>
<ItemTemplate>
<tr>
<td>
<strong><asp:Label runat="server" ID="lblCourseName" Text='<%# Eval("CourseName") %>'></asp:Label></strong>
<br />
<asp:Label runat="server" ID="lblCourseSummary" Text='<%# Eval("Summary") %>'></asp:Label>
<br />
<asp:HyperLink ID="hpCourseMaterial" Visible='<%# CheckCourseMaterial(Eval("CourseMaterial")) %>' NavigateUrl='<%# "/files/Portal_Course_Material/" & Eval("CourseMaterial")%>' Text="Download Course Material" runat="server"></asp:HyperLink>
</td>
</tr>
<tr>
<td>
<asp:Repeater ID="rptTechDates" DataSource='<%#Eval("relCourses") %>' runat="server" >
<HeaderTemplate>
<table>
</HeaderTemplate>
<ItemTemplate>
<tr>
<td>
<strong><asp:Label runat="server" ID="lblDate" Text='<%# CDate(Eval("dteDate")).ToString("dd/MM/yyyy") %>'></asp:Label></strong><br />
<asp:Label runat="server" ID="dteTime" Text='<%# "Time: " & CDate(Eval("dteTime2")).ToString("HH:mm") & " EST" %>'></asp:Label><br />
<asp:HyperLink ID="Register" NavigateUrl='<%# "/Test.aspx?intMeetingID2=" & Eval("pkWebinarID")%>' Text="Register" runat="server"></asp:HyperLink>
</td>
</tr>
</ItemTemplate>
<FooterTemplate>
<tr>
<td>
<asp:Label ID="lblNoCourses" Text="There are no course dates available at this time. Please check back regularly to see any updates." runat="server" Visible="False"></asp:Label>
</td>
</tr>
</table>
</FooterTemplate>
</asp:Repeater>
</td>
</tr>
</ItemTemplate>
<FooterTemplate>
</table>
</FooterTemplate>
</asp:Repeater>
And Codebehind:
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
Dim curDate As Date = CDate(Now)
'Create the connection
Dim connstring As String
connstring = ConfigurationManager.ConnectionStrings("LPISQLConn").ConnectionString
Using conn As New SqlConnection(connstring)
Dim cmd As New SqlCommand("SELECT foo1, foo2, foo3 FROM tblFoo", conn)
cmd.CommandType = CommandType.Text
Dim ad As New SqlDataAdapter(cmd)
Dim ds As New DataSet()
ad.Fill(ds)
ds.Relations.Add(New DataRelation("relCourses", ds.Tables(0).Columns("fkcrsID"), ds.Tables(1).Columns("webinarID"), False))
rptTech.DataSource = ds.Tables(0)
rptTech.DataBind()
End Using
End Sub
Protected Sub rptTech_ItemDataBound(ByVal sender As Object, ByVal e As RepeaterItemEventArgs)
If e.Item.ItemType = ListItemType.Item OrElse e.Item.ItemType = ListItemType.AlternatingItem Then
Dim drv As DataRowView = TryCast(e.Item.DataItem, DataRowView)
Dim rptTechDates As Repeater = TryCast(e.Item.FindControl("rptTechDates"), Repeater)
rptTechDates.DataSource = drv.CreateChildView("relCourses")
If rptTechDates.Items.Count < 1 And rptTechDates IsNot Nothing Then
If e.Item.ItemType = ListItemType.Footer Then
Dim lblNoCourses As Label = TryCast(e.Item.FindControl("lblNoCourses"), Label)
If lblNoCourses IsNot Nothing Then
lblNoCourses.Visible = True
End If
End If
Else
rptTechDates.DataBind()
End If
End If
End Sub
I would do the following:
Change your ItemDataBoundEvent for the parent repeater to:
Protected Sub rptTech_ItemDataBound(ByVal sender As Object, ByVal e As RepeaterItemEventArgs)
If e.Item.ItemType = ListItemType.Item OrElse e.Item.ItemType = ListItemType.AlternatingItem Then
Dim drv As DataRowView = TryCast(e.Item.DataItem, DataRowView)
Dim rptTechDates As Repeater = TryCast(e.Item.FindControl("rptTechDates"), Repeater)
rptTechDates.DataSource = drv.CreateChildView("relCourses")
rptTechDates.DataBind()
End If
End Sub
Add this to the markup of your child repeater:
OnItemDataBound="rptTechDates_ItemDataBound"
Now add this:
Protected Sub rptTechDates_ItemDataBound(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.RepeaterItemEventArgs)
If e.Item.ItemType = ListItemType.Footer Then
If DirectCast(DirectCast(e.Item.NamingContainer, Repeater).DataSource, DataTable).Rows.Count = 0 Then
DirectCast(e.Item.FindControl("lblNoCourses"), Label).Visible = True
End If
End If
So, if the number of rows in the child repeater's datasource is 0, then show the label.
EDIT After comment from OP
Protected Sub rptTechDates_ItemDataBound(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.RepeaterItemEventArgs)
If e.Item.ItemType = ListItemType.Footer Then
If DirectCast(DirectCast(e.Item.NamingContainer, Repeater).DataSource, DataView).Count = 0 Then
DirectCast(e.Item.FindControl("lblNoCourses"), Label).Visible = True
End If
End If
You want to search rptTechDates repeater for the lblNoCourses control. The e.Item.FindControl("lblNoCourses") is searching the rptTech repeater which doesn't exist. See this question to search the control in the rptTechDates footer.
Also, you'll want to call Databind() on the repeater after setting it's DataSource
rptTechDates.DataSource = drv.CreateChildView("relCourses")
rptTechDates.Databind()

Repeater control. Using a table that spans rows

The following "FindControl" method fails to find the imgAd control. Any idea why? Could it be the Table that contains it? The intent of the table is to line things up in columns across rows.
<asp:Content ID="Content3" ContentPlaceHolderID="phPageContent" runat="Server">
<asp:Repeater ID="repBanner" runat="server">
<HeaderTemplate>
<table>
</HeaderTemplate>
<ItemTemplate>
<tr>
<td>
<asp:Image ID="imgAd" runat="server" AlternateText="Panda Visa" ImageUrl="Images/AffiliateBanners/125%20by%20125.jpg" />
</td>
<td>
<asp:TextBox ID="txtHtml" runat="server" Columns="80" ReadOnly="True" Rows="7" TextMode="MultiLine"></asp:TextBox>
</td>
<td>
<asp:Button runat="server" Text="Copy HTML to Clipboard" OnClientClick="ClipBoard('txtHtml')" />
</td>
</tr>
</ItemTemplate>
<FooterTemplate>
</table>
</FooterTemplate>
</asp:Repeater>
Protected Sub repBanner_ItemDataBound(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.RepeaterItemEventArgs) Handles repBanner.ItemDataBound
Dim CurrentAd As Ad = CType(e.Item.DataItem, Ad)
Dim RepeaterItem As RepeaterItem = e.Item
Dim imgAd As Image = CType(RepeaterItem.FindControl("imgAd"), Image)
imgAd.ImageUrl = "Images/" & "125 by 125.jpg" '<<<Error occurs here
End Sub
Object reference not set to an instance of an object.
Here's some debug info that I thought may help:
? RepeaterItem.Controls.Count
1
? RepeaterItem.Controls(0).Controls.Count
0
? typename(RepeaterItem.Controls(0))
"LiteralControl"
You need to check e.Item.ItemType to make sure that you're dealing with an item, not a header or footer. Something like this:
Protected Sub repBanner_ItemDataBound(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.RepeaterItemEventArgs) Handles repBanner.ItemDataBound
If (e.Item.ItemType <> ListItemType.Item AndAlso e.Item.ItemType <> ListItemType.AlternatingItem) Then
Return
End If
Dim CurrentAd As Ad = CType(e.Item.DataItem, Ad)
Dim RepeaterItem As RepeaterItem = e.Item
Dim imgAd As Image = CType(RepeaterItem.FindControl("imgAd"), Image)
imgAd.ImageUrl = "Images/" & "125 by 125.jpg" '<<<Error occurs here
End Sub

Resources