Find a Control inside ASP:Repeater - asp.net

I am trying to access a control inside a Repeater. The control is inside the <ItemTemplate> tag. I am using FindControl but it's always coming out Null.
What am I doing wrong?

My guess is that FindControl can only be used in record-level events such as ItemDataBound:
protected void Repeater1_ItemDataBound(object sender, RepeaterItemEventArgs e)
{
(ControlTypeCast) e.Item.FindControl("myControl")).SomeProperty = "foo";
}

I'm guessing that you're trying to find a control at the wrong point in the page lifecycle. The ItemDataBound event is where you need to look for it.
This example is in vb.net, but I'm sure you get the idea.
Protected Sub rp_items_ItemDataBound(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.RepeaterItemEventArgs) Handles rp_items.ItemDataBound
If e.Item.ItemType = ListItemType.Item OrElse e.Item.ItemType = ListItemType.AlternatingItem Then
Dim someLiteral As Literal = e.Item.FindControl("someliteral")
End If
End Sub

In most cases, spelling the control name wrong :) It may also be that you are searching for a control that exists within another container. Can you post your code?

for (int i = 0; i <= repeater1.Items.Count - 1; i++)
{
Button delete = (Button)repeater1.Items[i].FindControl("btnDelete");
delete.Visible = true;
Button edit = (Button)repeater1.Items[i].FindControl("btnEdit");
edit.Visible = true;
}
Vb.net
For i As Integer = 0 To Repeater1.Items.Count - 1
Dim CmbTyp As DropDownList = DirectCast(Repeater1.Items(i).FindControl("DropDownList1"),DropDownList)
Dim SeatN As Label = DirectCast(Repeater1.Items(i).FindControl("label1"), Label)
styp = CmbTyp.SelectedItem.Text.Trim
sNo = SeatN.Text
Next

Try This
For vb.net
CType(e.Item.FindControl("myControl"), Literal).Text = "foo"
For c#
[Literal]e.item.FindControl["myControl"].Text="foo";

Related

Replace text of ASP Literal in Code Behind

I'm new to ASP and was hoping to get some guidance on how to make my literal accessible in my Code Behind and then change it to the text of a passed in parameter.
I have an resources.ascx file that displays a list of people (pulled from a database). That is working fine and it looks something like this:
Full name
T: (888-888-8888)
F: (888-888-8888)
The problem, however, is that I now want it conditionally say "Toll Free" instead of "F:" for one page.
In the people.aspx page, I'm passing in "Toll Free" to the resource:
<%# Register Src="~/UserControls/resources.ascx" TagName="Resources" TagPrefix="ucResources" %>
<ucResources:Resources ID="Resources1" FaxNumberAlias="Toll Free" runat="server" />
resources.ascx The repeater outputs all the people from the database to the page.
<asp:Repeater ID="Repeater1" runat="server">
<ItemTemplate>
<div class="sectioncontent">
<b><%#Eval("EmployeeFirstName")%> <%#Eval("EmployeeLastName)%></b>
T: <%#Eval("Phone")%>
<br>
<asp:Literal runat="server" ID="FaxNumberLabel">F:</asp:Literal> <%#Eval("Fax")%><br>
</div>
<br />
</ItemTemplate>
In the resources.ascx.vb file, I wanna do something like this but FaxNumberLabel (the literal I declared in resources.ascx) isn't accessible or hasn't been declared.
Public Property FaxNumberAlias() As String
Get
Return _FaxNumberAlias
End Get
Set(ByVal value As String)
_FaxNumberAlias = value
End Set
End Property
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
If Not String.IsNullOrEmpty(_FaxNumberAlias) Then
FaxNumberLabel.Text = _FaxNumberAlias
End If
PopulateRepeater()
End Sub
What am I missing that connects the literal to the code behind?
The problem is that the Literal is inside a Repeater so you may potentially have lots of them. The best way is to access them inside the OnDataItemBound event of your repeaters:
Protected Sub Repeater1_OnDataItemBound(ByVal sender As Object, ByVal e As RepeaterItemEventArgs) Handles Repeater1.OnDataItemBound
If (e.Item.ItemType = ListItemType.Item) Or _
(e.Item.ItemType = ListItemType.AlternatingItem) Then
Dim litFaxNumberLabel As Literal = e.Item.FindControl("FaxNumberLabel")
litFaxNumberLabel.Text = _FaxNumberAlias
End If
End Sub
Note: Excuse any bad syntax, it's been over 4 years since I touched VB!
You can simply use like this -
protected void DataDisplay_ItemDataBound(object sender, RepeaterItemEventArgs e)
{
if ((e.Item.ItemType == ListItemType.Item) || (e.Item.ItemType == ListItemType.AlternatingItem))
{
LinkButton lit = (LinkButton)e.Item.FindControl("LinkButton2");
if (lit.Text == "0")
{
int a = Convert.ToInt32(lit.Text);
if (a == 0)
{
if (a == 0)
{
lit.Text = "Your Text";
}
}
}
}
}
In my case, I am using a button (btnRedirect) in each row.
RepeaterItem item = btnRedirect.NamingContainer as RepeaterItem;
var type = item.FindControl("IdUsed") as Literal;
var literalValue = type.Text;

