How to fire a button event from inside a repeater? - asp.net

I have done my research but can't find an efficient way to do the following in VB:
Each button should fire the same event.
The button event saves every repeater item and so each event is not unique.
I am aware I can use the ItemCommand option but have not been able to get it working as desired.
ASP.NET
Inside Repeater Item
<asp:Button ID="btnSave" RunAt="Server"/>
VB.NET
Protected Sub btnSave_Click(ByVal sender As Object, ByVal e As System.EventArgs)
sqlConn.Open()
For Each Item As RepeaterItem In rpt.Items
...
Next
sqlConn.Close()
End Sub

Edit:
After some research here on SO, I found that others events than ItemCommand are not caught by Asp:Repeater, as FlySwat said on his answer. So you'll need to write your VB.NET code like this:
First, declare the ItemCommand event on your page with something like this:
Protected Sub rpt_ItemCommand(ByVal source As Object, ByVal e As System.Web.UI.WebControls.RepeaterCommandEventArgs) Handles rpt.ItemCommand
If e.CommandName = "Save" Then
'Save
End If
End Sub
Then, on Asp:Button markup inside the Asp:Repeater, you must set its CommandName property like this:
<Asp:Button ID="btnSave" runat="server" CommandName="Save" UseSubmitBehavior="false"/>
Take a look here to learn more about the UseSubmitBehavior.
Try it.

When the button is inside a Repeater template, you need to add OnClick event, you can add event on ItemDataBound event of the Repeater control.
Your .aspx code will look something like this:
<asp:Repeater ID="Repeater1" runat="server">
<ItemTemplate>
<asp:Button ID="btnSave" runat="server" Text="SomeText" />
</ItemTemplate>
</asp:Repeater>
code-behind
void Repeater1_ItemDataBound(object sender, RepeaterItemEventArgs e)
{
if (e.Item.ItemType == Repeater1.AlternatingItem || e.Item.ItemType == Repeater1.Item)
{
var btn = e.Item.FindControl("btnSave") as Button;
if (btn != null)
{ // adding button event
btn.Click += new EventHandler(btn_Click);
}
}
}
void btn_Click(object sender, EventArgs e)
{
//write your code
}
in vb.net
Private Sub Repeater1_ItemDataBound(sender As Object, e As RepeaterItemEventArgs)
If e.Item.ItemType = Repeater1.AlternatingItem OrElse e.Item.ItemType = Repeater1.Item Then
Dim btn = TryCast(e.Item.FindControl("btnSave"), Button)
If btn IsNot Nothing Then
' adding button event
btn.Click += New EventHandler(btn_Click)
End If
End If
End Sub
Private Sub btn_Click(sender As Object, e As EventArgs)
'write your code
End Sub

Related

Repeater -button click

I have an Add button outside the Repeater control.When ADD IS Clicked a new row is inserted into database and databinded to repeater.
On click of ADD button i want a linkbutton to be showed in the new row of repeater.
Thanks in advance for the help.
DirectCast(e.Item.FindControl("lnksave"), LinkButton).Visible = True
This code i can not place in my ADD button's click event.what changes should i make to show the linkbutton in newly created row.
I would set LinkButton's property Visible="false" in the markup:
<asp:Repeater ...
... ... ...
<ItemTemplate>
<asp:LinkButton ID="lnksave" runat="server" Visible="false">LinkButton</asp:LinkButton>
</ItemTemplate>
In the code behind declare a flag at page level:
Dim btnClicked As Boolean = False
In Add button's event method set the flag to true. then data bind the repeater:
Protected Sub btnAdd_Click(sender As Object, e As EventArgs) Handles btnSubmit.Click
btnClicked = True
BindRepeater() 'your method to data bind repeater
End Sub
In the repeater's item databound event method check the flag and set the link button's visible property accordingly:
Protected Sub Repeater1_ItemDataBound(sender As Object, e As RepeaterItemEventArgs) Handles Repeater1.ItemDataBound
If (e.Item.ItemType = ListItemType.Item Or e.Item.ItemType = ListItemType.AlternatingItem) Then
Dim lnksave As LinkButton = DirectCast(e.Item.FindControl("lnksave"), LinkButton)
lnksave.Visible = btnClicked
End If
End Sub

