Solved

Handling postback with dynamically created buttons

Posted on 2006-11-08
11
638 Views
Last Modified: 2008-02-01
Hi,

I am creating an array of buttons and inserting them into a table. trouble is the event handling for the buttons doesnt occur after post back unless I re-create the buttons in the onload event of the code behind.

As after post back I want to re-create the buttons with different render options (I neeed to insert extra cells into a dynamic table depending on the button pressed) but with the same CommandNames as the original render I run into problems as I am effectivly creating the buttons twice. i.e the first buttonarray I create on page load has 10 buttons with commandnames going from 1-10 after post back, in order to gain access to the command name I need to create the buttons again (1-10), when I create the new array of buttons it gives the commandName 11-20.

How do I either preserve the button array through postback (viewstate doesnt seem to work) or access the button handle without re-creating the buttons?  Code Below:


Partial Class shop_components_BrowseBar
    Inherits System.Web.UI.UserControl
    Dim ButtonArray(0) As LinkButton
    Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load


        '### select cetagories on e at a time and them

        Dim CatReader As MySqlDataReader = CreateReader("SELECT * FROM Categories")

        '# set options for the table
        CategoryTbl.BorderStyle = BorderStyle.None
        CategoryTbl.BorderColor = Drawing.Color.Transparent
        CategoryTbl.BorderWidth = 0

        Do While CatReader.Read '# loop through categores
            '#Add category to table
            AddCat("<b>" & CatReader("PrimeCat") & "</b>", Drawing.Color.Transparent, CatReader("ID"))
            'resize the button array
            ReDim Preserve ButtonArray(UBound(ButtonArray) + 1)
        Loop

    End Sub


    Sub AddCat(ByVal CellText As String, ByVal CellColour As Drawing.Color, ByVal ID As String)
        Dim objRow As TableRow = New TableRow
        Dim objCell As New TableCell

        ButtonArray(UBound(ButtonArray)) = New LinkButton

        With ButtonArray(UBound(ButtonArray))
            .Text = CellText
            .CommandName = ID
            AddHandler .Click, AddressOf ButtonHandler
        End With

        objCell.Controls.Add(ButtonArray(UBound(ButtonArray)))   '.Text = "<a href='" & HyperLink & "'>" & CellText & "</a>"
        objRow.BorderColor = Drawing.Color.Transparent
        objRow.Cells.Add(objCell)
        objRow.BackColor = CellColour
        objRow.Font.Size = "10"

        CategoryTbl.Rows.Add(objRow)
    End Sub

    Sub AddRow(ByVal CellText As String, ByVal CellColour As Drawing.Color, ByVal HyperLink As String)

        Dim objRow As TableRow = New TableRow
        Dim objCell As New TableCell
        If HyperLink = "" Then
            objCell.Text = CellText
        Else
            objCell.Text = "<a href='" & HyperLink & "'>" & CellText & "</a>"
        End If
        objRow.BorderColor = Drawing.Color.Transparent
        objRow.Cells.Add(objCell)
        objRow.BackColor = CellColour
        objRow.Font.Size = "10"

        CategoryTbl.Rows.Add(objRow)
    End Sub


    Public Sub ButtonHandler(ByVal sender As System.Object, ByVal e As System.EventArgs)
        '# destroy the button array
        ReDim ButtonArray(0)
        '# get the cat ID of the button
        Dim PrimeCatID As Integer = CType(sender, LinkButton).CommandName
        '#  destroy the html from the original table so we can build it again
        CategoryTbl.Rows.Clear()
        ' get th ebutton id and populate the sub categories
        Dim SubCatReader As MySqlDataReader = CreateReader("SELECT * FROM subcategories WHERE CatID = " & PrimeCatID)

        Dim CatReader As MySqlDataReader = CreateReader("SELECT * FROM Categories")

        Do While CatReader.Read '# loop through categores
            '#Add category to table
            AddCat("<b>" & CatReader("PrimeCat") & "</b>", Drawing.Color.Transparent, CatReader("ID"))
            '# see if is needs children and if so, populat
            If PrimeCatID = CatReader("ID") Then
                Do While SubCatReader.Read
                    AddRow(" -" & SubCatReader("SubCat"), Drawing.Color.Transparent, "search.aspx?PrimeCat=" & CatReader("ID") & "&SubCat=" & SubCatReader("ID"))
                Loop
            End If
            ReDim Preserve ButtonArray(UBound(ButtonArray) + 1)
        Loop
    End Sub