GridView Double-click Event in Asp.Net

I am using a GridView control in ASP.NET web form to display the data records. I want to handle the double click event of the row of the GridView. also I should get information which row is clicked.
Please refer to this brilliant post that will assist you with your problem
http://www.codeproject.com/Articles/15677/Clickable-and-Double-Clickable-Rows-with-GridView
Protected Sub GridView2_SelectedIndexChanged(sender As Object, e As EventArgs) Handles GridView1.SelectedIndexChanged
Dim row As GridViewRow = GridView1.SelectedRow
Dim i As Integer = 0
GridView1.Rows(row.RowIndex).BackColor = ColorTranslator.FromHtml("#A1DCF2")
MsgBox(doubleClick & " " & GridView1.SelectedIndex.ToString)
If doubleClick = GridView1.SelectedIndex.ToString Then
MsgBox("Yo")
End If
doubleClick = GridView1.SelectedIndex.ToString
End Sub
Try this simple code I wrote. It works for me.
Protected Sub GridView2_SelectedIndexChanged(sender As Object, e As EventArgs) Handles GridView1.SelectedIndexChanged
Dim row As GridViewRow = GridView1.SelectedRow
Dim i As Integer = 0
GridView1.Rows(row.RowIndex).BackColor = ColorTranslator.FromHtml("#A1DCF2")
MsgBox(doubleClick & " " & GridView1.SelectedIndex.ToString)
If doubleClick = GridView1.SelectedIndex.ToString Then
MsgBox("Yo")
End If
doubleClick = GridView1.SelectedIndex.ToString
End Sub
After a lot of unsuccessful tries, I got it done using Jquery, this way:
$('html').on('click', ".wwdblclick", function (e) {
e.preventDefault;
return false;
});
$('html').on('dblclick', ".wwdblclick", function (e) {
var x = $(this).attr("href").replace("javascript:", "");
$(this).attr("ondblclick", x);
if (!(typeof $(this).attr("wwRunningDblclick") !== "undefined")) {
$(this).attr("wwRunningDblclick", "true");
$(this).trigger("dblclick");
}
else {
$(this).removeAttr("wwRunningDblclick");
}
});
Then just set the "wwdblclick" cssClass in your grid button command, like this:
<asp:LinkButton ID="lkbX" runat="server" Text="<i class=icon-minus-sign></i>" CssClass="wwdblclick" CommandName="action" CommandArgument='<%# Eval("id") %>' />
Cheers,
You can try with RowCommand
http://msdn.microsoft.com/fr-fr/library/system.web.ui.webcontrols.gridview.rowcommand%28v=vs.80%29.aspx

Why won't FindControl find the button in the footer of my repeater?

