Problems with repeater controls - asp.net

I'm having lots of trouble understanding how repeaters are supposed to work and how I'm supposed to use them. Basically, I have a label, a set of image buttons, and a div within a repeater. When you click on the button, I want to populate the div that was created with these repeaters (so a different div for each iteration of the repeater).
It seems so simple, yet I can't get anything to work. Please help me!
Here is some of my code if that helps.
(my data source)
<asp:sqldatasource runat="server" id="dtsSpecialNotes" connectionstring="connection string" providername="System.Data.SqlClient"> /asp:sqldatasource
(my repeater)
<asp:Repeater id="rptSpecialNotes" runat="server">
<HeaderTemplate>
</HeaderTemplate>
<ItemTemplate>
</ItemTemplate>
</asp:Repeater>
(some code behind, I have a method to populate the repeater called on page load)
rptSpecialNotes.DataSource = dtsSpecialNotes
rptSpecialNotes.DataBind()
Dim imbMotion As New ImageButton
Dim imbAttachments As New ImageButton
imbMotion.ImageUrl = "images/controls/Exclaim.png"
imbMotion.Width = "40"
imbMotion.Height = "40"
imbMotion.AlternateText = "Add Motion"
imbMotion.ID = "imbMotion" & listSpecialNotesCounter
imbAttachments.ImageUrl = "images/controls/Documents.png"
imbAttachments.Width = "40"
imbAttachments.Height = "40"
imbAttachments.AlternateText = "Add Document"
imbAttachments.ID = "imbAttachments" & listSpecialNotesCounter
rptSpecialNotes.Controls.Add(lblTitle)
rptSpecialNotes.Controls.Add(imbMotion)
Here are the problems I'm having:
I can't get the repeater to iterate. It's only firing off once and then stops.
I can't get a label with the repeater data source's Subject (a field in my database) to appear.
I really appreciate any help. I just can't grasp these repeater controls.

What you want to do is populate your repeater using the ItemDataBound event as opposed to going through the item collection. That will be called for each of the items that are in the collection you assigned to the DataSource.
This is what I do when working with repeater data binding and the ItemDataBound event, I'll use a list of news items for this example:
' Bind my collection to the repeater
Private Sub LoadData()
Dim newsList As List(Of News) = dao.GetNewsList()
rptrNews.DataSource = newsList
rptrNews.DataBind()
End Sub
' Every item in the data binded list will come through here, to get the item it will be the object e.Item.DataItem
Protected Sub rptrNews_ItemDataBound(sender As Object, e As RepeaterItemEventArgs)
If e.Item.ItemType = ListItemType.Item OrElse e.Item.ItemType = ListItemType.AlternatingItem Then
' Get the current item that is being data bound
Dim news As News = DirectCast(e.Item.DataItem, News)
' Then get all the controls that are within the <ItemTemplate> or other sections and populate them with the correct data, in your case it would be an image
Dim ltlHeadline As Literal = DirectCast(e.Item.FindControl("ltlHeadline"), Literal)
Dim ltlContent As Literal = DirectCast(e.Item.FindControl("ltlContent"), Literal)
ltlHeadline.Text = news.Headline
ltlContent.Text = news.Teaser
End If
End Sub
Alternatively, you could do it all in the Markup Code and only assign the datasource and call data bind in the code behind. To achieve this you could define your ItemTemplate as follows (again with the news e.g.):
<ItemTemplate>
<tr>
<td><%#Container.DataItem("Headline")%></td>
<td><%#Container.DataItem("Teaser")%></td>
</tr>
</ItemTemplate>

Related

How to htmlencode body but not heading of gridview