End Class
0
Comment
Question by:bhermer
  • 4
  • 3
  • 2
  • +1
11 Comments
 
LVL 4

Expert Comment

by:Xeavn
ID: 17902630
I would instead use an ArrayList and set it up to use either ViewState or Session.

Protected Property alMyButtonList As ArrayList
      Get
            If Session("MyButtons") Is Nothing Then
                  Session("MyButtons") = New ArrayList()
            End If
            Return Session("MyButtons")
      End Get
      Set(ByVal Value As ArrayList)
            Session("MyButtons") = Value
      End Set
End Property 'alMyButtonList

This should allow you preserve the handles through postback, and access them again.

You will have to change a few things around, since an ArrayList will work a little different than an normal array, but looking at what your doing it seems the better choice, and you will be able to avoid all your ReDim's that you are doing with your current array. If you aren't familar with ArrayList's I can help there as well.

If you really want to keep using a regular Array, you can probably set one up as a Property to use Session as well.


0
 
LVL 23

Expert Comment

by:Jens Fiederer
ID: 17906375
Another suggestion that does not DIRECTLY address your question, but might be useful:

Generate ALL the controls in the original Load event.  After that, simply remove the VISIBILITY of the controls you do NOT want showing.  Controls that are not visible are not even sent to the client, so you are not clogging the pipes.
0
 
LVL 7

Expert Comment

by:ExpertAdmin
ID: 17914073
I think the solution here is to wrap a [If  Not IsPostback Then] around the line that is calling SubPageLoad, or around the control creation code in SubPageLoad:
 If  Not IsPostback Then
      '# set options for the table
        CategoryTbl.BorderStyle = BorderStyle.None
        CategoryTbl.BorderColor = Drawing.Color.Transparent
        CategoryTbl.BorderWidth = 0

        Do While CatReader.Read '# loop through categores
            '#Add category to table
            AddCat("<b>" & CatReader("PrimeCat") & "</b>", Drawing.Color.Transparent, CatReader("ID"))
            'resize the button array
            ReDim Preserve ButtonArray(UBound(ButtonArray) + 1)
        Loop
End If

M@
0
 
LVL 4

Author Comment

by:bhermer
ID: 17914914
Thanks for the comments peeps, my comments below:

Xeavn, A little help on this would be appreciated, I have no idea how to use an ArrayList as buttons!

jensfiederer, Problem I have with this is I still have the issue of getting the button ID after the second time it posts back as I lose the reference to the original control when  I recreate the buttons

ExpertAdmin, If I do this then it wont capture the handle event of the pressed button as it hasent been created when it posts back! so I dont get to do anything on postback!

The bit I dont understand is on postback:

1)  I re-create the buttons in the onload event so I can capture the button event, giving each an incremental command name of 1-10, this matches the original command names on the first render

2) I capture the button press event :

    Public Sub ButtonHandler(ByVal sender As System.Object, ByVal e As System.EventArgs)

3) I read the command name attached to the button that was pressed

    Dim PrimeCatID As Integer = CType(sender, LinkButton).CommandName

4) I 'Try' to destroy the buttons I have created

    ReDim ButtonArray(0)

5) I re-create the buton array, again giving each a command name from 1-10, BUT when I look in the rendered page html the buttons have taken the numbers 11-20, i.e
On initial page load the first button has this on its click code
     javascript:__doPostBack('BrowseBar1$ctl03','')   : notice the 03

But once I have re-rendered the page after postback the ccode is:
     javascript:__doPostBack('BrowseBar1$ctl13','')    : notice the 13

Why is it renumbering the ommand names? I am oviously not destrioying the original buttons with the ReDim ButtonArray(0), but how else can I ensure they are gone?

0
 
LVL 23

Accepted Solution

by:
Jens Fiederer earned 200 total points
ID: 17915167
What I was saying is DON'T recreate the buttons.  Create them once, and then keep only the ones you want to use visible.  That way you have a fixed hierarchy, fixed ids, etc.
0
How to improve team productivity

Quip adds documents, spreadsheets, and tasklists to your Slack experience
- Elevate ideas to Quip docs
- Share Quip docs in Slack
- Get notified of changes to your docs
- Available on iOS/Android/Desktop/Web
- Online/Offline

 
LVL 7

Expert Comment

