Rick Strahl's Weblog  

Wind, waves, code and everything in between...
.NET • C# • Markdown • WPF • All Things Web
Contact   •   Articles   •   Products   •   Support   •   Advertise
Sponsored by:
West Wind WebSurge - Rest Client and Http Load Testing for Windows

ASP.NET ListBoxes, SelectedValue and ViewState


:P
On this page:

Aarrgh. Am I the only one who's repeatedly cursing the way ListBoxes and the OnSelectedIndexChanged event and SelectedValue assignments work in ASP.NET when ViewState is off?

 

If you turn off ViewState on a ListBox or DropDownList control it's a real bitch to deal with the OnSelectedIndexChanged event. Take the following scenario assuming you set ViewState off for the lstCustomers control:

 

protected void Page_Load(object sender, EventArgs e)

{

    Customer = new busCustomer();

    Customer.GetCustomerList();

 

    this.lstCustomers.DataSource = Customer.DataSet.Tables["TCustomerList"];

    this.lstCustomers.DataValueField = "CustomerId";

    this.lstCustomers.DataTextField = "CompanyName";

    this.lstCustomers.DataBind();

}

 

If you've tried this you've probably know that this doesn't work quite the way as is expected if you have AutoPostBack and the OnSelectedIndexChanged hooked up to the listbox.

 

This code works to display the customer information each time you load the page, but there are a couple of problems with this:

 

First if you do this in Page_Load(), you end up with no selected value because by the time Page_Load fires the selected value has already been updated from the POST buffer. This is easy to fix with:

 

  if (this.IsPostBack)

     this.lstCustomers.SelectedValue = Request.Form[lstCustomers.UniqueID];

 

 

But this isn't exactly a good idea. Because of a more serious issue. If you bind in Page_Load() the SelectedIndexChanged event doesn't get fired correctly because the value wasn't set correctly when the POST data was assigned to the control – since the list wasn't loaded yet SelectedValue couldn't be assigned.  Manually assigning the value in Page_Load doesn't change this behavior.

 

There's is a simple but not so obvious solution and that's to bind in OnInit() of the Page:

 

protected override void OnInit(EventArgs e)

{

    base.OnInit(e);

 

    Customer = new busCustomer();

    Customer.GetCustomerList();

 

    this.lstCustomers.DataSource = Customer.DataSet.Tables["TCustomerList"];

    this.lstCustomers.DataValueField = "CustomerId";

    this.lstCustomers.DataTextField = "CompanyName";

    this.lstCustomers.DataBind();

}

 

Now it works as you would expect. Sort of… most of the time. This still won't work correctly if you fire other events on the same page (like a Button Click). If you do you'll find that the SelectedIndexChanged event fires as well…

 

Soooooo… this sucks. It seems to me in ASP.NET 2.0 this particular issue could have been easily fixed by putting the SelectedValue(s) into ControlState so it always gets stored. Can anybody think of why this wasn't done?

 

The only way I can figure out to get this to work using only page code is to manage my own ViewState for the control. I can add these two methods to the form:

 

protected override void OnPreRender(EventArgs e)

{

base.OnPreRender(e);

 

    this.ViewState["lstCustomers_SelectedValue"] = this.lstCustomers.SelectedValue;

}

 

protected override void LoadViewState(object savedState)

{

    base.LoadViewState(savedState);

 

    string Val = this.ViewState["lstCustomers_SelectedValue"] as string;

    if (Val != null)

       this.lstCustomers.SelectedValue = Val;

}

 

Now it works, but this is messy as hell. And it won't work if ViewState is off completely on the page. I'm not sure how that could be handled if ViewState is of, short of implementating a custom listbox control that implements controlstate for the SelectedValue property.

 

One workaround is my PreservePropertyControl I posted a while back. This control can track property values automatically. It stores and hooks ControlState events, so everything happens at the right time automatically. To use the control you add the control to the page like this:

 

<ww:PreservePropertyControl runat="server" ID="preserve">

    <PreservedProperties>

        <ww:PreservedProperty ID="PreservedProperty1" runat="server"

ControlId="lstCustomers"

            Property="SelectedValue">

        </ww:PreservedProperty>

    </PreservedProperties>

</ww:PreservePropertyControl>  

 

and it all works. You still need to remember to stick the databinding code into the OnInit() of the page so that the list is filled with data prior to ViewState/ControlState unbinding but otherwise nothing else needs to happen.

 