I am using an OnItemDataBound event to attempt to activate a disabled button in a repeater. Quite simply, if the event is triggered, I know there are items in the repeater and therefore want to enable the button. Where I am getting stuck is on casting the button in the function so I can enable it. The relevant part of the repeater code is below:
<asp:Repeater ID="RptEnterHours" runat="server" DataSourceID="SQL_EmployeeGetTimesheet" ClientIDMode="Predictable" OnItemDataBound="RptEnterHours_Bound">
'.....Irrelevant code.....
<FooterTemplate>
<asp:Button Enabled="false" ID="SubmitTimesheets" Text="Submit All Timesheets" OnClick="processTimesheetEntry" runat="server" OnClientClick="checkValues();" />
</FooterTemplate>
</asp:Repeater>
This is my code behind:
Sub RptEnterHours_Bound(Sender As Object, e As RepeaterItemEventArgs)
'Exposes the Submit All Timesheets button if timesheets are available.
If (e.Item.ItemType = ListItemType.Item) Or _
(e.Item.ItemType = ListItemType.AlternatingItem) Then
Dim sButton As Button = TryCast(Me.FindControl("SubmitTimesheets"), Button)
sButton.Enabled = True
End If
End Sub
This and all other attempts has yielded the dreaded "Object reference not set to an instance of an object" message. Can anyone tell me what I am doing wrong and why my code behind won't find the button?
please try this i am sure it will help you.
If e.Item.ItemType = ListItemType.Footer Then
Dim btn as new button
btn = CType(e.Item.FindControl("SubmitTimesheets"), Button)
btn.enabled = true
End If
You're restricting it to looking within the item and alternating item templates.
Change this:
If (e.Item.ItemType = ListItemType.Item) Or _
(e.Item.ItemType = ListItemType.AlternatingItem) Then
To:
If (e.Item.ItemType = ListItemType.Footer) Then
You want to test for e.Item.ItemType = ListItemType.Footer. Item and AlternatingItem are used for the actual data records, not the footer. Therefore, the button indeed does not exist for Items and AlternatingItems.
Then, you will want to add a test for whether the RptEnterHours.DataSource object has records. For this, you will need to cast the RptEnterHours.DataSource to whatever type the data source is.
So, basically something like this. You will obviously need to change it to fit your code:
Sub RptEnterHours_Bound(Sender As Object, e As RepeaterItemEventArgs)
'Exposes the Submit All Timesheets button if timesheets are available.
If (e.Item.ItemType = ListItemType.Footer) Then
Dim sButton As Button = TryCast(Me.FindControl("SubmitTimesheets"), Button)
Dim myDataSource = CType(RptEnterHours.DataSource, MyDataSourceType)
sButton.Enabled = (myDataSource.Count > 0)
End If
End Sub
It's been a little while since I've worked with web forms, but I believe the problem is two fold.
When the item type is Item or AlternatingItem then you know you have data in the repeater. In those cases you could set an instance level flag to indicate that you have items.
Then, when the item type is footer AND you have items you want to enable to button. The way to do this is mentioned in an unaccepted answer to the question linked to by #codingkiwi.com, but I believe the problem is the context in which you're calling FindControl. You are calling Me.FindControl which will search the level 1 children of the page (or user control, or control, or whatever Me is a reference to). You want to be searching the child controls of the actual repeater element, which in this case is the footer. So the search becomes e.Item.FindControl.
It should be noted that there are probably more elegant ways to detect whether or not a repeater control has elements. Perhaps all you need to check for in the OnDataBound event is the footer item, and then look for something like: (my VB may be a bit rusty too)
If (Me.RptEnterHours.Items IsNot Null AndAlso Me.RptEnterHours.Items.Any()) Then
Not sure why its just not enabled in the first place, but this will work since it will fire for the footer after the Item/AlternatingItem types:
Private m_bolEnableButton As Boolean = False
Sub RptEnterHours_Bound(Sender As Object, e As RepeaterItemEventArgs)
'Exposes the Submit All Timesheets button if timesheets are available.
If (e.Item.ItemType = ListItemType.Item) Or _
(e.Item.ItemType = ListItemType.AlternatingItem) Then
'"if the event is triggered, I know there are items in the repeater and therefore want to enable the button"
m_bolEnableButton = True
End If
If e.Item.ItemType = ListItemType.Footer Then
If m_bolEnableButton Then
Dim sButton As Button = TryCast(e.Item.FindControl("SubmitTimesheets"), Button)
sButton.Enabled = True
End If
m_bolEnableButton = False
End If
End Sub
The reason you get the Object null reference exception is because you are fixated on the cast, which isn't causing the problem. You can generally safely cast the results of FindControl implicitly. What you need to explicitly check for is a null reference AFTER the FindControl results are captured.
Also, you should be looking for ListItemType.Footer so you can reference the footer row.
Finally, FindControl() is not recursive. It only finds controls within the top-level naming container. In most databound controls, each row represents its own naming container, so you must FindControl within the row you want to search. When you use Me, it refers to the page. You should instead use e.Item.FindControl().
Code:
Dim bRecordsFound as Boolean = False
Sub RptEnterHours_Bound(Sender As Object, e As RepeaterItemEventArgs)
If (e.Item.ItemType = ListItemType.Item) Or _
(e.Item.ItemType = ListItemType.AlternatingItem) Then
bRecordsFound = True
End If
If (e.Item.ItemType = ListItemType.Footer) And (bRecordsFound) Then
Dim sButton As Button = e.Item.FindControl("SubmitTimesheets")
If sButton IsNot Nothing Then
sButton.Visible = True
End If
End If
End Sub