by:ExpertAdmin
ID: 17915194
I think you have to capture the event BEFORE the load event by calling your event code in the INIT function. I have gone through the exact same thing with dynamic controls...MS apparently never considered that we would actually want to create layouts based on data.

M@
0
 
LVL 4

Author Comment

by:bhermer
ID: 17915207
Hi, Thats a good idea, but how do I do that, I tried storing them in the ViewState, and got an error. So I tried storing them in a session  var, but they just dont come back, the button handler event doesnt get fired, my code:

        If IsPostBack = False Then
            Dim CatReader As MySqlDataReader = CreateReader("SELECT * FROM Categories")

            '# set options for the table
            CategoryTbl.BorderStyle = BorderStyle.None
            CategoryTbl.BorderColor = Drawing.Color.Transparent
            CategoryTbl.BorderWidth = 0

            Do While CatReader.Read '# loop through categores
                '#Add category to table
                AddCat("<b>" & CatReader("PrimeCat") & "</b>", Drawing.Color.Transparent, CatReader("ID"))
                'resize the button array
                ReDim Preserve ButtonArray(UBound(ButtonArray) + 1)
            Loop
            Session("ButtonArray") = ButtonArray
        Else
            ButtonArray = Session("ButonArray")
        End If
0
 
LVL 4

Author Comment

by:bhermer
ID: 17915235
ExpertAdmin , Whcih part of my code would I put there, would I cerate the buttons there? sorry if that is a bit of an ignorant question!?
0
 
LVL 7

Assisted Solution

by:ExpertAdmin
ExpertAdmin earned 150 total points
ID: 17915270
No, it is not ignorant at all. I found this particular problem to be very, very frustrating and there is very little in the way of useful information about it out there.

I am trying to remember how I got around this problem. I am pretty sure that I moved the event definition to the init function so that it would be called before my dynamic controls would be destroyed and recreated in the Load event. So if you have a function called "Button_clicked" that handles the click event for your dynamic button, you would define the event in the init section.

The other problem is that you are working in VB and I did it in C#. I am not as familiar with the way VB handles events.

Maybe someone else could talk bhermer through that part of it.

M@
0
 
LVL 4

Assisted Solution