use a imageButton in GridView to select the row

I am trying to use a image button in a grid view to select a row so that i can then use the SelectedIndexChanged function to do other things. i have tried this:
<asp:ImageButton ID="Image1" CommandName="SelectRowGrid2" runat="server" ImageUrl="~/images/select.png" />
Protected Sub GridView2_RowCommand(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.GridViewCommandEventArgs) Handles GridView2.RowCommand
Select Case e.CommandName
Case "SelectRowGrid2"
'some code for selecting the index?
Label1.Text = GridView2.SelectedIndex
End Select
End Sub
but it didn't even step in to the RowCommand sub when i went to debug it.
You could set GridView's AutoGenerateSelectButton to true MSDN
You could set LinkButton's CommandName to "Select" MSDN
Or you could try following:
Protected Sub ImgSelect_Click(ByVal sender As Object, ByVal e As System.Web.UI.ImageClickEventArgs)
Dim row As GridViewRow = DirectCast(DirectCast(sender, ImageButton).NamingContainer, GridViewRow)
DirectCast(row.NamingContainer, GridView).SelectedIndex = row.RowIndex
End Sub
aspx:
<asp:TemplateField HeaderText="Select">
<ItemTemplate >
<asp:ImageButton ID="ImgSelect" OnClick="ImgSelect_Click" runat="server" />
</ItemTemplate>
</asp:TemplateField>
I have done this plenty times in c#. Maybe I can help you out.
Some things I do different than you:
I call CellContentClick opposed to RowCommand.
2. I think check to see, which cell in the row was clicked
3. Check that the row is not null
4. If it was my image button cell, get my row data
1.
private void dgv_CellContentClick(object sender, DataGridViewCellEventArgs e)
2.
if (e.ColumnIndex == dgv.Columns["btnClmn"].Index)
3.
if (dgvAllApplication.CurrentRow != null)
4.
txtName.Text = dgv.CurrentRow.Cells["App_Name"].Value.ToString();
Good Luck!
you can create on click event on the imagebutton and do whatever you need to do on that row from there
protected void ImageButton1_Click(object sender, ImageClickEventArgs e)
{
ImageButton img = (ImageButton)sender;
GridViewRow row = (GridViewRow)img.Parent.Parent;
//do stuff
//find label in the same row
Label lbl = (Label)row.FindControl("Label1");
}

Dynamically add drop down lists and remember them through postbacks

