trouble with click events on dynamically created link buttons

A

Amoril

I've read quite a few different message on various boards and for some
reason I'm still having trouble wrapping my head around this viewstate
maintenance and trying to get these dynamically created link buttons
to stay wired up to their click events.

I have what is basically a simply survey question generation page. The
page first displays a few static fields and a dropdownlist of various
options for the user to select. When the user selects an option from
the list the page will generate a new table with 5 rows of textboxes,
drop down lists, and link buttons (to delete a row if desired). There
is also a static insert button to allow users to add additional rows
if needed.

Saving the data in the fields during postback isn't an issue, but I'm
stuck in two situations depending on how I adjust the code. First is
that I put the rebuilding of the controls in the Page_load and users
are forced to click twice on the static Insert Row button to add a row
or they have to click twice on a dynamic Delete Row link button to
remove a row. If I take the rebuilding of the controls out of the
Page_Load then the Insert Row button works fine, but clicking on a
Delete Row link button causes the click event to not fire and all the
dynamic controls disappear from the page.

Does anyone have any suggestions on what I need to do to fix this so
it's written correctly and will operate as intended? (if you need more
detail or code please ask)

Thank you for your help.

--Code Snippets (this setup requires 2 clicks on a button before the
click event appears to do anything--

Private Sub Page_Load(ByVal sender As System.Object, ByVal e As
System.EventArgs) Handles MyBase.Load

If Not IsPostBack Then
LoadQuestionTypes()
End If

RebuildControls()

End Sub
---------------------------
Private Sub ddlQuestionType_SelectedIndexChanged(ByVal sender As
System.Object, ByVal e As System.EventArgs) Handles
ddlQuestionType.SelectedIndexChanged

....
BuildEmptyFive()
....

End Sub
--------------------------
Private Sub BuildEmptyFive()

Dim IDArray As New ArrayList
Dim tblAnswers As New Table
Dim x As Integer

For x = 1 To 5

Dim row As New TableRow
Dim ID As String

ID = Left(System.Guid.NewGuid.ToString, 8)

Dim cell1 As New TableCell
cell1.Controls.Add(BuildTextBox("txtChoice-" & ID, 140))

Dim cell2 As New TableCell
cell2.Controls.Add(BuildDropDownList("ddlFamily-" & ID, 150,
"Family"))

Dim cell3 As New TableCell
cell3.Controls.Add(BuildDropDownList("ddlAttribute-" & ID, 150,
"Attributes"))

Dim cell4 As New TableCell
cell4.Controls.Add(BuildTextBox("txtScore-" & ID, 40))

Dim cell5 As New TableCell
cell5.Controls.Add(BuildLinkButton("lnkDelete-" & ID))

row.Cells.Add(cell1)
row.Cells.Add(cell2)
row.Cells.Add(cell3)
row.Cells.Add(cell4)
row.Cells.Add(cell5)

tblAnswers.Rows.Add(row)
IDArray.Add(ID)
Next

plhDynControls.Controls.Add(tblAnswers)

'Insert Array containing ID of each row
If IsNothing(ViewState.Item("IDArray")) Then
ViewState.Add("IDArray", IDArray)
Else
ViewState.Item("IDArray") = IDArray
End If

End Sub
------------------------------
Private Sub btnInsert_Click(ByVal sender As System.Object, ByVal e As
System.EventArgs) Handles btnInsert.Click

'Add a new ID to the viewstate which will cause a new row to be
inserted when the viewstate is rebuilt
Dim IDArray As ArrayList
IDArray = CType(ViewState.Item("IDArray"), ArrayList)
IDArray.Add(Left(Guid.NewGuid.ToString, 8))
ViewState.Item("IDArray") = IDArray

'RebuildControls() 'unremark this and remove from page_load to get
insert button to work perfectly (delete no workie though)

End If

End Sub
---------------------------------
Private Sub lnkDelete_Click(ByVal sender As System.Object, ByVal e As
System.EventArgs)

Dim IDArray As ArrayList
IDArray = CType(ViewState.Item("IDArray"), ArrayList)

Dim ID As String = Right(CType(sender, LinkButton).ID.ToString, 8)
IDArray.RemoveAt(IDArray.IndexOf(ID))

ViewState.Item("IDArray") = IDArray

End Sub
-----------------------------------
this is how I generate the link button dynamically
Private Function BuildLinkButton(ByVal name As String) As LinkButton

Dim lnkLink As New LinkButton
lnkLink.ID = name
lnkLink.Text = "Delete"
AddHandler lnkLink.Click, AddressOf lnkDelete_Click

Return lnkLink

End Function
-----------------------------------
Private Sub RebuildControls()

If IsNothing(ViewState.Item("IDArray")) Then
Exit Sub
End If

Dim IDArray As ArrayList
IDArray = CType(ViewState.Item("IDArray"), ArrayList)

Dim tblAnswers As New Table
Dim x As Integer

For x = 0 To IDArray.Count - 1

Dim row As New TableRow

Dim cell1 As New TableCell
cell1.Controls.Add(BuildTextBox("txtChoice-" &
Convert.ToString(IDArray.Item(x)), 140, Request.Form.Item("txtChoice-"
& Convert.ToString(IDArray.Item(x)))))

Dim cell2 As New TableCell
cell2.Controls.Add(BuildDropDownList("ddlFamily-" &
Convert.ToString(IDArray.Item(x)), 150, "Family",
Request.Form.Item("ddlFamily-" & Convert.ToString(IDArray.Item(x)))))

Dim cell3 As New TableCell
cell3.Controls.Add(BuildDropDownList("ddlAttribute-" &
Convert.ToString(IDArray.Item(x)), 150, "Attributes",
Request.Form.Item("ddlAttribute-" &
Convert.ToString(IDArray.Item(x)))))

Dim cell4 As New TableCell
cell4.Controls.Add(BuildTextBox("txtScore-" &
Convert.ToString(IDArray.Item(x)), 40, Request.Form.Item("txtScore-" &
Convert.ToString(IDArray.Item(x)))))

Dim cell5 As New TableCell
cell5.Controls.Add(BuildLinkButton("lnkDelete-" &
Convert.ToString(IDArray.Item(x))))

row.Cells.Add(cell1)
row.Cells.Add(cell2)
row.Cells.Add(cell3)
row.Cells.Add(cell4)
row.Cells.Add(cell5)

tblAnswers.Rows.Add(row)

Next

plhDynControls.Controls.Add(tblAnswers)

End Sub
 
G

Guest

Amoril

You cannot use NewGuid function for ids because it'll generate different id
on every call (it means also on every postback) so events for all dynamically
created controls will not be fired. And you want be able to find a value
entered by the user. Use x (loop counter) with contact prefix instead. Have
also in mind you should recreate controls in page_init (but do not access
viewstate at this stage because it’s simply not collected yet) as they will
automatically recreate their state.

Hope it helps
 
A

Amoril

The only place that I use NewGuid to assign the ID's is in the
BuildEmptyFive sub (only fired after the user selects an item from the
drop down), for RebuildingControls sub I pull the ID's out of the
IDArray in the ViewState, so that shouldn't be an issue.

Moving the RebuildControls() sub from Page_Load to Page_Init actually
made the issue worse, now when I click on the static Insert Row button
or the dynamics link buttons to delete a row, all the dynamic controls
disappear. The static button fires it's event, but the link buttons
don't. Perhaps I'm not understanding what you mean by that since
without accessing the IDArray in the viewstate I won't know how many
controls need to be recreated.

Any more detail you could provide would be appreciated.
 
G

Guest

Hi again,

Oh yes, you're right but no need for that. it's easier to use row index and
a constant prefix for a particular control type (attribute, score,etc). I'll
try to provide a fully working example later on today.

take care
 
G

Guest

Hi again,

Actually we have to use guid because you can delete row, which i didn't pick
up before. Anyway, i created fully working example for you. You should be
fine from this point

-- begin aspx code --

<%@ Page Language="VB" AutoEventWireup="false" CodeFile="Survey.aspx.vb"
Inherits="Survey" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title>Untitled Page</title>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:DropDownList runat="server" ID="questions" AutoPostBack="true">
<asp:ListItem Text="Please Select a Question..." />
<asp:ListItem Text="What are your names?" />
<asp:ListItem Text="Name all girlfriends you have had in your life" />
</asp:DropDownList>
<asp:panel runat="server" ID="container" />
<asp:panel runat="server" ID="surveyOptions">
<asp:Button ID="btnAddRow" runat="server" Text="Add row" />
<asp:Button ID="btnSubmit" runat="server" Text="Submit Survey"/>
</asp:panel>
</div>
</form>
</body>
</html>
-- end aspx code --

-- begin vb.net code --


Partial Class Survey
Inherits System.Web.UI.Page


Protected Sub Page_Load(ByVal sender As Object, ByVal e As
System.EventArgs) Handles Me.Load
RecreateRows()
End Sub


Protected Sub questions_SelectedIndexChanged(ByVal sender As Object, ByVal
e As System.EventArgs) Handles questions.SelectedIndexChanged

Const DefaultRowCount As Integer = 5

' clear everything
IDs.Clear()

If CType(sender, DropDownList).SelectedIndex >= 0 Then
' create x default empty rows
For i As Integer = 1 To DefaultRowCount
IDs.Add(GenerateId())
Next
End If

RecreateRows()

End Sub

Private Sub RecreateRows()

container.Controls.Clear()

For Each id As String In IDs
AddAnswerRow(id)
Next

surveyOptions.Visible = IDs.Count > 0

End Sub

Private Const RowIdPrefix As String = "row"
Private Const TextBoxIdPrefix As String = "txt"
Private Const DropDownListIdPrefix As String = "ddl"

Private Sub AddAnswerRow(ByVal id As String)

Dim panel As Panel
Dim textBox As TextBox
Dim linkButton As LinkButton
Dim dropDownList As DropDownList

' row panel
panel = New Panel()
panel.ID = RowIdPrefix & id

' answer text box
textBox = New TextBox()
textBox.ID = TextBoxIdPrefix & id

' delete button
linkButton = New LinkButton()
linkButton.ID = "btn" & id
linkButton.Text = "delete"
linkButton.CommandArgument = id
AddHandler linkButton.Command, New CommandEventHandler(AddressOf
DeleteAnswerRow)

dropDownList = New DropDownList()
dropDownList.ID = DropDownListIdPrefix & id
dropDownList.Items.Add(New ListItem("Value0", "0"))
dropDownList.Items.Add(New ListItem("Value1", "1"))
dropDownList.Items.Add(New ListItem("Value2", "2"))

panel.Controls.Add(textBox)
panel.Controls.Add(dropDownList)
panel.Controls.Add(linkButton)
container.Controls.Add(panel)

End Sub

Private Sub DeleteAnswerRow(ByVal source As Object, ByVal e As
CommandEventArgs)

Dim id As String = CType(e.CommandArgument, String)
Dim control As Control = container.FindControl(RowIdPrefix & id)

If (Not control Is Nothing) Then
container.Controls.Remove(control)

Dim index As Integer = IDs.IndexOf(id)
If index <> -1 Then
IDs.RemoveAt(index)
End If

End If

End Sub

Private ReadOnly Property IDs() As ArrayList
Get
Dim value As Object = ViewState("IDs")
If value Is Nothing Then
value = New ArrayList()
ViewState("IDs") = value
End If
Return value
End Get
End Property

Private Function GenerateId() As String
Return Guid.NewGuid().ToString("N")
End Function

Protected Sub btnAddRow_Click(ByVal sender As Object, ByVal e As
System.EventArgs) Handles btnAddRow.Click

Dim id As String = GenerateId()

IDs.Add(id)
AddAnswerRow(id)

End Sub

Protected Sub btnSubmit_Click(ByVal sender As Object, ByVal e As
System.EventArgs) Handles btnSubmit.Click

' obtain results
Dim textBox As TextBox
Dim dropDownList As DropDownList

For Each id As String In IDs

'
' text box value
'
textBox = CType(container.FindControl(TextBoxIdPrefix & id), TextBox)

If (Not textBox Is Nothing) Then
Dim textBoxValue As String = textBox.Text
End If

'
' drop down list selected value
'
dropDownList = CType(container.FindControl(DropDownListIdPrefix & id),
DropDownList)

If (Not dropDownList Is Nothing) Then
Dim dropDownListValue As String = dropDownList.SelectedValue
End If

Next


End Sub

End Class

-- end vb.net code --
Milosz
 

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

Forum statistics

Threads
473,968
Messages
2,570,152
Members
46,697
Latest member
AugustNabo

Latest Threads

Top