Get data being bound to ListView on DataBound event

I have a ListView control and I have added a DataBound event (don't know if this is the correct one) to the control.
I'm wanting to access the data being bound to that particular ItemTemplate from this event, is that possible?
C# Solution
protected void listView_ItemDataBound(object sender, ListViewItemEventArgs e)
{
if (e.Item.ItemType == ListViewItemType.DataItem)
{
ListViewDataItem dataItem = (ListViewDataItem)e.Item;
// you would use your actual data item type here, not "object"
object o = (object)dataItem.DataItem;
}
}
Why they made this so different for ListView still sort of puzzles me. There must be a reason though.
A little late, but I'll try to answer your question, as I had the same problem and found a solution. You have to cast Item property of the ListViewItemEventArgs to a ListViewDataItem, and then you can access the DataItem property of that object, like this:
Private Sub listView_ItemDataBound(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.ListViewItemEventArgs) Handles productsList.ItemDataBound
If e.Item.ItemType = ListViewItemType.DataItem Then
Dim dataItem As Object = DirectCast(e.Item, ListViewDataItem).DataItem
...
End Sub
You could then cast the dataItem object to whatever type your bound object was. This is different from how other databound controls like the repeater work, where the DataItem is a property on the event args for the DataBound method.
Found a workaround, I created a method to format the data how I needed and called it from the markup using:
<%# doFormatting(Convert.ToInt32(Eval("Points")))%>
The data that is used for the current item can be found from the EventArgs.
So from the RepeaterItemEventArgs e we can access the current item by looking in e.Item.DataItem.
protected void listView_ItemDataBound(object sender, ListViewItemEventArgs e)
{
if (e.Item.ItemType == ListViewItemType.DataItem)
{
var currentItem = e.Item.DataItem;
}
}

Implementing clickable page numbers in search result

Classic scenario: Take user input, get a search-result and display it in pages to the user. I then need to display buttons for First, Next, Previous etc, and I maintain the users current page in viewstate. All is good, works fine.
Then I need to implement clickable page numbers, ie. 1-2-3-4-5-6 etc.
Rendering them is simple. I generate a linkbutton control at runtime, add commandargument with the page number and add a handler to it, so click are to be handled. Then I add it to a placeholder, and it is displayed as expected.
But then... If I did not already have a shaved head, I would be pulling out my hair getting the events to fire as expected every time.
How should I do this, so my events are always wired up and able to fire when the paging-linkbuttons are called?
Below is the important parts of the code, some pseudo to make it (hopefully) easier to understand, what I am doing.
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
If Not Page.IsPostBack Then
Search()
End If
End Sub
Sub Search
'Misc databinding stuff, searches and displays results for the page specified in Me.CurrentPage
RenderPagingControls()
End Sub
Sub RenderPagingControls
'loop throug pagenumbers, Build a linkbutton control, add it to a placeholder
AddHandler lbn.Click, AddressOf lbnNumber_Click
lblPageNumbers.Controls.Add(lbn)
...
End Sub
Protected Sub lbnNumber_Click(ByVal sender As Object, ByVal e As EventArgs)
Dim b As LinkButton = CType(sender, LinkButton)
Me.CurrentPage = CInt(b.CommandArgument)
Search()
End Sub
Public Property CurrentPage() As Integer
Get
Dim o As Object = Me.ViewState("CurrentPage")
If o Is Nothing Then
Return 1
Else
Return CType(o, Integer)
End If
End Get
Set(ByVal value As Integer)
Me.ViewState("CurrentPage") = value
End Set
End Property
Protected Sub lbnNumber_Click(ByVal sender As Object, ByVal e As EventArgs)
Dim b As LinkButton = CType(sender, LinkButton)
Me.CurrentPage = CInt(b.CommandArgument)
Search()
End Sub
I'm going to recommend against a LinkButton and recommend Hyperlinks / QueryString parameters instead. For several reasons:
Your page will be much more efficient without the viewstate overhead of a link button.
If these are public facing pages, you'll get better indexing of all the pages if they can be accessed via hyperlinks (and indexed via search engines).
You'll find them much easier to implement. No event management, etc.
You would redefine your CurrentPage method as (hopefully this is correct, I'm better at C# than vb.net):
Public Property CurrentPage() As Integer
Get
Dim o As Object = Me.Request.QueryString("page")
If o Is Nothing Then
Return 1
Else
Return CType(o, Integer)
End If
End Get
End Property
Then just add hyperlinks for each page.
<a href='mypage.aspx?page=1'>1</a> - <a href='mypage.aspx?page=2'>2</a>
etc...
Alternative: If you want to use the LinkButton, you might want to consider putting a single LinkButton in a repeater. Then the only event you have to worry about is the OnItemCommand event. Then no dynamic controls or events. Something like this:
<asp:Repeater ID="rptPages" runat="server" OnItemCommand='doPaging'>
<ItemTemplate>
<asp:LinkButton ID="LinkButton1" runat="server" Text='<%# (Container.DataItem).ToString() %>'
CommandArgument='<%# (Container.DataItem).ToString() %>' />
</ItemTemplate>
<SeparatorTemplate>-</SeparatorTemplate>
</asp:Repeater>
Bind this control to an array (or list) of consecutive Integers (as many are there are pages). Then in your doPaging function (as I call it), check RepeaterCommandEventArgs.CommandArgument to get the page number.
Thanks for the answers, guys. I tried out Austins first, but I must be missing something, because I keep getting the same behavior of link buttons only working every second time... So I gave up on that, and saw the alternative solution with the repeater by Keltex! It is as brilliant as it is simple, and we don't have to worry about any page life-cycle bullshit.
It just really works! ;)
If somebody should need something similar in the future, here is the relevant code behind the scenes:
Sub Search()
...
RenderPagingControls()
End Sub
Sub RenderPagingControls()
Dim pages As New ArrayList
For i As Integer = 1 To Me.PageCount
pages.Add(i)
Next
repPageNumbersTop.DataSource = pages
repPageNumbersTop.DataBind()
repPageNumbersBottom.DataSource = pages
repPageNumbersBottom.DataBind()
End Sub
Public Property CurrentPage() As Integer
Get
Dim o As Object = Me.ViewState("CurrentPage")
If o Is Nothing Then
Return 1
Else
Return CType(o, Integer)
End If
End Get
Set(ByVal value As Integer)
Me.ViewState("CurrentPage") = value
End Set
End Property
Public Property PageCount() As Integer
Get
Dim o As Object = Me.ViewState("PageCount")
If o Is Nothing Then
Return 0
Else
Return CType(o, Integer)
End If
End Get
Set(ByVal value As Integer)
Me.ViewState("PageCount") = value
End Set
End Property
Protected Sub repPageNumbersTop_ItemCommand(ByVal source As Object, ByVal e As System.Web.UI.WebControls.RepeaterCommandEventArgs) Handles repPageNumbersTop.ItemCommand, repPageNumbersBottom.ItemCommand
Me.CurrentPage = CType(e.CommandArgument, Integer)
Search()
End Sub
Private Sub repPageNumbersTop_ItemDataBound(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.RepeaterItemEventArgs) Handles repPageNumbersTop.ItemDataBound, repPageNumbersBottom.ItemDataBound
If e.Item.ItemType = ListItemType.Item Or e.Item.ItemType = ListItemType.AlternatingItem Then
Dim lbn As LinkButton = CType(e.Item.FindControl("lbnPageNumber"), LinkButton)
If lbn.CommandArgument = Me.CurrentPage.ToString Then
lbn.Enabled = False
End If
End If
End Sub
This code works (sorry it's in C#):
protected void SearchButton_Click(object sender, EventArgs e)
{
//clear the collection!
pnlPageNumber.Controls.Clear();
//simulate search
System.Random rnd = new Random();
//create page buttons
for (int i = 0; i < rnd.Next(3, 15); i++)
{
LinkButton lb = new LinkButton();
pnlPageNumber.Controls.Add(lb);
lb.ID = "btn" + i;
lb.Text = i.ToString();
lb.CommandArgument = i.ToString();
lb.Command += new CommandEventHandler(linkbutton_Command);
//optional literal
pnlPageNumber.Controls.Add(new LiteralControl(" "));
}
ViewState["control#"] = Panel1.Controls.Count;
}
protected void Page_Load(object sender, EventArgs e)
{
if (IsPostBack)
{
//Recreate link buttons
//This is necessary to ensure proper event binding
int count = 0;
if (ViewState["control#"] != null)
count = (int)ViewState["control#"];
for (int i = 0; i < count; i++)
{
LinkButton lb = new LinkButton();
pnlPageNumber.Controls.Add(lb);
lb.ID = "btn" + i; //make sure IDs are the same here and on Search
lb.Command += new CommandEventHandler(linkbutton_Command);
//this is not necessary, but if you do, make sure its in both places
pnlPageNumber.Controls.Add(new LiteralControl(" "));
}
}
}
void linkbutton_Command(object sender, CommandEventArgs e)
{
Response.Write(e.CommandArgument.ToString() + " CLICK<br />");
}
You could use the DataPager control -- the only limitation is you have to use it with the ListView control, but you should be able to represent your data using the ListView control fairly easily because it is very flexible. You can set the DataSource of the ListView control to the result of your data result, whether that be a DataSet, Collection, Array, etc.
To create the paging controls with "first", "last", and page numbers, set up the DataPager like this (where ListView1 is the ID of your ListView control):
<asp:DataPager ID="DataPager1" runat="server"
PagedControlID="ListView1" PageSize="25">
<Fields>
<asp:NextPreviousPagerField FirstPageText="first" ShowFirstPageButton="True"
ShowNextPageButton="False" ShowPreviousPageButton="False" />
<asp:NumericPagerField />
<asp:NextPreviousPagerField LastPageText="last" ShowLastPageButton="True"
ShowNextPageButton="False" ShowPreviousPageButton="False" />
</Fields>
</asp:DataPager>
By design, the DataPager uses the whole result set from the database, but you can improve the performance by caching the result and using that on the subsequent requests.
Hope this helps.
iirc... adding controls dynamically at runtime is a bit tricky. The control tree must be rebuilt during post back... but before viewstate is loaded (not sure when in the page life cycle... but way before page load). So... your problem is that by the time asp.net is trying to figure out your event the actual originating control has not yet been created.

Resources