Dynamic ImageButton Handler

  • Thread starter Thread starter Jonathan Wood
  • Start date Start date
J

Jonathan Wood

I'm dynamically creating a table on my Web form. Among other things, each
row contains an edit control and an ImageButton that performs a task based
on the value in that edit control.

I'm having the following problems:

1. When I create the ImageButton, I set it's CommandArgument property to a
value that indicates which item it is associated with. But how do I figure
out and reference the edit control associated with the button?

2. Also, I set my handler for the ImageButton using the following code
(which the editor basically wrote for me):

btn.Command += new CommandEventHandler(btn_Command);

void btn_Command(object sender, CommandEventArgs e)
{
// ...
}

It compiles just fine but a breakpoint at the top of btn_Command never gets
hit. Can anyone tell me what I'm missing here?

Thanks for any tips!

Jonathan
 
Howdy Jonathan!

You haven't given quite enough information to determine why, but teh problem
is that the event is not wiring up correctly.
Here's a small class in VB.Net that demonstrates the salient points in
dynamically adding controls.

This control is a Server Control, but the basic functionality is similar to
what you describe.
1) You are interating through a collection and adding buttons each time.
2) Each Button has an event handler attached

To make it work, ensure that each button has a unique ID, this is easilly
accomplished by just appending the index of the collection item yer on, like
a counter, row index, item index, etc...


Public Class TestButtons
Inherits CompositeControl

Protected Overrides Sub CreateChildControls()
MyBase.Controls.Clear()
Dim upnl_Main As New UpdatePanel
With upnl_Main
.ID = "upnl_Main"
With .ContentTemplateContainer
Dim Div_Main As New HtmlGenericControl("div")
With Div_Main
.ID = "Div_Main"
For RowCounter As Integer = 0 To 10
Dim btn As New Button
With btn
.ID = "btn_" + CStr(RowCounter)
.Text = "Button " + CStr(RowCounter)
.CommandName = "edit"
.CommandArgument = CStr(RowCounter)
AddHandler .Command, AddressOf
Me.btn_OnCommand
End With
.Controls.Add(btn)
Next RowCounter
End With
.Controls.Add(Div_Main)
End With
End With
MyBase.Controls.Add(upnl_Main)
End Sub

Protected Sub btn_OnCommand(ByVal sender As Object, ByVal e As
System.EventArgs)
Dim btn As Button = TryCast(sender, Button)
If btn IsNot Nothing Then
Dim CommandName As String = btn.CommandName
Dim CommandArgument As String = btn.CommandArgument
Select Case CommandName
Case "edit"
'Call your edit method with your commandargument as
the argument.
Case "delete"
'Call your delete method with your commandargument
as the argument.
End Select
End If
End Sub

End Class
 
Thanks for the detailed response. Giving the controls a unique ID could be
used to correctly access the correct edit control (my second issue).

However, I can't see what you are doing that I'm not with regards to wiring
up the button event. I used to be a regular columnist for Visual Basic
Programmer's Journal but VB.NET looks a bit strange to me these days. But
all I see you are doing is calling the AddHandler method, which should be
equal to C#'s btn.Command += new CommandEventHandler(btn_Command). Did you
see something I'm missing that I just don't see?

Thanks again.

Jonathan
 
I just realised I only answered half your question.
The second part was, "how do you associate the button with the edit
control"?

When you create your button and append the RowIndex to the end of its' ID,
do the same for your edit control.
Lets say you were using a TextBox, and the ID's were:

ID = "txt_" + CStr(RowIndex)

You could then find the controls when you retrieve your CommandArgument

Protected Sub btn_OnCommand(ByVal sender As Object, ByVal e As
System.EventArgs)
Dim btn As Button = TryCast(sender, Button)
If btn IsNot Nothing Then
Dim CommandName As String = btn.CommandName
Dim CommandArgument As String = btn.CommandArgument
'Find the associated TextBox
Dim txt as TextBox = TryCast(Me.FindControl("txt_" +
CommandArgument), TextBox)
If txt IsNot Nothing
Select Case CommandName
Case "edit"
'Call your edit method with your commandargument
as the argument.
Case "delete"
'Call your delete method with your
commandargument as the argument.
End Select
End If
End If
End Sub
 
As I said, there's not quite enough code there to determine why it isn't
wiring up correctly.
If all your other code is solid, then it should work.

The two major reasons it might not be working is the Unique ID issue or that
you aren't recreating the controls and handlers
when the page is initially rebuilt just after the postback in order to wire
up (or match a control ID to a Handler).

The second issue can cause fits of frustration, but is generally solved by
judiciously storing the values you use to build your rows in the viewstate.

For Example, if you are grabbing a datatable and using that to build the
table rows in your control, save that datatable in a property which accesses
the Viewstate rather than a local variable.
The initial rebuild and wireup is build based on the ViewState (and
ControlState) by default (altho this behavior can be overridden)

IE:

Private Property MyDataTable() As DataTable
Get
Return ViewState("MyDataTable")
End Get
Set(ByVal value As DataTable)
ViewState("MyDataTable") = value
End Set
End Property

Instead of something like:

Protected m_MyDataTable As DataTable
Private Property MyDataTable() As DataTable
Get
Return m_MyDataTable
End Get
Set(ByVal value As DataTable)
m_MyDataTable = value
End Set
End Property

Private Property MyDataTable
 
The two major reasons it might not be working is the Unique ID issue or
that you aren't recreating the controls and handlers
when the page is initially rebuilt just after the postback in order to
wire up (or match a control ID to a Handler).

Doh! That appears to be the problem. If I change my load event so it
recreates the table (that contains the dynamic buttons) on the post back,
then my breakpoint gets hit as expected! I hadn't considered that the
controls must be recreated in order to process an event already initiated.
The second issue can cause fits of frustration, but is generally solved by
judiciously storing the values you use to build your rows in the
viewstate.

For Example, if you are grabbing a datatable and using that to build the
table rows in your control, save that datatable in a property which
accesses the Viewstate rather than a local variable.
The initial rebuild and wireup is build based on the ViewState (and
ControlState) by default (altho this behavior can be overridden)

I'm actually using ViewState already. Once the data has been read from the
database, I store it in ViewState so I can efficiently recreate the table.

The problem is that I need to modify the data in the table in response to
the button click. So this means I need to reconstruct the table, process the
event, and then construct the table again. Boy, that doesn't sound like the
most efficient way to handle this.

Thanks!

Jonathan
 
Back
Top