Search code examples
asp.netvb.netdynamicviewstate

Persisting Dynamic ReadOnly Field Past Initialisation


I create a readonly textbox dynamically on page load/init and on the first load (not IsPostBack) I set the text. I also have a button on the page with a click event that changes the content of the textbox. Using another button, I read the text of the textbox. The problem is as follows:

  1. If I click the button that changes the textbox and then click the button that reads the text of the textbox, it gets it fine.

  2. If I just load the page and click the button that reads the text of the textbox, it brings back an empty string

I need both scenarios to bring back a result - whether it's the original text or the new programmatically changed text.

Example code:

Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
    Dim Textbox As New TextBox()
    Textbox.ID = "Bob"
    Textbox.ReadOnly = True
    If Not IsPostBack Then
        Textbox.Text = "Initial Text"
    End If
    Content.Controls.Add(Textbox)

    Dim Button As New Button()
    Button.Text = "Change text"
    AddHandler Button.Click, AddressOf Button_Click
    Content.Controls.Add(Button)
End Sub

Protected Sub Button_Click(ByVal sender As Object, ByVal e As EventArgs)
    'FindControlRecursive is locally defined - just finds a control based on root control and ID
    CType(FindControlRecursive(Content, "Bob"), TextBox).Text = "Hmmmmm"
End Sub

Private Sub Submit_Click(sender As Object, e As EventArgs) Handles Submit.Click
    Dim Textbox As TextBox = CType(FindControlRecursive(Content, "Bob"), TextBox)
    MsgBox(Textbox.Text)
End Sub

I could just reinitialise the textbox each time but this is just an example and in practice I'll be pulling it from SQL and if I have several read only controls on the page I'd rather get the control to persist its text rather than going back to SQL each time.


Solution

  • I found two ways to solve this, the second being the more favoured approach (depending on situation):

    1. Specifically add the initial text to the viewstate and then set the text on each postback using the value from viewstate:

      If Not IsPostBack Then
          Textbox.Text = "Initial Text"
          ViewState("Bob") = Textbox.Text
      Else
          Textbox.Text = ViewState("Bob")
      End If   
      
    2. Add a PreRender event to the textbox that just sets it text to itself. This takes place after the ViewState starts being tracked so it's added to the viewstate via the control and is persisted across postbacks:

      If Not IsPostBack Then
          Textbox.Text = "Initial Text"
          AddHandler Textbox.PreRender, AddressOf PersistPastInitialLoad
      End If
      

      And add new sub:

      Protected Sub PersistPastInitialLoad(ByVal sender As Object, ByVal e As EventArgs)
          'This is just for example - would need refining for different types of controls
          CType(sender, TextBox).Text = CType(sender, TextBox).Text
      End Sub
      

    Essentially they both do the same thing - hold the text in viewstate - but it depends on implementation which is the best to use. The first implementation might be a bit easier to read/work with but it would result in redundant viewstate bloat if you click the button to change the textbox text (i.e. you'll have the initial text in viewstate against the viewstate key you created as well as the current text added to viewstate by the control onchange).