by:Xeavn
Xeavn earned 150 total points
ID: 17915913
Feel free to give this a try Bhermer. I don't know if I did it exactly right, but It should be pretty close. Maybe it will work for you. Instead of removing your buttons and readding them, I just stored them in an arraylist, and reused them as needed.

    Protected Property alMyButtonList As ArrayList
        Get
            If Session("MyButtons") Is Nothing Then
                Session("MyButtons") = New ArrayList()
            End If
            Return Session("MyButtons")
        End Get
        Set(ByVal Value As ArrayList)
            Session("MyButtons") = Value
        End Set
    End Property 'alMyButtonList

    Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
        '### select cetagories one at a time and them
        If Not Page.IsPostBack Then
            Dim CatReader As MySqlDataReader = CreateReader("SELECT * FROM Categories")

            '# set options for the table
            CategoryTbl.BorderStyle = BorderStyle.None
            CategoryTbl.BorderColor = Drawing.Color.Transparent
            CategoryTbl.BorderWidth = 0

            Do While CatReader.Read '# loop through categores
                '#Add category to table
                AddCat("<b>" & CatReader("PrimeCat") & "</b>", Drawing.Color.Transparent, CatReader("ID"))
                'resize the button array
                'ReDim Preserve ButtonArray(UBound(ButtonArray) + 1)
            Loop
        End If
    End Sub


    Sub AddCat(ByVal CellText As String, ByVal CellColour As Drawing.Color, ByVal ID As String)
        Dim objRow As TableRow = New TableRow
        Dim objCell As New TableCell

        Dim tmpLinkButton As New LinkButton
        tmpLinkButton.Text = CellText
        tmpLinkButton.CommandName = ID
        AddHandler tmpLinkButton.Click, AddressOf ButtonHandler
       
        If Not alMyButtonList.Contains(tmpLinkButton) Then
            alMyButtonList.Add(tmpLinkButton)
        Else
            'Don't Add the button, since it already exists, just reuse the old one.
        End If
   
        Dim myIndex As Integer = alMyButtonList.IndexOf(tmpLinkButton)

        'ButtonArray(UBound(ButtonArray)) = New LinkButton

        'With ButtonArray(UBound(ButtonArray))
        '    .Text = CellText
        '    .CommandName = ID
        '    AddHandler .Click, AddressOf ButtonHandler
        'End With
       

        objCell.Controls.Add(alMyButtonList.Item(myIndex))   '.Text = "<a href='" & HyperLink & "'>" & CellText & "</a>"
        objRow.BorderColor = Drawing.Color.Transparent
        objRow.Cells.Add(objCell)
        objRow.BackColor = CellColour
        objRow.Font.Size = "10"

        CategoryTbl.Rows.Add(objRow)
    End Sub

    Sub AddRow(ByVal CellText As String, ByVal CellColour As Drawing.Color, ByVal HyperLink As String)

        Dim objRow As TableRow = New TableRow
        Dim objCell As New TableCell
        If HyperLink = "" Then
            objCell.Text = CellText
        Else
            objCell.Text = "<a href='" & HyperLink & "'>" & CellText & "</a>"
        End If
        objRow.BorderColor = Drawing.Color.Transparent
        objRow.Cells.Add(objCell)
        objRow.BackColor = CellColour
        objRow.Font.Size = "10"

        CategoryTbl.Rows.Add(objRow)
    End Sub


    Public Sub ButtonHandler(ByVal sender As System.Object, ByVal e As System.EventArgs)
        '# destroy the button array
        'ReDim ButtonArray(0)
        '# get the cat ID of the button
        Dim PrimeCatID As Integer = CType(sender, LinkButton).CommandName
        '#  destroy the html from the original table so we can build it again
        CategoryTbl.Rows.Clear()
        ' get the button id and populate the sub categories
        Dim SubCatReader As MySqlDataReader = CreateReader("SELECT * FROM subcategories WHERE CatID = " & PrimeCatID)

        Dim CatReader As MySqlDataReader = CreateReader("SELECT * FROM Categories")

        Do While CatReader.Read '# loop through categores
            '#Add category to table
            AddCat("<b>" & CatReader("PrimeCat") & "</b>", Drawing.Color.Transparent, CatReader("ID"))
            '# see if is needs children and if so, populat
            If PrimeCatID = CatReader("ID") Then
                Do While SubCatReader.Read
                    AddRow(" -" & SubCatReader("SubCat"), Drawing.Color.Transparent, "search.aspx?PrimeCat=" & CatReader("ID") & "&SubCat=" & SubCatReader("ID"))
                Loop
            End If
            'ReDim Preserve ButtonArray(UBound(ButtonArray) + 1)
        Loop
    End Sub
End Class
0
 
LVL 4

Author Comment

by:bhermer
ID: 17929663
Right, I have solved this problem by completely changing my plan of attack, what I have done is:

1) Create a table and put my dynamic buttons in it, giving the button command name and the Cell ID where the button sits the same name
2) On postback I recreate the same table exactly so I can capture the button handle
3) on the button handle event I analys which button was pressed, I then cycle through the table looking for the Cell ID that matches the button command name, once I have this I use CategoryTbl.Rows.AddAt command to add my sub category cells below the relevant Cell!

The problem I had was trying to destroy the buttons and re-create them with the SAME command name, NET just wont let you do that for some reason, this way I dont destroy them, just create them on every page load and dynamically insert content at the right place.

Thanks for your partisipation, always helps having ideas bounced off you. I will spluit the points as without your ideas I wouldnt have thought of teh solution.
0

Featured Post

What Security Threats Are You Missing?

Enhance your security with threat intelligence from the web. Get trending threat insights on hackers, exploits, and suspicious IP addresses delivered to your inbox with our free Cyber Daily.

Join & Write a Comment

Suggested Solutions

Wouldn’t it be nice if you could test whether an element is contained in an array by using a Contains method just like the one available on List objects? Wouldn’t it be good if you could write code like this? (CODE) In .NET 3.5, this is possible…
Today I had a very interesting conundrum that had to get solved quickly. Needless to say, it wasn't resolved quickly because when we needed it we were very rushed, but as soon as the conference call was over and I took a step back I saw the correct …
In this tutorial you'll learn about bandwidth monitoring with flows and packet sniffing with our network monitoring solution PRTG Network Monitor (https://www.paessler.com/prtg). If you're interested in additional methods for monitoring bandwidt…
Polish reports in Access so they look terrific. Take yourself to another level. Equations, Back Color, Alternate Back Color. Write easy VBA Code. Tighten space to use less pages. Launch report from a menu, considering criteria only when it is filled…

743 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

12 Experts available now in Live!

Get 1:1 Help Now