Discussion on Development in Several Different Flavours

ASP.Net Dynamic Controls (Part 1)

August 27, 2006 | Tagged:

If you have ever tried to work with dynamic controls in ASP.Net you’ve probably run afoul of the tons of limitations and pulled a good number of hairs out. It shouldn’t be this difficult should it? I don’t think so! The main issue with dynamic controls is that they don’t really fit into the ASP.Net Page framework since they don’t exist when ASP.Net is doing all its behind the scenes work.

These are the typical problems with dynamic controls:

  1. Controls must be created in Page_Init or Page_Load for Event Handlers to work
  2. All dynamic controls must be regenerated every time the page loads
  3. Collecting the data after a postback is difficult since controls no longer exist
  4. Control constructors don’t set many parameters which makes configuration tedious
  5. The methods available to add a control to the page vary depending on where it is created

As you can see the rigidity of the ASP.Net framework causes the programmer a lot of grief compared to languages like PHP that use raw HTML elements for everything and have no server side controls. You can’t really complain though since at the same time as it is limiting it is very powerful and saves a lot of programming hours. In this series I will address some of the issues listed above and make this a very useful resource for programmers like myself who like to live on the edge of the framework. Alright so first things first lets take a look at a basic example of how to create a dynamic control.

Sub Page_Load
  Dim cmd As New Button()
  cmd.Text = "Button 1"
  AddHandler cmd.Click, AddressOf Button_Click
  phPage.Controls.Add(cmd)
End Sub
…
<asp:PlaceHolder ID="phPage" runat="server />

This very simple example illustrates how to create a dynamic button on a web form. It would be very simple using a loop to create an arbitrary number of buttons each with different text and different event handlers, but wait. Why would you go through all that pain to create so many event handlers when it could be done using one? If you are not familiar with the Command Event you are really missing out. It operates in the same way as a standard Click except it allows for two parameters to the Event Sub. Lets take another look at our example using this method.

Sub Page_Load
  Dim cmd As New Button()
  cmd.Text = "Button 1"
  cmd.CommandName = "ButtonGroupName"
  cmd.CommandArgument = "WhichButtonInGroup"
  AddHandler cmd.Command, AddressOf Button_Click
  phPage.Controls.Add(cmd)
End Sub

Sub Button_Click (ByVal s As Object, ByVal e As CommandEventArgs)
  If e.CommandName = "ButtonGroupName" Then
    If e.CommandArgument = "WhichButtonInGroup" Then
      …
    End If
  End If
End Sub
…
<asp:PlaceHolder ID="phPage" runat="server />

Any type of button can be used for this since ASP.Net considers them all equally. However, when it comes to other non-button controls it becomes a bit more challenging. Just say you wanted to create a list from a database that gives the viewer a set of tools for each item in the list, for example we need a checkbox to record a boolean value. The first step is to generate the list with the check boxes. Some people like to do this in a Sub and others directly on the page. In either case you will still need a PlaceHolder control. Lets looks at code to generate the checkboxes.

Sub Generate_List (ByRef rdr As SqlDataReader)
  Dim chk As CheckBox
  While rdr.Read()
    phPage.Controls.Add(New LiteralControl("HTML and Data"))
    chk = New CheckBox()
    chk.ID = rdr("item_id")
    phPage.Controls.Add(chk)
    phPage.Controls.Add(New LiteralControl("HTML and Data"))
  End While
End Sub
…
<asp:PlaceHolder ID="phPage" runat="server />

There is not much difference between this and the dynamic button code above. We don’t have an event handler for the checkbox because we don’t want anything to happen after clicking each checkbox instead there will be one save button at the bottom of the page. The major thing to note from this example is the use of the ID field. Without this field every checkbox would be the same and we wouldn’t know which goes with each entry in the list when processing the form on postback. So lets see how we would use the data, remember from earlier that dynamic controls disappear on postback so we can’t use ID.Checked to find out the value.

Sub Save_Click (ByVal s As Object, ByVal e As EventArgs)
  …
  rdr = cmd.ExecuteDataReader()
  While rdr.Read()
    If String.IsNullOrEmpty(Request.Form(rdr("item_id"))) Then
      … 'Checkbox wasn't checked
    Else
      … 'Checkbox was checked
    End If
  End While
  rdr.Close()
  …
End Sub

This fragment probably looks a little strange but let me explain what is going on. We can’t access the posted value of each checkbox through its control name natively since after postback it no longer actually exists in the page. What we do have left is the posted values in the Request.Form collection just as in a standard HTML form. So we just need to go through the list of checkboxes that we added and check what the value is set to. The same process will work on any other form data containers in the same way. This next example will be the last for this part and shows the issues with advanced techniques.

Sub Page_Load
  Dim btn As Button
  …
  rdr = cmd.ExecuteDataReader()
  While rdr.Read()
    btn =  New Button()
    btn.Text = "X"
    btn.CommandName = "Delete"
    btn.CommandArgument = rdr("item_id")
    AddHandler btn.Command, AddressOf Button_Click
    phPage.Controls.Add(btn)
  End While
  rdr.Close()
  …
End Sub

Sub Button_Click (ByVal s As Object, ByVal e As CommandEventArgs)
  … ' Delete list item whose delete button was click
End Sub
…
<asp:PlaceHolder ID="phPage" runat="server />

You are probably thinking “Everything looks alright to me, what’s wrong with it?”. Well the answer lies in the order of page events. Page_Load fires before any user event handlers so you have already regenerated the list before the element is deleted which means its still on the page. “Well that is a simple fix right?, just move the list generation code into a function and call it at the end of the Button_Click event”. Sure sounds good to me, unfortunately if you create the buttons after Page_Load the event handlers won’t actually work and your page will be filled with dead buttons. “So what is the answer” you ask. For now all I will say is the solution requires a bit of JavaScript. Of course there are other possibilities as is common in programming and they are all very hacky so pick your poison if you like, if not stay tuned for the next part. Same RSS channel same RSS time, which doesn’t really make sense but gives me a chance to use that very excellent anachronism.

ASP.Net Dynamic Controls (Part 2) »
ASP.Net Dynamic Controls (Part 3) »
ASP.Net Dynamic Controls (Part 4) »

19 Comments »

RSS feed for comments on this post. TrackBack URI

Leave a comment