Now if you were using ViewState none of this would have to happen. ViewSate stores both the data of the ListBox and the SelectedValues. That's one of the main reasons not to use ViewState on a list unless it just has a few values in it. Otherwise you keep posting the content of the listbox back to the server and back on every postback.

 

Trouble in AJAX Land

All of this becomes a fairly major issue when you're dealing with AJAX implementations that rely on server side controls. Both Anthem and ATLAS (for server controls anyway) post back form data including viewstate and fire server side events like regular POSTBacks for Callbacks. Most of Anthems controls shuttle requests back to the server and capture the control's HTML content and shuttle that back to the client. ATLAS does a similar thing with UpdatePanel and the handful of Server controls like Timer that fire server side events.

 

ViewState is even more of an issue in Callback scenarios since ViewState may be going a heck of a lot more in callbacks, so having a large listbox stored in ViewState is going to defeat the whole light weight aspect of callback.


The Voices of Reason


 

Steve from Pleasant Hill
March 18, 2006

# re: ASP.NET ListBoxes, SelectedValue and ViewState

You are not alone. You touch on what may seem a minor issue to some, but to me an architectural flaw that should be addressed in order to avoid work-arounds. It just shouldn't be quite so much work.

scottgu
March 18, 2006

# re: ASP.NET ListBoxes, SelectedValue and ViewState

One approach would just be to subclass the control and add the controlstate semantics to your subclass. This would encapsulate it and allow you to use it anywhere.

Hope this helps,

Scott

Rick Strahl
March 18, 2006

# re: ASP.NET ListBoxes, SelectedValue and ViewState

Scott,

Actually my subclass does just that, which is why I rarely notice this issue and run my head against a wall when I use the stock controls <g>...

Is there any reason you guys didn't add SelectedValue to ControlState internally? Backwards compatibility?

There is one issue with the code I posted above, which is another quirky behavior of ASP.NET Pages: If you assign a selected value when the list is not bound and you DataBind() (say you do a Page.DataBind()) ASP.NET will complain that the selectedValue is invalid and bomb. So the logic needs to somehow also figure out if there's data already in the list to restore the value... <sigh> Not a problem in my sample since I could bind just the appropriate panel rather than the whole page but that's a lot of things to have to shuffle around just to get OnSelectedIndexChanged to work without ViewState.



Bertrand Le Roy
March 20, 2006

# re: ASP.NET ListBoxes, SelectedValue and ViewState

You guessed it. Back compat.

katie
May 17, 2006

# re: ASP.NET ListBoxes, SelectedValue and ViewState

Why wouldn't something like that work?
When populating controls check for presence of the corresponding Response.Form variable and use that instead of the value from the datasource.

Dan Sharpe
August 06, 2006

# re: ASP.NET ListBoxes, SelectedValue and ViewState

Scott,

I've been having the same problems with large listboxes in Atlas UpdatePanels, which seems to have gotten worse with the July CTP. Once these controls are bound and rendered, they show on the client fast enough, but anything over several hundred items in the list causes a noticeable delay with any control that does a postback. The delay is proportionate to the size of the listboxes; upwards to 10,000 items causes the browser to freeze for well over a minute before working again.


# DotNetSlackers: ASP.NET ListBoxes, SelectedValue and ViewState


Rick Strahl's Web Log
October 15, 2006

# Implementing an ASP.NET PreserveProperty Control - Rick Strahl's Web Log

A couple of days ago I posted about having PreserveProperty functionality in ASP.NET. Today I'll look at implementing a custom control that implements this functionality. The control allows saving and restoring of property values automatically and declaratively without ViewState being enabled. This article discusses the control functionality and implementation in some detail. If you're new to Server Control development this article covers a few important topics such as exposing collections as ch

Allan
February 26, 2007

# re: ASP.NET ListBoxes, SelectedValue and ViewState

Speaking of hacks, I got around this problem by directly referenceing the controls name in the page init event and subsequently setting the listbox controls value to it

It works but what a pain and its a workaround for what I thought would be expected behaviour...

Subclassing this and that is a typical overkill solution for what is really a passing simple form value and quite often confuses the hell outta me when I know what is required for the page... :|

Protected Sub Page_Init(ByVal sender As Object, ByVal e As System.EventArgs)
thesection = ReplaceNull(Request.Form("ctl00$ContentPlaceHolder1$ListBoxSections"))
End Sub

Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs)

With Me.ListBoxSections
' populate sections
.DataSource = ....
.DataTextField = "sectionname"
.DataValueField = "sectionid"
.DataBind()

If thesection <> "" Then .SelectedValue = thesection

End With

End Sub

