views:

2154

answers:

2

Hello,


I think I understand ViewState pretty well, but the following is giving me some troubles:

From http://weblogs.asp.net/infinitiesloop/archive/2006/08/03/truly-understanding-viewstate.aspx

Postback controls such as dropdownlist and textbox restore their posted state (the selected item of a dropdown ist 'posted') even when ViewState is disabled, because even with ViewState disabled the control is still able to post its value


Assuming DropDownList has EnableViewState set to false, then ( according to the above quote ) when user issues a postback by selecting an item in DropDownList, the following code should result in Label1.Text displaying a value of a selected item ( thus DropDownList.SelectedValue should return a value selected by user, even if viewstate is disabled ), but instead I get an empty string:

protected void Page_Load(object sender, EventArgs e)
{
    if (!IsPostBack)
    {
        string[] number = {"first","second","third"};
        DropDownList1.DataSource = number;
        this.DataBind();

    }
    if (IsPostBack)
    {
       Label1.Text = DropDownList1.SelectedValue; // displays empty string
       // Label1.Text = DropDownList1.SelectedItem.Text; // causes an exception           
       // Label1.Text = DropDownList1.SelectedIndex.ToString(); // displays empty string
    }
}


The author of that article appears to be an expert on the subject, so I'm assuming I'm doing something wrong?!


thanx

+9  A: 

There is no selected value because:

1) there are no items in the drop down list, because you are only binding the data to it the first time around. Normally this is good practice, but when you have ViewState turned off, you must explicitly re-create the data for the control every time.

2) You are binding the data after the point in the page lifecycle where the Request dictionary values are applied to controls (namely, during the restoration of ViewState to controls.) The posted value exists in the Request dictionary, but since there are no items in the dropdownlist, it can't really do much with it. Even though ViewState is turned off, your author is correct - the posted value will be applied, at the point in the lifecycle where ViewState would normally be applied.

If you were to re-create the data for the list in Init(), then the dropdown would be populated for the posted value to be applied to, it would be applied, and the selected value would be available by the time you got to Load(). Hopefully that's clear. Some working code is below:

public partial class _Default : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        if (IsPostBack)
        {
            Label1.Text = DropDownList1.SelectedValue;
        }
    }

    protected void Page_Init(object sender, EventArgs e)
    {
        string[] number = { "first", "second", "third" };
        DropDownList1.DataSource = number;
        DropDownList1.DataBind();
    }
}
womp
a) I assume Request dictionary values are applied to controls during LoadPostbackData stage?!b) Prob a stupid question - but wouldn't it make senese for SelectedValue to still hold a value of a selected item, even if that item no longer exists ( due to ViewState being turned off )? That way, we could get the selected value without needing to re-create the data in a DropDownList?!
SourceC
I ran into this very problem. I decided that I'm better off extracting values from Request.Params on my own than repopulating controls for the sole reason of using .SelectedValue
Vnuk
+1  A: 

Here's a much smarter DropDownList implementation. Not to troll or anything, but I can't believe how limited (read: stupid) some of the ASP.Net controls are....

Public Class DropDownList
 Inherits System.Web.UI.WebControls.DropDownList

 Private _SelectedIndex As Integer? = -1
 Private _SelectedValue As String = Nothing
 Private _StateFromClient As Boolean = False
 Private _StateFromLocal As Boolean = False

 Protected Overrides Sub OnInit(ByVal e As EventArgs)
  MyBase.OnInit(e)
  Page.RegisterRequiresControlState(Me)
 End Sub

 Public Overrides Property SelectedIndex() As Integer
  Get
   If Not _StateFromLocal Then
    Me.LoadStateFromClient()

    If _StateFromClient Then
     Return _SelectedIndex
    End If
   End If

   Return MyBase.SelectedIndex
  End Get
  Set(ByVal value As Integer)
   _StateFromLocal = True
   MyBase.SelectedIndex = value
  End Set
 End Property

 Public Overrides Property SelectedValue() As String
  Get
   If Not _StateFromLocal Then
    LoadStateFromClient()

    If _StateFromClient Then
     Return _SelectedValue
    End If
   End If

   Return MyBase.SelectedValue
  End Get
  Set(ByVal value As String)
   _StateFromLocal = True
   MyBase.SelectedValue = value
  End Set
 End Property

 Private Sub LoadStateFromClient()
  If _StateFromClient Then Return
  If _StateFromLocal Then Return
  If Me.IsViewStateEnabled Then Return
  If Not Me.Page.IsPostBack Then Return

  If Not _SelectedIndex.HasValue Then
   Throw New Exception("ControlState has not yet been loaded and so state does not exist.")
  End If
  _SelectedValue = Me.Page.Request.Form(Me.UniqueID)
  _StateFromClient = True
 End Sub

 Protected Overrides Sub PerformSelect()
  ' Called when DataBound() is called, which can affect the Selected* property values
  _StateFromLocal = True
  MyBase.PerformSelect()
 End Sub

 Protected Overrides Function SaveControlState() As Object
  Dim state As Object = MyBase.SaveControlState()

  If Me.SelectedIndex >= 0 Then
   If state IsNot Nothing Then
    Return New Pair(state, Me.SelectedIndex)
   Else
    Return Me.SelectedIndex
   End If
  Else
   Return state
  End If
 End Function

 Protected Overrides Sub LoadControlState(ByVal state As Object)
  If state IsNot Nothing Then
   Dim p As Pair = TryCast(state, Pair)
   If p IsNot Nothing Then
    MyBase.LoadControlState(p.First)
    _SelectedIndex = CInt(p.Second)
   Else
    If (TypeOf (state) Is Integer) Then
     _SelectedIndex = CInt(state)
    Else
     MyBase.LoadControlState(state)
    End If
   End If
  End If
  MyBase.SelectedIndex = _SelectedIndex
 End Sub

End Class
Josh