Where did my ViewState go???

S

Scott Nichols

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
 
S

Scott Mitchell [MVP]

Scott said:
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

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.
_ViewState exists on the web page.

It could be populated from other Web controls on the page, or from the
Web Form itself.

--

Scott Mitchell
(e-mail address removed)
http://www.4GuysFromRolla.com
http://www.ASPFAQs.com
http://www.ASPMessageboard.com

* When you think ASP, think 4GuysFromRolla.com!
 
S

Scott Nichols

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
 
T

Teemu Keiski

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
 
S

Scott Mitchell [MVP]

Scott said:
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".

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
(e-mail address removed)
http://www.4GuysFromRolla.com
http://www.ASPFAQs.com
http://www.ASPMessageboard.com

* When you think ASP, think 4GuysFromRolla.com!
 
S

Scott Nichols

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
 
S

Scott Nichols

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
 
S

Scott Nichols

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
 

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. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,968
Messages
2,570,153
Members
46,699
Latest member
AnneRosen

Latest Threads

Top