Public Function ReplaceNull(ByVal _value As Object) As String
If _value Is DBNull.Value Then
Return ""
ElseIf _value Is Nothing Then
Return ""
Else
Return CType(_value, String)
End If
End Function

Eugene
April 25, 2007

# another, somewhat less messy approach

Use javascript to postback the control instead. No further server-side garbage required. This should work with ATLAS too.


<script>
function postback() {
document.getElementById('<%=btnSearch.ClientID %>').click();
}
</script>

<asp:ImageButton runat="server" ID="btnSearch" ImageUrl="~/images/search.png" OnClick="btnSearch_Click" />


<asp:DropDownList id="itemsPerPage" runat="server" EnableViewState="true" onchange="postback();" AutoPostBack="false" />

WhiteSites
August 21, 2007

# re: ASP.NET ListBoxes, SelectedValue and ViewState

13 hours, two pots of coffee, and a whole box of Oatmeal Creme Pies later and I found the answer. The problem was that OnSelectedIndexChanged was not even postingback, which was obvious but I couldn't figure out why. So a took my page down to nothing but the dropdownlist. And it worked. This restored my faith in God. Then I added one by one my other controls back to the page ( textboxes, other dropdowns ext.. ). At the end of the page I have a button. As soon as I added this button to my page my postback would not work for my dropdown. So I looked at the button and noticed something. The ID of the button was 'submit'. I changed the ID to 'button1', and then everything on my page works. Leason learned. Check all your ID's to make sure they are not ID="submit" as it will prevent any of your dropdowns from calling postback to the server.

Steve from Pleasant Hill
February 12, 2008

# re: ASP.NET ListBoxes, SelectedValue and ViewState

Maybe you can shed some light --

I have a control that descends from CheckboxList, and have hijacked the Render (I do not call the Base Render) since I have to do several things with other data fields in the DataSource.

During the first load of this (during the Render) I siphon off the other columns from the DataSource and stuff them in an ArrayList, which I try to park in ViewState.

However, when I come back on a Postback, the DataSource is of course Nothing, the Items of the (ancestor) CheckBoxList are still populated due to I am assuming ViewState (or ControlState?), but my ArrayList is Nothing when I pull it from the Property above.

How does one save this type of object in ViewState between Postbacks?

<DefaultProperty("Text"), ToolboxData("<{0}:CheckBoxList_Plus runat=server></{0}:CheckBoxList_Plus>")> _
Public Class CheckBoxList_Plus
Inherits CheckBoxList

Private m_DescriptionColName As String
Private m_D1DataSource As ArrayList

Property D1DataSource() As ArrayList
Get
Dim o As Object = ViewState("D1DataSource")
If o Is Nothing Then
Return Nothing
Else
Return CType(o, ArrayList)
End If
End Get
Set(ByVal Value As ArrayList)
ViewState("D1DataSource") = Value
End Set
End Property


' the code that loads it:

Public Overrides Sub RenderControl(ByVal output As HtmlTextWriter)

Dim lic As ListItemCollection = Me.Items

If Not Me.DataSource Is Nothing Then

Dim arList As New ArrayList

For i = 0 To lic.Count - 1
arList.Add(dt.Rows(i)(DescriptionColName))
Next

' assign to Property/ViewState (I verified that the arList is populated)
D1DataSource = arList

When I reference the Property D1DataSource on a PostBack it returns Nothing...

Thank you,

Steve

stm
April 14, 2008

# re: ASP.NET ListBoxes, SelectedValue and ViewState

I just spent hours to figure out why selectedindexchanged fire always for me (even when I not change the list, juts press some button on the page).

Here is the answer (thank you for it):

"This still won't work correctly if you fire other events on the same page (like a Button Click). If you do you'll find that the SelectedIndexChanged event fires as well…"

And here is the bug report about this in ms connect:
http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=103844

