Where did my ViewState go???

Discussion in 'ASP .Net Building Controls' started by Scott Nichols, Apr 13, 2004.

  1. I'm trying to build serval custom controls and I'm havinig trouble
    maintaining their viewstate. So I wanted to back it up to the most
    basic level - and now I'm more confused than ever.

    I have a web form where I've overridden two methods:

    Protected Overrides Function SaveViewState() As Object
    Return (MyBase.SaveViewState)
    End Function

    Protected Overrides Sub LoadViewState(ByVal savedState As Object)
    If Not (savedState Is Nothing) Then
    MyBase.LoadViewState(savedState)
    End If
    End Sub

    Basically, (I think) I'm not even overridding them, I'm just calling
    their base functionality.

    The web form works. I'm able to access control values programatically.

    The problem is when I set a break point and look at the return object
    MyBase.SaveViewState or when I look at the parameter savedState they
    are ALWAYS NOTHING.

    _ViewState exists on the web page.

    But why can't I see it when I debug the form? I must be missing
    something....

    Cheers,

    Scott
     
    Scott Nichols, Apr 13, 2004
    #1
    1. Advertisements

  2. Scott, what is your custom control deriving from?
    System.Web.UI.Control? System.Web.UI.WebControls.WebControl? Some
    other Web control class? Whatever it is, it is this class's
    SaveViewState()/LoadViewState() you are envoking. So... what is being
    stored in that class's ViewState?

    What you need to realize is that only *programmatic changes* to the
    control's state is saved in the ViewState. So, if you have code that
    does something like:

    MyControl.ID = "Foo"

    in your ASP.NET Web page's code-behind class, then when you step through
    the control's SaveViewState() method you should see that there's some
    content there. If you are not setting any properties programmatically,
    but only through the declarative syntax (i.e., <skm:MyControl ID="bob"
    runat="server" ... />), then the ViewState will be empty.
    It could be populated from other Web controls on the page, or from the
    Web Form itself.

    --

    Scott Mitchell

    http://www.4GuysFromRolla.com
    http://www.ASPFAQs.com
    http://www.ASPMessageboard.com

    * When you think ASP, think 4GuysFromRolla.com!
     
    Scott Mitchell [MVP], Apr 14, 2004
    #2
    1. Advertisements

  3. Hi Scott,

    I think I can now articulate this question better. The question is

    Why in my overrided LoadViewState method doesn't the parameter passed
    to it have infromation about the viewstates of standard webcontrols on
    the page?

    I was expecting the LoadViewState to work that way, but apparently it
    doesn't. All the loading, saving, and tracking is done "somewhere
    else".

    It doesn't really matter, becauses it's not affecting my prog but it's
    more just a curiosity question now.

    Cheers,

    Scott
     
    Scott Nichols, Apr 14, 2004
    #3
  4. Scott Nichols

    Teemu Keiski Guest

    Hi,

    maybe this article helps you:
    http://aspalliance.com/135

    --
    Teemu Keiski
    MCP, Microsoft MVP (ASP.NET), AspInsiders member
    ASP.NET Forum Moderator, AspAlliance Columnist
    http://blogs.aspadvice.com/joteke

    Hi Scott,

    I think I can now articulate this question better. The question is

    Why in my overrided LoadViewState method doesn't the parameter passed
    to it have infromation about the viewstates of standard webcontrols on
    the page?

    I was expecting the LoadViewState to work that way, but apparently it
    doesn't. All the loading, saving, and tracking is done "somewhere
    else".

    It doesn't really matter, becauses it's not affecting my prog but it's
    more just a curiosity question now.

    Cheers,

    Scott
     
    Teemu Keiski, Apr 14, 2004
    #4
  5. No, the loading is done is LoadViewState(). The reason the object
    passed in would be null on postback would be due to the fact that there
    was no ViewState saved on the previous page visit. No previous
    ViewState would have been saved if:

    (1) The page had EnableViewState="False" set in the @Page directive
    (2) The Web controls in your page have not had any of their properties
    programmatically changed.
    (3) Those Web controls that have had their properties programmatically
    changed have their EnableViewState property set to False.

    Remember, ViewState is used ONLY TO STORE CHANGES TO A WEB CONTROL'S
    STATE. If there is no change in state, there is no information recorded
    in the ViewState. For a better description of this, I would highly
    recommend Nikhil Kothari's book Developing Microsoft ASP.NET Server
    Controls and Components
    [http://www.4GuysFromRolla.com/ASPScripts/Goto.asp?ID=170]. It really
    is a *must read* for all ASP.NET server control developers.

    --

    Scott Mitchell

    http://www.4GuysFromRolla.com
    http://www.ASPFAQs.com
    http://www.ASPMessageboard.com

    * When you think ASP, think 4GuysFromRolla.com!
     
    Scott Mitchell [MVP], Apr 15, 2004
    #5
  6. Teemu,

    Thanks! That article was just what I needed. I now have what I think
    is a classic "problem". I am dynamically creating a datagrid and I
    want to access the datagriditems of the dadtagrid when a user presses
    "update". I see to not be able to get at this infromation in the post
    back.

    Do you know what the right approach is for me to try to access the
    datagriditem of a dynamically created datagrid?

    Thanks,

    Scott
     
    Scott Nichols, Apr 15, 2004
    #6
  7. Scott Mitchell [MVP], Apr 16, 2004
    #7
  8. Hi Scott,

    On the bright side - I've learned A LOT over the last three days.
    Teemu's article was key (http://aspalliance.com/135). I was expecting
    to see values stored for the controls on a page, but that's not the
    way it works.

    Yes, I can iterate through the datagriditems to get the value of the
    datagrid but ONLY because I have saved the datasest in a custom
    viewstate and I'm repopulating the datagrid with the saved dataset
    from the viewstate.

    I've tested this with this code in the code behind page:

    Public Class WebForm2
    Inherits System.Web.UI.Page
    Protected WithEvents LinkButton1 As
    System.Web.UI.WebControls.LinkButton
    Protected WithEvents panel1 As System.Web.UI.WebControls.Panel

    Dim mt As New myTracker()

    Private Sub Page_Load(ByVal sender As System.Object, ByVal e As
    System.EventArgs) Handles MyBase.Load
    If Not Page.IsPostBack Then
    mt.myDatabind()
    End If
    panel1.Controls.Add(mt.getPanel)
    End Sub

    Protected Overrides Sub LoadViewState(ByVal savedState As Object)
    If Not (savedState Is Nothing) Then
    Dim myState As Object() = CType(savedState, Object())
    'panelJobDetail = New JobDetails(10)
    If Not (myState(0) Is Nothing) Then
    MyBase.LoadViewState(myState(0))
    End If
    If Not (myState(1) Is Nothing) Then
    mt.LoadViewState(myState(1))
    End If
    End If
    End Sub

    Protected Overrides Function SaveViewState() As Object
    Dim myState(1) As Object
    myState(0) = MyBase.SaveViewState()
    myState(1) = mt.SaveViewState

    Return myState
    End Function

    Protected Overrides Sub TrackViewState()
    mt.TrackViewState()
    MyBase.TrackViewState()
    End Sub
    End Class

    Here's my code for the mt object:


    Imports System.Web.UI

    Public Class myTracker
    Implements IStateManager

    Dim ds As New DataSet()
    Dim mg As New myGrid()

    Private _isTrackingViewState As Boolean
    Private _viewState As StateBag
    Private _savedDetail As Object

    Public Sub New()

    End Sub

    Public Function getPanel() As Panel
    Return mg.getPanel()
    End Function

    Protected ReadOnly Property ViewState() As StateBag
    Get
    If _viewState Is Nothing Then
    _viewState = New StateBag(False)
    If _isTrackingViewState Then
    CType(_viewState, IStateManager).TrackViewState()
    End If
    End If

    Return _viewState
    End Get
    End Property

    Public ReadOnly Property IsTrackingViewState() As Boolean
    Implements IStateManager.IsTrackingViewState
    Get
    Return _isTrackingViewState
    End Get
    End Property

    Public Sub LoadViewState(ByVal savedState As Object) Implements
    IStateManager.LoadViewState
    If Not (savedState Is Nothing) Then
    CType(ViewState, IStateManager).LoadViewState(savedState)
    End If
    _savedDetail = ViewState("detail")

    If Not (savedState Is Nothing) Then
    mg.Detail = ViewState("detail")
    End If
    End Sub

    Public Function SaveViewState() As Object Implements
    IStateManager.SaveViewState
    Dim currentDetail, savedDetail As Object

    currentDetail = mg.Detail
    savedDetail = ViewState("detail")
    If Not mg.compare(currentDetail, savedDetail) Then
    ViewState("detail") = currentDetail
    End If

    If Not IsNothing(_viewState) Then
    Return CType(_viewState, IStateManager).SaveViewState
    End If
    End Function

    Public Sub TrackViewState() Implements
    IStateManager.TrackViewState
    'If Not IsNothing(_viewState) Then
    ViewState("detail") = mg.Detail
    ' End If
    _isTrackingViewState = True

    If Not IsNothing(_viewState) Then
    CType(_viewState, IStateManager).TrackViewState()
    End If
    End Sub

    Public Sub myDatabind()
    mg.DataBind()
    End Sub

    End Class


    Public Class myGrid
    Inherits Panel
    Implements IComparer

    Dim ds As New DataSet()
    Dim dg As New DataGrid()

    Public Sub New()

    Controls.Clear()

    dg.Columns.Clear()
    dg.AutoGenerateColumns = False

    Dim col1 As New TemplateColumn()
    col1.ItemTemplate = New myEditItemTemplate("ServiceID", 5)
    col1.HeaderText = "ServiceID"
    dg.Columns.Add(col1)

    Dim col2 As New TemplateColumn()
    col2.ItemTemplate = New myEditItemTemplate("Description", 20)
    col2.HeaderText = "Description"
    dg.Columns.Add(col2)

    Dim col3 As New TemplateColumn()
    col3.ItemTemplate = New myEditItemTemplate("Qty", 5)
    col3.HeaderText = "Qty"
    dg.Columns.Add(col3)


    Controls.Add(dg)

    ds.Tables.Add("one")
    ds.Tables(0).Columns.Add("ServiceID")
    ds.Tables(0).Columns.Add("Description")
    ds.Tables(0).Columns.Add("Qty")

    Dim dr As DataRow
    dr = ds.Tables(0).NewRow
    dr.Item(0) = "scott"
    dr.Item(1) = "kent"
    dr.Item(2) = "nichols"
    ds.Tables(0).Rows.Add(dr)
    ds.AcceptChanges()

    dg.DataSource = ds.Tables(0)
    End Sub

    Public Property Detail() As Object
    Get
    Return ds
    End Get
    Set(ByVal Value As Object)
    ds = Value
    dg.DataSource = Value
    dg.DataBind()
    End Set
    End Property

    Public Sub myDatabind()
    dg.DataBind()
    End Sub

    Public Function getPanel() As Panel
    Return Me
    End Function

    Public Function compare(ByVal x As Object, ByVal y As Object) As
    Integer Implements IComparer.Compare
    Dim ds1 As New DataSet()
    Dim ds2 As New DataSet()

    ds1 = CType(x, DataSet)
    ds2 = CType(y, DataSet)

    If ds1.GetXml = ds2.GetXml Then
    Return 0
    Else
    Return 1
    End If

    End Function

    End Class

    Class myEditItemTemplate
    Implements ITemplate

    Private _colname As String = ""
    Private _width As Integer

    Public Sub New(ByVal ColName As String, ByVal width As Integer)
    _colname = ColName
    _width = width
    End Sub
    Sub instantiatein(ByVal container As Control) Implements
    ITemplate.InstantiateIn
    Dim tb As New TextBox()
    tb.Columns = _width
    'This is so it would be added to the automatic populating
    routine
    tb.ID = "txt" & _colname
    AddHandler tb.DataBinding, AddressOf bindTextBox
    container.Controls.Add(tb)
    End Sub

    Public Sub bindTextBox(ByVal sender As Object, ByVal e As
    EventArgs)
    Dim tb As TextBox = CType(sender, TextBox)
    Dim container As DataGridItem = CType(tb.NamingContainer,
    DataGridItem)
    tb.Text = Convert.ToString(DataBinder.Eval((CType(container,
    DataGridItem)).DataItem, _colname))
    End Sub
    End Class


    The cool news is THIS WORKS. If you have an aspx page that has a
    linkbutton on it and you click it (just so the page will do a
    postback) the datagrid stays intact and the textxbox on the
    dynamically created datagrid retains any value entered into it.


    The bad news is when I move this test code over to my production
    program I run into a small problem - The datagrid does not retain new
    values on the FIRST POSTBACK. On subsequent postbacks it works, but
    not the first time.

    It seems like I have the answer in front of me... but I just can't
    find it.

    Cheers,

    Scott
     
    Scott Nichols, Apr 16, 2004
    #8
  9. Got It!!

    I know the what - but not the why. But I did get it fixed. I was
    deleting and re-adding the control on the first click (but not on
    subsequent clicks) what that means is that the control ID of the
    rendered datagrid changed.

    Since the ID changed the internal *whatever* that repopulates control
    values didn't find a matching control and so the control retained it's
    original value.

    In fact in my previous example - even though I'm repopulating the
    datagrid with the saved viewstate I don't think that's what retaining
    the values in the controls in the datagrid. I think repopulating the
    datagrid with the saved viewstate simply creates the controls and the
    the internal *whatever* repopulates the controls with the correct
    value - since they now exist.

    Cheers,

    Scott
     
    Scott Nichols, Apr 16, 2004
    #9
    1. Advertisements

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments (here). After that, you can post your question and our members will help you out.