I'm trying to figure out the best way to achieve this:
I need to show a drop down list when the page loads, the default selected value is nothing, or an empty element (such as "-"). When the user selects one value from the list, another drop down list is added below the first one, and so on.
My problem with this is how you make the page to remember the drop down lists created through postbacks and the values they have selected? Do I have to use some kind of array to store them or something?
Thank you very much.
EDIT: I did this example for dynamically add drop down lists and suscribe them to an event handler but the event won't fire.
EDIT 2: Changed the code because I labelled for VB.Net and posted something in C#. I update the code with some trivial improvements, but still won't fire the event :(
Private myDdlArray As New List(Of DropDownList)
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
If (Session("ddl") Is Nothing) Then
Session("ddl") = myDdlArray
End If
If (Session("ddlcount") Is Nothing) Then
Session("ddlcount") = 0
End If
myDdlArray = CType(Session("ddl"), List(Of DropDownList))
Dim myDdl As New DropDownList
myDdl = New DropDownList
For Each myDdl In myDdlArray
panel.Controls.Add(myDdl)
panel.Controls.Add(New LiteralControl("<br/>"))
Next
Session("ddl") = myDdlArray
End Sub
Protected Sub btn_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles btn.Click
myDdlArray = CType(Session("ddl"), List(Of DropDownList))
Dim ddlCount As Integer = CInt(Session("ddlcount")) + 1
Dim newDdl As New DropDownList
newDdl.ID = String.Format("ddlNPKMaterial{0}", ddlCount)
newDdl.AutoPostBack = True
AddHandler newDdl.SelectedIndexChanged, AddressOf cbo_SelectedIndexChanged
newDdl.Items.Add("Uno")
newDdl.Items.Add("Dos")
newDdl.Items.Add("Tres")
myDdlArray.Add(newDdl)
panel.Controls.Clear()
Dim myDdl As New DropDownList
myDdl = New DropDownList
For Each myDdl In myDdlArray
panel.Controls.Add(myDdl)
panel.Controls.Add(New LiteralControl("<br/>"))
Next
Session("ddl") = myDdlArray
Session("ddlcount") = ddlCount + 1
End Sub
Protected Sub btnReset_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles btnReset.Click
Session("ddl") = Nothing
Session("ddlcount") = Nothing
End Sub
Protected Sub btnShow_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles btnShow.Click
' Insert brain here
End Sub
Public Sub cbo_SelectedIndexChanged(ByVal sender As Object, ByVal e As System.EventArgs)
Response.Write(CType(sender, DropDownList).ID)
End Sub
Protected Sub Page_PreRender(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.PreRender
If (Session("ddl") Is Nothing) Then
panel.Controls.Clear()
End If
End Sub
in the aspx I have:
<form id="form1" runat="server">
<div>
<asp:Button ID="btn" runat="server" Text="Add" />
<asp:Button runat="server" ID="btnShow" Text="Tell me" />
<asp:Button runat="server" ID="btnReset" Text="Reset" />
<br />
<asp:Panel runat="server" ID="panel">
<asp:GridView runat="server" ID="grd">
<Columns>
<asp:BoundField HeaderText="Id" DataField="Id" />
</Columns>
</asp:GridView>
</asp:Panel>
</div>
</form>
I'll use OnLoad to recreate the controls, but if you're not storing the # of controls in ViewState (you could put it into Session, a HiddenField, Cookie, etc.) then you can do it in OnInit. Either way should still fire the postback event.
<asp:PlaceHolder id="phDdl" runat="server">
<asp:DropDwonList id="ddl" runat="server"
OnSelectedIndexChanged="ddl_SelectedIndexChanged" />
</asp:PlaceHolder>
<script runat="server">
int DdlIndex {
get {
object o = ViewState["_ddlIndex"];
if (o == null) {
ViewState["_ddlIndex"] = 0;
}
return (int)ViewState["_ddlIndex"];
}
set {
ViewState["_ddlIndex"] = value;
}
}
protected override void OnLoad(EventArgs e) {
base.OnLoad(e);
for (int i = 0; i < DdlIndex; i++) {
AddDropDownList(i);
}
}
void ddl_SelectedIndexChanged(object sender, EventArgs e) {
var ddl = (DropDownList)sender;
if (ddl.SelectedIndex > 0) {
AddDropDownList(DdlIndex++);
}
}
void AddDropDownList(int i) {
var ddl = new DropDownList();
ddl.Id = string.Format("ddl{0}", i);
ddl.SelectedIndexChanged += ddl_SelectedIndexChanged;
// add items
phDdls.Add(ddl);
}
</script>
You can simply have both dropdown lists exist in your asp code with only one visible on first page load. So something like...
<asp:DropDownList ID="mainDDL" Visible="true" runat="server">
<asp:ListItem Text="Elements here" Value="0" />
<asp:ListItem Text="More elements" Value="1" />
</asp:DropDownList>
<asp:DropDownList ID="dynamicDDL" Visible="false" runat="server">
<asp:ListItem Text="This is an element of the dynamic DDL" Value="3" />
</asp:DropDownList>
And then when, say the "More Elements" item selected, switch the dynamicDDL's visibility to true.
Then on each postback, on the Page_Load event, check what the value of mainDDL is. If it is 0, set dynamicDDL to have visible=true
Edit:
Okay, I took a stab at this. There is some headway in this, however, and maybe it will lead us to some clues.
To start off, we DO need an array to store this. We will need a static array of DDLs and a static integer to count our elements. These can be defined as...
Private Shared ddl_arr As DropDownList() = New DropDownList(100) {} 'max, 100 ddls.
Private Shared ddl_count As Integer = 0
Now, we'll need a panel to store our DDLs in. This is simple asp scripting, such as...
<asp:Panel ID="parentPanel" runat="server">
<asp:DropDownList ID="mainDDL" AutoPostBack="true" Visible="true" runat="server">
<asp:ListItem Text="Elements here" Value="0" />
<asp:ListItem Text="More elements" Value="1" />
</asp:DropDownList>
</asp:Panel>
So now, on our page load we will want to load any of our dropdowns that we have saved so far. This can be coded in a way such as..
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
Try
If TypeOf ddl_arr(0) Is DropDownList Then
For Each ddl As DropDownList In ddl_arr
add_ddl(ddl)
Next
End If
Catch ex As Exception ' this is a bad idea, but for brevity..
End Try
End Sub
Our add_ddl method will simply add our new drop down to the parentPanel.
Protected Sub add_ddl(ByVal ddl As DropDownList)
Try
parentPanel.Controls.Add(ddl)
'add any formatting you would like after each ddl here.
Catch ex As Exception
End Try
End Sub
And finally, our method when we change the ddl. This creates a brand new ddl, gives it an id (and whatever properties you may want for it), adds it to the array, increments our counter, and adds it to the page..
Protected Sub mainDDL_SelectedIndexChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles mainDDL.SelectedIndexChanged
Dim newDDL As DropDownList = New DropDownList()
newDDL.ID = "ddlID" & ddl_count 'we will need to store a new ID for each one. So, dynamically generate this.
'continue to add properties to your ddl in this fashion...
ddl_arr(ddl_count) = newDDL
ddl_count = ddl_count + 1
add_ddl(newDDL)
End Sub
This method should definitely check for the end of the array (among other things), but lets keep things simple. This code will only add a new DDL whenever you change the index of the ORIGINAL DDL. You will need to set each newly created DDL to have a method called (performing the above instructions) whenever the selected index changes for all of those newly crafted DDLs.
Hopefully this gets you in the right direction. This was way less organized than I'd hoped, sorry!
Dynamic controls are pretty tricky to create. I would start by reading this.
Create and add the dropdowns during the Page_Init method and generate the names based on the backing data that you're gathering. When getting input from them, either read their returned values off of the page using the FindControl() method or by getting the returned value of the control from the Request.Form[]. FindControl uses the id of the control, and Requst.Name uses the client id - you can get this information using the same function you use to generate the names.
Overall, ASP.NET isn't very good at working with generated controls, but by keeping the data you're working with in a model or datastructure separate from the controls that are being generated it isn't difficult to both create the controls and retrieve the results.

Getting row index from an ImageButton click in a GridView

I have a Gridview with ImageButtons added to a column via a templatefield. I've attached a function to the "OnClick" event.
Once in this function, how can I get the index of the row that has the button that has been clicked. It appears that all I have is the mouse coordinates on the page.
Instead of looping through the rows you can use this
<asp:ImageButton runat="server" id="ibtn1" ... RowIndex='<%# Container.DisplayIndex %>'
OnClick="button_click"/>
...
protected void button_click(object sender, EventArgs e){
ImageButton ibtn1 = sender as ImageButton;
int rowIndex = Convert.ToInt32(ibtn1.Attributes["RowIndex"]);
//Use this rowIndex in your code
}
Cast the sender to an ImageButton then cast the image button's NamingContainer to a row:
VB:
Dim btn as ImageButton = CType(sender, ImageButton)
Dim row as GridViewRow = CType(btn.NamingContainer, GridViewRow)
C#:
ImageButton btn = (ImageButton)sender;
GridViewRow row = (GridViewRow)btn.NamingContainer;
Would agree with bdukes that the simplest option is to use the CommandArgument. Bind your data's unique ID number into that property, then handle the _RowCommand event.
For example:
<asp:TemplateField >
<HeaderStyle Width="20" />
<ItemTemplate>
<asp:ImageButton ImageUrl="images/icons/iCal.png" CommandArgument='<%# Eval("Event_ID") %>' ToolTip="iCal" runat="server" Height="18" Width="18" />
</ItemTemplate>
</asp:TemplateField>
Protected Sub gv_RowCommand(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.GridViewCommandEventArgs) Handles gv.RowCommand
e.CommandArgument 'use this value in whatever way you like
End Sub
This is very good trick.
I have another trick also.
You can try it...
protected void userGridview_RowCommand(object sender, GridViewCommandEventArgs e)
{
if (e.CommandName == "Select")
{
GridViewRow rowSelect = (GridViewRow)(((Button)e.CommandSource).NamingContainer);
int rowindex = rowSelect.RowIndex;
}
}
It's a also good method.
The easiest way that I've found is to use the Command event over the Click event, and send the item ID as the command argument.
You could also loop over the rows in the GridView and compare the ImageButton in the row to the sender argument in your Click event.

ASP.Net: why is my button's click/command events not binding/firing in a repeater?

Here's the code from the ascx that has the repeater:
<asp:Repeater ID="ListOfEmails" runat="server" >
<HeaderTemplate><h3>A sub-header:</h3></HeaderTemplate>
<ItemTemplate>
[Some other stuff is here]
<asp:Button ID="removeEmail" runat="server" Text="X" ToolTip="remove" />
</ItemTemplate>
</asp:Repeater>
And in the codebehind for the repeater's databound and events:
Protected Sub ListOfEmails_ItemDataBound(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.RepeaterItemEventArgs) Handles ListOfEmails.ItemDataBound
If (e.Item.ItemType = ListItemType.Item) Or (e.Item.ItemType = ListItemType.AlternatingItem) Then
Dim removeEmail As Button = CType(e.Item.FindControl("removeEmail"), Button)
removeEmail.CommandArgument = e.Item.ItemIndex.ToString()
AddHandler removeEmail.Click, AddressOf removeEmail_Click
AddHandler removeEmail.Command, AddressOf removeEmail_Command
End If
End Sub
Sub removeEmail_Click(ByVal sender As Object, ByVal e As System.EventArgs)
Response.Write("<h1>click</h1>")
End Sub
Sub removeEmail_Command(ByVal sender As Object, ByVal e As CommandEventArgs)
Response.Write("<h1>command</h1>")
End Sub
Neither the click or command is getting called, what am I doing wrong?
Controls nested inside of Repeaters do not intercept events. Instead you need to bind to the Repeater.ItemCommand Event.
ItemCommand contains RepeaterCommandEventArgs which has two important fields:
CommandName
CommandArgument
So, a trivial example:
void rptr_ItemDataBound(object sender, RepeaterItemEventArgs e)
{
if (e.Item.ItemType == ListItemType.AlternatingItem || e.Item.ItemType == ListItemType.Item)
{
// Stuff to databind
Button myButton = (Button)e.Item.FindControl("myButton");
myButton.CommandName = "Add";
myButton.CommandArgument = "Some Identifying Argument";
}
}
void rptr_ItemCommand(object source, RepeaterCommandEventArgs e)
{
if (e.CommandName == "Add")
{
// Do your event
}
}
You need to handle the ItemCommand event on your Repeater. Here's an example.
Then, your button clicks will be handled by the ListOfEmails_ItemCommand method. I don't think wiring up the Click or Command event (of the button) in ItemDataBound will work.
If you're planning to use ItemCommand event, make sure you register to ItemCommand event in Page_Init not in Page_Load.
protected void Page_Init(object sender, EventArgs e)
{
// rptr is your repeater's name
rptr.ItemCommand += new RepeaterCommandEventHandler(rptr_ItemCommand);
}
I am not sure why it wasn't working for me with this event registered in Page_Load but moving it to Page_Init helped.
Here's an experiment for you to try:
Set a breakpoint on ListOfEmails_ItemDataBound and see if it's being called for postbacks.
You know what's frustrating about this?
If you specify an OnClick in that asp:Button tag, the build will verify that the named method exists in the codebehind class, and report an error if it doesn't... even though that method will never get called.

Resources