I have a GridView. I want the content of the cells, the actual detail data, to be html-encoded. But I want to keep the raw HTML in the heading.
That is, the heading in this case is the name of a language and the ISO code, for example "English (EN)". I want the name and code on separate lines, so I'm specifying the heading as "English<br/>(EN)".
But for the content, I want to see any HTML. If it says "<p>foobar</p>" I want the p-tags to display.
If I say "htmlencode=false", then the heading doesn't get encoded, but neither does the data.
Is there any way to say, html-encode the data, but not the heading?
If it matters, I'm building the columns in code rather than with tags in the ASP file, depending on which languages I find in my data. So here's how I'm creating the column:
For Each row As DataRow In ds.Tables(0).Rows
Dim iso2 = row("language")
Dim name = row("name")
... other code ...
Dim head = String.Format("{0}<br/>({1})", name, iso2)
gvSnippets.Columns.Add(New BoundField With {.HeaderText = head, .DataField = iso2, .HtmlEncode = False})
... other code ...
End For
(My first draft did not set HtmlEncode.)
Curious observation: In my first couple of test runs, the data did not include any HTML or entities, and the HTML in the heading was NOT encoded, I got the line break and not "<br/>". Then I did a test where there were entities in the data and the entities got html-encoded ... and so did the header. So like, ASP is apparently saying that by default, if the data has no HTML but the heading does, then don't HTML-encode the heading. But if the data has HTML, then HTML-encode both the data and the heading. So like, it's deciding dynamically whether to html-encode the heading or not.
In reply to #fnostro, here's the markup for the GridView:
<asp:GridView ID="gvSnippets" runat="server" AutoGenerateColumns="False" SkinID="skin3" EmptyDataText="No records found" Visible="false">
</asp:GridView>
There is no <Columns> markup. I build the columns completely in code. I haven't tested if the same behavior occurs in what I assume is the more normal case where you specify columns with markup.
Add these lines of code to the beginning of your loop
row("language") = HttpUtility.HtmlEncode(dr("language"))
row("name") = HttpUtility.HtmlEncode(dr("name"))
Based on my reading of the information provided in your post I have the following suggestions:
You should isolate data formatting from raw data.
Your DataTable is the data source for the GridView. The DataTable should contain only raw data, completely unformatted and unadulterated.
The DataTable gets bound to the Gridview by setting the GridView DataSource and making a call to DataBind(). Calling gvSnippets.Databind() will trigger all the databinding events associated with Gridviews which will allow you to:
Manage simple formatting and binding in the GridView Columns in the aspx markup.
Handle more complicated formatting in the code behind for specific GridView events like the RowDataBound event.
Regarding the GridView Header: HeaderText appears once at the top of the Gridview and is stored internally in GridView.HeaderRow, not on a per row basis even though every bound field has a HeaderText property.
Example excerpt gvSnippets Markup per your update:
<asp:GridView ID="gvSnippets" runat="server"
AutoGenerateColumns="False" SkinID="skin3"
EmptyDataText="No records found" Visible="false">
</asp:GridView>
Then you can do things like this in the Code Behind:
Public Class __JunkWebForm1
Inherits System.Web.UI.Page
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
' Fill Dataset first, then:
gvSnippets.DataSource = ds.Tables(0)
' Add Columns...
gvSnippets.Columns.Add(...)
gvSnippets.Columns.Add(...)
gvSnippets.Columns.Add(...)
. . .
' This will trigger data binding events
gvSnippets.DataBind();
End Sub
Private Property HeaderTextLanguage As String
Private Property HeaderTextLanguageIso As String
Dim NeedHeaderText As Boolean = False
Private Sub gvSnippets_DataBinding(sender As Object, e As EventArgs) Handles gvSnippets.DataBinding
NeedHeaderText = True
End Sub
Private Sub gvSnippets_DataBound(sender As Object, e As EventArgs) Handles gvSnippets.DataBound
Dim this As GridView = sender
this.Columns(0).HeaderText = String.Format("{0}<br>({1})", HeaderTextLanguage, HeaderTextLanguageIso)
End Sub
Private Sub gvSnippets_RowDataBound(sender As Object, e As GridViewRowEventArgs) Handles gvSnippets.RowDataBound
Dim this As GridView = sender
Dim drv As DataRowView = e.Row.DataItem
If e.Row.RowType = DataControlRowType.DataRow Then
If NeedHeaderText Then
HeaderTextLanguage = drv("language")
HeaderTextLanguageIso = drv("iso")
NeedHeaderText = False
End If
e.Row.Cells(0).Text = Server.HtmlEncode(drv("Somefieldnameofyours"))
End If
End Sub
End Class
Addendum Dealing with Dynamic cells
For a GridView defined as:
<asp:GridView ID="gvSnippets" runat="server"
AutoGenerateColumns="False">
</asp:GridView>
I don't know how you're populating your ds but I'm using a SqlDataSource to fill a table in the Code Behind. Note there is no GridView DataSourceID above:
<asp:SqlDataSource ID="SqlDataSource1" runat="server"
SelectCommand="select top 10 user_id, user_name, user_logon, 'English' as language, 'en' as iso from tbl_users"
ConnectionString='<%$ ConnectionStrings:MyConnectionString %>'>
</asp:SqlDataSource>
Then in the Code Behind I can do this:
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
GetDataSetAndPopulate(gvSnippets)
End Sub
Private Sub GetDataSetAndPopulate(gv As GridView)
Dim view As DataView = SqlDataSource1.Select(DataSourceSelectArguments.Empty)
Dim table As DataTable = view.ToTable()
Dim ds As DataSet = New DataSet()
ds.Tables.Add(table)
For Each dc As DataColumn In ds.Tables(0).Columns
Dim field As New BoundField
field.DataField = dc.ColumnName
field.HeaderText = dc.ColumnName
gv.Columns.Add(field)
Next
gv.DataSource = ds
gv.DataBind()
End Sub
And now the Gridview is populated dynamically and you can still handle all the formatting during the DataBinding events as needed.
Here's a solution I came up with that works in my particular case.
I am building the data to populate the GridView in code. I create a DataTable, add columns to it, and then populate those columns. Like:
dim dslang=GetListOfLanguages()
dim dtgv=new DataTable()
for each row as DataRow in dslang.tables(0).rows
dim language_name=row("name")
dim language_code=row("code")
dtgv.columns.add(String.format("{0}<br/>({1})",language_name, language_code)
end for
... bunch of other stuff ...
... inside a loop that reads the data ...
dim outrow=dtgv.NewRow()
... get data for a specific language ...
outrow(language_code)=text
... later ...
my_gridview.datasource=dtgv
my_gridview.databind()
So my solution:
When creating the GridView, set HtmlEncode=false. This makes the header display correctly.
When populating the data, say
outrow(language_code)=HttpUtility.HtmlEncode(text)
So I add the data to the datatable that will ultimately be used as the datasource for the gridview already encoded, so I don't need to rely on the Gridview encoding it.
I consider this a mediocre solution, as it only works because I am populating GridView from a DataTable that I build with code. If I was populating the GridView directly from a DataSource specified in markup, or from a DataSet created by a database query, there'd be no convenient place to put the HttpUtility.HtmlEncode call.

Databinding each checkboxlist in Repeater control (asp.net 4)

Having asked my last question here _
Creating dynamic boxes (divs) with different titles returned by SP
I created a repeater control and create few boxes (divs). I could successfully create Titles for each boxes. My next big step is to have Checkboxlists in the repeater control. Those checkboxlists are populated by data returned by a stored procedure. I have about 8 boxes but this number vary depend on the data returned by the Stored Procedure. With the code i have now, all the checkboxlist will display the same data. I need to have different checkboxlist for each category. My next step will be to create SelectedIndexChanged Events and refilter all those checkboxlist. The overall design and idea is sort of like Search Filter that we find on shopping carts. I found some say Repeater are just good to display data so may be i have to use something else. I can't brainstorm how this can be achievable. I have to use VB.net. The database is SQL server 2008. Using Linq for datasource is alright. Please give me suggestion how this can be achieveable. Please also suggest best control to achieve this. I don't need to stick to repeater control.
<asp:Repeater ID="Repeater1" runat="server" OnItemDataBound="Repeater1_OnItemDataBound" >
<ItemTemplate>
<div class="portlet">
<div class="portlet-header"> <%# Eval("categoryLabel") %></div>
<div class="portlet-content">
<asp:CheckBoxList ID="CheckBoxList1" runat="server">
</asp:CheckBoxList>
</div></div>
</ItemTemplate>
</asp:Repeater>
Protected Sub Repeater1_OnItemDataBound(ByVal sender As Object, ByVal e As RepeaterItemEventArgs)
Dim x As New SIDLinqDataContext
Dim item As RepeaterItem = e.Item
Dim query = x.GetSelectedValue(xxxx, xxxx)
Dim cbl1 As CheckBoxList = DirectCast(item.FindControl("checkboxlist1"), CheckBoxList)
For Each aa In query
cbl1.items.add(New ListItem(xxxx, xxxx))
Next
End Sub
OnLoad page event
Dim dc As New SIDLinqDataContext
Dim query = dc.GetCategories(1710307, 9)
Repeater1.DataSource = query
Repeater1.DataBind()

Changing an asp:label text inside a repeater

I have this label inside a repeater
<asp:Label id="lblsub" runat=server text="sdds" />
I am trying to change the text of this label,this is the code behind
For Each Item As RepeaterItem In dg.Items
Dim l As New label
l = CType(Item.FindControl("lblsub"), System.Web.UI.WebControls.Label)
l.text="test"
Next
unfortunately this code doesn't work for me ,the text value doesn't change ,so I will be very happy to some help here
thanks
See my answer in this question. Believe it will solve your issue.
Getting the databound value from repeater control in asp.net
EDIT
First of all, make sure you always use quotes in your markup:
<asp:Label ID="lblsub" runat="server" Text="sdds" />
Try something like this in your code behind. Forgive me if my VB is a little rusty:
For Each Item As RepeaterItem in dg.Items
Dim l As Label = CType(Item.FindControl("lblsub"), Label)
l.Text = "test"
Next
There were just a couple little problems with syntax. I don't know if they are causing your issue, but best get the easy stuff out of the way first.
When is that bit of code being ran?
Make sure you are not rebinding the repeater afterwards by doing the following in your Page_Load:
Sub Page_load
If not Page.IsPostBack Then
BindRepeater()
End If
End Sub
If you need to do this bit of code every time the repeater is bound, then put your code into the DataBound event:
Protected Sub dg_ItemDataBound(sender As Object, e As System.Web.UI.WebControls.RepeaterItemEventArgs) Handles dg.ItemDataBound
Select Case e.item.itemtype
Case ListItemType.Item, ListItemType.AlternatingItem
Dim l As Label = e.Item.FindControl("lblsub")
l.Text = "foo"
End Select
End Sub
I haven't got any programming software with me so unsure of the syntax
C# code sample (it should be inside method or page_load)
foreach (RepeaterItem rt in MyRepeater.Items)
{
Label l = ((Label)rt.FindControl("lblPrice"));
l.Text = "200";
}

Unable get Label.text in ASP.Net Repeater using FindControl

I can access the text within a textbox within my repeater, but I am unable to pull the text value from a label within my repeater.
The repeater is populated from a datatable with row(x) being filled by sqlreader(x), I don't know if that makes a difference.
I cannot use javascript for this. I need to access the label value from the codebehind.
<asp:label id="weiLabel" runat="server">
<%#DataBinder.Eval(Container, "DataItem.weiLabel")%>
</asp:label>
is the markup
I can access a textbox on the same row using:
featTable.Controls(1).Controls(1).FindControl("costText")
and retrieve the textbox.text, but using the same statement for the label gives me {text=""}.
I have verified that the clientID of control that is returned with findcontrol is correct (featTable__ctl1_weiLabel)
Thanks for any help
Can you try declaring your label like this:
<asp:label id="weiLabel" runat="server" Text='<%#DataBinder.Eval(Container, "DataItem.weiLabel")%>' / >
You can also try putting in the value into your label from the code behind using the databound method. I find it a bit easier to debug and cleaner then putting it in the html
Private Sub repPoliList_ItemDataBound(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.RepeaterItemEventArgs) Handles repPoliList.ItemDataBound
If (e.Item.ItemType = ListItemType.Item OrElse e.Item.ItemType = ListItemType.AlternatingItem) Then
Dim dr As DataRowView = CType(e.Row.DataItem, DataRowView)
Dim weiLabel As System.Web.UI.WebControls.Label= CType(e.Item.FindControl("weiLabel"), System.Web.UI.WebControls.Label)
weiLabel.text= dr("ColumnFromDatabase").toString
End If
End Sub

Not possible to load DropDownList on FormView from code behind?

I have a UserControl, containing a FormView, containing a DropDownList.
The FormView is bound to a data control.
Like so:
<asp:FormView ID="frmEdit" DataKeyNames="MetricCode" runat="server" DefaultMode="Edit" DataSourceID="llbDataSource" Cellpadding="0" >
<EditItemTemplate>
<asp:DropDownList ID="ParentMetricCode" runat="server" SelectedValue='<%# Bind("ParentMetricCode") %>' />
</EditItemTemplate>
<asp:FormView>
I am trying to populate the DropDownList from the codebehind. If this was not contained in a FormView, I would normally just do it in the Page_Load event. However, that does not work within a FormView, as as soon as I try to do it, accessing the dropdownlist in code, i.e.:
theListcontrol = CType(formView.FindControl(listControlName), ListControl)
...the data binding mechanism of the FormView is invoked, which, of course, tries to bind the DropDownList to the underlying datasource, causing a **'ParentMetricCode' has a SelectedValue which is invalid because it does not exist in the list of items. "Parameter name: value ..." error, since the DropDownList has not yet been populated.
I tried performing the load in the DataBinding() event of the FormView, but then:
theListcontrol = CType(formView.FindControl(listControlName), System.Web.UI.WebControls.ListControl)
...fails, as the FormView.Controls.Count = 0 at that point.
Is this impossible? (I do not want to have to use a secondary ObjectDataSource to bind the dropdownlist to)
Implement the OnDataBinding directly on your DropDownList.
When you bind your formview to some data the OnDataBinding for the DropDownList will fire. At this point you can then load the the values you want into the list and bind the selected value to the loaded list as well.
Here is an example:
<asp:DropDownList ID="ParentMetricCode" runat="server" OnDataBinding="ParentMetricCode_DataBinding" />
Then implement the OnDataBinding:
protected void ParentMetricCode_DataBinding(object sender, System.EventArgs e)
{
DropDownList ddl = (DropDownList)(sender);
// Fill the list items however you want
ddl.Items.Add(new ListItem("1", "1"));
ddl.Items.Add(new ListItem("2", "2"));
// etc...
// Set the selected value
ddl.SelectedValue = Eval("ParentMetricCode").ToString();
}
When DataBind your FormView, everything will start to do its magic :)
Also if you loading the list data for your DropDownList from a DB or something you might want to cache it since each 'row' of data will cause the loading of the list data.
EDIT:
Since you think it's not possible I wrote a quick demo app to prove how it works:
In your aspx file include this:
<asp:FormView ID="fvTest" runat="server">
<ItemTemplate>
<asp:DropDownList ID="ddlTest" runat="server" OnDataBinding="ddlTest_DataBinding"></asp:DropDownList>
</ItemTemplate>
</asp:FormView>
Then in your .cs file:
public class MockData
{
public string ID { get; set; }
public string Text { get; set; }
}
protected void Page_Load(object sender, EventArgs e)
{
List<MockData> lst = new List<MockData>();
lst.Add(new MockData() { ID = "3", Text = "Test3" });
fvTest.DataSource = lst;
fvTest.DataBind();
}
protected void ddlTest_DataBinding(object sender, System.EventArgs e)
{
DropDownList ddl = (DropDownList)(sender);
ddl.Items.Add("1");
ddl.Items.Add("2");
ddl.Items.Add("3");
ddl.Items.Add("4");
ddl.Items.Add("5");
ddl.SelectedValue = Eval("ID").ToString();
}
Run the code... the DropDownList will be loaded with all the values and be set to the correct selected value that was set in the mocked object.
Not sure what else I can do to prove it any better... I think your missing how DataBinding actually works. If you try to do it at the FormView level, the controls don't get made until the FormView is actually bound. When the binding happens, you can trigger each control within the template to do something by implementing it's OnDataBinding event. At that point the current iteration of bound object and it's values are available and that is where you see my code do an Eval("ID").
It is working for me with the OnDataBound event instead of OnDataBinding which occurs before the control is created.
<asp:DropDownList ID="ddlCountry" runat="server" DataSourceID="SqlDataSrcCountries" AutoPostBack="True" DataTextField="name" DataValueField="code" OnDataBound="ddlCountry_DataBound">
</asp:DropDownList>
Code-behind:
Protected Sub ddlCountry_DataBound(ByVal sender As Object, ByVal e As System.EventArgs)
Dim ddlCountry As New DropDownList
Dim strCountry As String = String.Empty
Dim lstItemToFind As New ListItem
ddlCountry = FormViewUsers.FindControl("ddlCountry")
strCountry = Eval("country")
lstItemToFind = ddlCountry.Items.FindByValue(strCountry)
If (Not IsNothing(lstItemToFind)) Then
ddlCountry.SelectedValue = strCountry
End If
End Sub
If you use this on other templates than the Edit you just need to validate:
If (FormViewUsers.CurrentMode = FormViewMode.Edit) Then
End If
Ok, found "a" solution:
1. Call the DDL populate function from the FormView_ItemCreated event.
2. Modify the code to populate the DDL like so:
Public Overloads Shared Sub BindListControl(Of theLLBLgenEntityType As EntityBase) _
(ByVal formView As FormView, _
ByVal listControlName As String, _
ByVal theCollection As CollectionCore(Of theLLBLgenEntityType), _
ByVal DataValueField As EntityField, _
ByVal DataTextField As EntityField, _
ByVal IncludeEmptyItem As Boolean)
If formView.CurrentMode = FormViewMode.Edit Then
Dim theListcontrol As System.Web.UI.WebControls.ListControl
theListcontrol = CType(formView.FindControl(listControlName), System.Web.UI.WebControls.ListControl)
For Each entity As theLLBLgenEntityType In theCollection
theListcontrol.Items.Add(New ListItem(entity.Fields(DataTextField.Name).CurrentValue.ToString, entity.Fields(DataValueField.Name).CurrentValue.ToString))
Next
If IncludeEmptyItem Then
theListcontrol.Items.Insert(0, New ListItem("", ""))
End If
End Sub
It kinda seems to basically boil down to, you cannot do additional "Databinding", programatically at runtime, within a FormView.
(Anyone know why the formatting of my code is all screwed up in this post??)
Update
This solution raises yet another batch of issues, related to inserting new items. After a lot of screwing around I eventually found a way around that. At this point, I am basically at the point where I would recommend either avoiding the FormView control entirely, or definitely avoid programatically altering bound controls. Hopefully, perhaps using seperate data controls for each DropDownList might work better, but this might be wishful thinking as well.

Resources