This is 3 years old bug (closed "by design"), which is still in vs 2008 :(

Peter H
November 11, 2008

# re: ASP.NET ListBoxes, SelectedValue and ViewState

Backward compatibility is no excuse for leaving a fairly significany bug in the system.

I still find it hard to believe that we are x years into asp.net development and we don't have a) a dropdownlist that functions correctly with viewstate is disabled and b) a radio-button that works correctly inside a repeater.

Calenders, trees etc are all well and good but the asp.net team should really get the basics working first!

At the very least, the asp.net team could have introduced a second dropdownlist that does use control-state, so at least we will have a work around it in the future. :(

Barbaros
January 02, 2009

# re: ASP.NET ListBoxes, SelectedValue and ViewState

I hate struggling with Dynamic controls, it's a total headache.

For example,
It is easy to get posted values after postback by the help of Request.Form except CheckBoxList control. Instead of this you can use CheckBox.

and what about a dynamically created requiredfieldvalidator for a dynamically created textbox or dropdownlist in a webUserControl(.ascx)

which one is get fired first, the aspx page's onInit or ascx's onInit (the answer is, ascx's OnInit :)) and where to create validation controls ? In the OnInit or OnPreRender, and what if all these controls are nested in a table. I meant MasterPage > WebContentForm > UserControl > Table.

It is really hard to understand the viewstate in the begining and hard to apply things about what you have learnt.

I get really bored all of this, and still i couldnt create requiredfieldvalidator dynamically to a dynamically created textbox in a <asp:Table control inside a usercontrol, inside a master page. I have override UniqueId and ClientId already, but i still get couldnt reference to controltovalidate, bla bla bla...

pofff

Koppaka
March 26, 2009

# re: ASP.NET ListBoxes, SelectedValue and ViewState

How should I get Mutliple User Selections using Request.Form???
I am able to get single user selection but for multiple user selection null is returned?

Dave Peru
June 14, 2009

# re: ASP.NET ListBoxes, SelectedValue and ViewState

Here's my solution to the Listboxes, SelectedValue, and ViewState problem.

Here's the problem as I see it: ViewState is off, and during the postback, DropDownList drops its selected value.

My Page_Load code is nearly identically to Ricks in terms of binding to an object data source.

In my ASPX file, I have the following additional code:

  <script type="text/javascript">
  function SetSelectedValue(parm1, parm2) 
  {
    document.getElementById(parm2).value = document.getElementById(parm1).value;
  }
  </script>

In addition to my DropDownList, I add an additional TextBox to hold the selected value:

  <asp:DropDownList ID="ddlTest1Id" DataTextField="Item" 
    DataValueField="Test1Id" CssClass="formDropDownList" runat="server" >
  </asp:DropDownList>
  <asp:TextBox ID="tbxSelectedValueTest1Id" runat="server" style="display: none;"/>

Then, I have the following additional code in Page_Load:

  StringBuilder sb = new StringBuilder();
  sb.Append("return SetSelectedValue('");
  sb.Append(ddlTest1Id.ClientID);
  sb.Append("', '");
  sb.Append(tbxSelectedValueTest1Id.ClientID);
  sb.Append("');");
  ddlTest1Id.Attributes.Add("onchange", sb.ToString());

Then, in my button handling code I have the following:

  string selectedValue= "";
  if (ddlTest1Id.SelectedItem != null && ddlTest1Id.SelectedItem.ToString().Length != 0)    // ViewState on, or bug fixed.
    selectedValue = ddlTest1Id.Text;
  else if (tbxSelectedValueTest1Id.Text != null && tbxSelectedValueTest1Id.Text.Length != 0)    // ViewState off.
    selectedValue = tbxSelectedValueTest1Id.Text;
  if (selectedValue.Length == 0)
    // No DropDownList value selected.
  else
    // DropDownList value selected and in selectedValue.

Now the DropDownList control acts like a TextBox control when the ViewState is off. I guess the more elegant solution would be to develop a custom control derived from the DropDownList class. But I saw a bunch of people posting stuff about problems they were having writing it. This solution seemed a whole lot more simple all the way around.

It seems kind of ridiculous to me that we have to do this kind of kludge in the first place. DropDownList should just work like everything else does!

Anyway, hope this helps,
Dave

Julio César
October 28, 2009

# re: ASP.NET ListBoxes, SelectedValue and ViewState

Hi Rick!

I totally agree with you!

I began programming with ASP.NET in 2007 when I got my bachelor. And while I have been progressing trying to master this web framework I have found "issues" that cause me a really headache.

For example: User Controls + DropDownList + ObjecDataSource + EnableViewState = false

The SelectedIndexChanged() event handler is fired even when the ddl's selection has not changed. The "SelectedValue" must be saved in the ControlState. That way many developers don't have to code messy workarounds.

Just how I feel right now...

http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=103844

And years still counting...

Alok Kumar
January 21, 2010

# re: ASP.NET ListBoxes, SelectedValue and ViewState

This is typical problem. multi-select list box always return the first selected value and not all the selected value. We have to loop through the entire collection and check each individual value if selected or not.

Alok Kumar
http://www.extendcode.com

West Wind  © Rick Strahl, West Wind Technologies, 2005 - 2024