Here it is buzgub.

I hope you don't mind too much about VB.NET. As usual will be checking the wiki all the day if you want further details.

Public Class WebForm1
    Inherits System.Web.UI.Page

#Region " Code généré par le Concepteur Web Form "

    'Cet appel est requis par le Concepteur Web Form.
    <System.Diagnostics.DebuggerStepThrough()> Private Sub InitializeComponent()
    End Sub

    Dim myButton As New Button()
    Dim myLabel As New Label()

    Private Sub Page_Init(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Init
        myButton.ID = "btnDynamic"
        myButton.Text = "Press me"
        AddHandler myButton.Click, AddressOf Button_Click

        myLabel.ID = "lblDynamic"
        myLabel.Text = "Not clicked yet"

        Page.FindControl("Form1").Controls.Add(myButton)
        Page.FindControl("Form1").Controls.Add(New LiteralControl("&nbsp;&nbsp;"))
        Page.FindControl("Form1").Controls.Add(myLabel)

        'CODEGEN : cet appel de méthode est requis par le Concepteur Web Form
        'Ne le modifiez pas en utilisant l'éditeur de code.
        InitializeComponent()
    End Sub

#End Region

    Private Sub Button_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)
        myLabel.Text = "You clicked the DYNAMIC BUTTON"
    End Sub

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

End Class


I'm fine with the vb code, but the french threw me :)

Thanks for the example. Because I was creating different dynamic controls every time, it wasn't as simple as just loading them in the init event. The details of the dynamic controls are dictated by the contents of some other controls on the page, and this is where it gets interesting. In order to draw the table containing the controls correctly, I needed to know the contents of those few controls. However - to get the event to be handled properly, I believe that the controls must be drawn *before* the other controls viewstates have been loaded.

To get around it, I'm storing the states of the controls in Page.Session - I tried this.ViewState, but it seems ViewState isn't loaded until it's too late, either. I'd prefer to have something local to the page, but I think none of the page-state type things are loaded until later than I need. I hope I'm wrong, though. Am I?

Thanks again. I hope my explanation here makes sense.

--IainMcCoy


Yes. Makes sense. You are probably right about what you say. I haven't done any serious programming with ASP.NET in a while, even if I use it daily.

But I think I may have another solution for you. Stay tuned.

-- FrancoisDenisGonthier

New solution

I tried to invent a simple example of your problem. Here is what I wanted to do:

  1. A dropdown list containing different possible captions for a dynamic button I want to create.
  2. Once the user presses 'OK', the caption of the dynamic button is changed to reflect the item selected in the list.
  3. The dynamically created button handles an event that changes the value of a label.

Your solution using the Session object is probably a lot simpler than mine so you might as well stick with it. I just found it was a nice challenge to try to come up with a solution that is not using Page.Session.

Believe it or not, that shitty code does exactly what I wanted it to do:

WebForm1.aspx

<%@ Page Language="vb" AutoEventWireup="false" Codebehind="WebForm1.aspx.vb" Inherits="ControleDynamique.WebForm1"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//FR">
<HTML>
  <HEAD>
    <title>Weirdass dynamic controls demo</title>
</HEAD>
  <body >

    <form id="frmMain" method="post" runat="server">
                
                <asp:DropDownList id=lstControls runat="server">
                        <asp:ListItem Value="0">'OK' button</asp:ListItem>
                        <asp:ListItem Value="1">'Cancel' button</asp:ListItem>
                        <asp:ListItem Value="2">'Teehee' button</asp:ListItem>
                </asp:DropDownList>
                
                <asp:Button id=btnOK style="Z-INDEX: 102" runat="server" Text="OK"></asp:Button>
                <br><br>
                
                <asp:Panel id=pnlTarget runat="server"></asp:Panel>
                <br><br>
                
                <asp:Label runat="server" id="lblTarget"></asp:Label>
    </form>

  </body>
</HTML>

WebForm1.aspx.vb

Public Class WebForm1
    Inherits System.Web.UI.Page

    Protected WithEvents lstControls As System.Web.UI.WebControls.DropDownList
    Protected WithEvents pnlTarget As System.Web.UI.WebControls.Panel
    Protected WithEvents lblTarget As System.Web.UI.WebControls.Label
    Protected WithEvents btnOK As System.Web.UI.WebControls.Button

    Private btnDynamic As Button
    Private SelectedItem As String

    ' Required by VS.NET
    <System.Diagnostics.DebuggerStepThrough()> Private Sub InitializeComponent()
    End Sub

    Private Sub Page_Init(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Init
        Dim callerPage As WebForm1 = CType(Context.Handler, WebForm1)

        ' Variant of: http://www.aspalliance.com/kenc/passval.aspx
        ' I forgot about the Context.Handler trick.

        If Not callerPage.SelectedItem = "" Then
            btnDynamic = New Button()
            btnDynamic.ID = "DynamicButton"

            Select Case callerPage.SelectedItem
                Case "0"
                    btnDynamic.Text = "OK"
                Case "1"
                    btnDynamic.Text = "Cancel"
                Case "2"
                    btnDynamic.Text = "Teehee"
            End Select

            AddHandler btnDynamic.Click, AddressOf UselessEventHandler
            Me.pnlTarget.Controls.Add(btnDynamic)
            Me.SelectedItem = callerPage.SelectedItem
        End If

        ' That too is required by VS.NET
        InitializeComponent()
    End Sub

    Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        ' As soon as we have the value previously selected in the list,
        ' we recall the page.
        If Me.SelectedItem = "" Then
            Me.SelectedItem = Me.lstControls.SelectedItem.Value
            Server.Transfer("WebForm1.aspx", True)
        Else
            Me.lstControls.Items(CInt(Me.SelectedItem)).Selected = True
        End If
    End Sub

    Private Sub UselessEventHandler(ByVal sender As System.Object, ByVal e As System.EventArgs)
        Me.lblTarget.Text = "You clicked " + btnDynamic.Text
    End Sub
End Class

Here is the black magic:

  1. At first, the SelectedItem is '0'.

  2. At the first PostBack, I check if SelectedItem is set to something. If there is nothing, that means this is the first call of the page. I then set SelectedItem and Transfer the control to the same page on the server. Note the true parameter passed to Server.Transfer, I will explain it later. Server.Transfer is an handy command which transfers the control to another page on the server. The difference between it and Response.Redirect is that the control passes transparently to the other page without a new request to the server. The first instance of the WebForm1 class is then preserved on the server.

  3. The control then passes to Page_Init of the new instance of the page. The Content.Handler is checked for a SelectedItem value, if there is one, then we can create the dynamic button and assign it an event handler. Note that I set Me.SelectedItem so that Page_Load doesn't transfer the control to another instance again and again and again...

  4. At the Page_Load of the new instance, I simply set the selected value of the list to the right value.

  5. Postback events, like the one generated by the dynamic button, will be honoured because of the true parameter in Server.Transfer. In effect, this parameter tells the server to preserve the content of the Form and QueryString collection after transfering. The ViewState of the form is then preserved and the events are called, just as they would be without Server.Transfer.

I don't fully understand why and how the Content.Handler trick works but the rest of the explanation is probably right.

Thanks!

AspNetDynamicControls (last edited 2003-10-22 12:52:33 by 206)