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

ListControl SelectedValue inconsistencies


:P
On this page:

I really dislike the ListControl SelectedValue implementation – this damn property is causing me all sorts of grief on occasion with its inconsistencies.

 

Here’s a scenario I’ve just run into again: I have a form that binds values early in the form cycle. It’s mostly an AJAX driven form so there are only a few isolated POSTbacks that occur that repost the entire page because they update most of the content anyway.

 

So the list is loaded up in OnInit() and the initial SelectedValue is set as part of the startup code. POSTBACKS then set the value to the last user set value and all that works well enough. Here’s the basic code that deals with the initial and POSTback assignments:

 

private void GetResourceSet()

{

    this.ResourceSet = Request.Form[this.lstResourceSet.UniqueID];

    if (ResourceSet == null)

        this.ResourceSet = Request.QueryString["ResourceSet"];

    if (this.ResourceSet == null)

        this.ResourceSet = ViewState["ResourceSet"] as string;

 

    this.ResourceSet = this.ResourceSet.ToLower();

 

    if (!string.IsNullOrEmpty(this.ResourceSet))

        ViewState["ResourceSet"] = this.ResourceSet;

   

    DataTable dt = Manager.GetAllResourceSets(ResourceListingTypes.AllResources);

    this.lstResourceSet.DataSource = dt;

    this.lstResourceSet.DataValueField = "ResourceSet";

    this.lstResourceSet.DataBind();

 

    if (this.ResourceSet != null)

        this.lstResourceSet.SelectedValue = this.ResourceSet;

}

 

The problem comes in if I need to change the selected value as part of an event LATER in the page cycle. Basically I have an event that fires and allows renaming one of the items which in turn changes the entire list interface of several lists of the form so they need to be reloaded. Here is the key that reloads the ResourceSet list:

 

protected void btnRenameResourceSet_Click(object sender, EventArgs e)

{

    if (!this.Manager.RenameResourceSet(this.txtOldResourceSet.Text,

        this.txtRenamedResourceSet.Text))

        this.ErrorDisplay.ShowError(this.Manager.ErrorMessage);

    else

    {

        this.lstResourceSet.ClearSelection();

        // this.lstResourceSet.SelectedValue = null;

        // this.lstResourceSet.SelectedIndex = -1;

 

        // *** Refresh and reset the resource list

        // *** Reloads the list from the data base calls databind()

        this.GetResourceSet();

 

        this.ErrorDisplay.ShowMessage("ResourceSet renamed");          

    }

}

 

The problem is when I call GetResourceSet() again I get an error:

 

'lstResourceSet' has a SelectedValue which is invalid because it does not exist in the list of items.

 

Ok, fine but how the heck can I clear the selection in the first place so that ASP.NET DOESN’T complain about the existing value so it can safely DataBind?

 

I’ve tried ClearSelection() – doesn’t work. I tried setting the SelectedValue to null which doesn’t work at all (the old selection stays active) and setting the SelectedIndex also has no effect because the SelectedValue lookup is what’s really being checked.

 

This seems to work however:

 

this.lstResourceSet.SelectedValue = this.txtRenamedResourceSet.Text.ToLower();

 

// *** Refresh and reset the resource list

this.GetResourceSet();

 

although I don’t really understand why that is working. SelectedValue assignments are supposed to fail if the value doesn’t exist in the list of items yet and in this case the item DOESN’T exist yet, because the list hasn’t been rebound with the new value.

 

I took a look at the SelectedValue implementation in System.Web with Reflector, and looking at this implementation I’m really scratching my head. It looks like there really is no way to clear out a selection of the SelectedValue:

 

set

{

    if (this.Items.Count != 0)

    {

          if ((value == null) || (base.DesignMode && (value.Length == 0)))

          {

                this.ClearSelection();

                return;

          }

          ListItem item1 = this.Items.FindByValue(value);

          if ((((this.Page != null) && this.Page.IsPostBack) && this._stateLoaded) && (item1 == null))

          {

                throw new ArgumentOutOfRangeException("value", SR.GetString("ListControl_SelectionOutOfRange", new object[] { this.ID, "SelectedValue" }));

          }

          if (item1 != null)

          {

                this.ClearSelection();

                item1.Selected = true;

          }

    }

    this.cachedSelectedValue = value;

}

 

Notice the call to:

 

          ListItem item1 = this.Items.FindByValue(value);

 

Which in theory should fail because the txtRenamedResourceSet.Text value doesn’t exist in the list yet.

Even odder is hooking the debugger up to this code:

 

this.lstResourceSet.SelectedValue = this.txtRenamedResourceSet.Text.ToLower();

 

and looking at lstREsourceSet.SelectedValue after the assignment – it’s showing the old value rather than the updated one, YET the DataBind() operation actually works with this same existing SelectedValue as when I hadn’t assigned this value.

 

I suppose this code would be safer:

 

// *** Force the selected value to be set

this.lstResourceSet.Items.Add(new ListItem("", this.txtRenamedResourceSet.Text.ToLower()));

this.lstResourceSet.SelectedValue = this.txtRenamedResourceSet.Text.ToLower();

 

Either way it seems it’s all rather convoluted to do something as simple as resetting a list selection value. I have this sinking feeling that I must be overlooking something simple that’s escaping me…

Posted in ASP.NET  

The Voices of Reason


 

Joel Rumerman
October 29, 2006

# re: ListControl SelectedValue inconsistencies

Dude, tell me about the pain with SelectedValue on the ListControl. The asp DropDown control has the same issues. If you're overlooking something really simple, so am I.

We went ahead and wrote a ListHeper static class with a bunch of overloads that added items one-by-one (as you did in your final code snippet). I have no idea if it's comparable performance wise, but really, we got that error so often that we gave up using data binding and rolled our own.

If I had any real motivation, I'd probably derive from the control and override the DataBind method to really encapsulate the one-by-one method.

Abhinav Gujjar
October 29, 2006

# re: ListControl SelectedValue inconsistencies

have you tried Items.Clear() ?I ran into the same problem. And this definitely clears both SelectedValue and SelectedIndex.

IMHO, the inter-dependancy between SelectedValue and SelectedIndex causes some very subtle postbcak problems.

hope this helps
Abhinav

Jackie Lin
November 02, 2006

# re: ListControl SelectedValue inconsistencies

I think it's all about "cachedSelectedValue". Do you know ListControl.SelectValue can be set before Items being created.

You can see it in ListControl.PerformDataBinding

that's it:

if (this.cachedSelectedValue != null)
{
int num1 = -1;
num1 = this.Items.FindByValueInternal(this.cachedSelectedValue, true);
if (-1 == num1)
{
throw new ArgumentOutOfRangeException("value", SR.GetString("ListControl_SelectionOutOfRange", new object[] { this.ID, "SelectedValue" }));
}
if ((this.cachedSelectedIndex != -1) && (this.cachedSelectedIndex != num1))
{
throw new ArgumentException(SR.GetString("Attributes_mutually_exclusive", new object[] { "SelectedIndex", "SelectedValue" }));
}
this.SelectedIndex = num1;
this.cachedSelectedValue = null;
this.cachedSelectedIndex = -1;
}
else if (this.cachedSelectedIndex != -1)
{
this.SelectedIndex = this.cachedSelectedIndex;
this.cachedSelectedIndex = -1;
}

Steve W
March 21, 2007

# re: ListControl SelectedValue inconsistencies

I ran into the same problem also. I subclassed the DropDownList in ASP.net 1.1 and changed the viewstate to only save the selected value. I never use the selected index property, so the viewstate would only save the selected value in the list.

In 1.1 I override the DataBind() method with this:

public override void DataBind()
{

base.ClearSelection();

base.DataBind();

this.AttemptSetBaseSelectedValue();
}

Which works fine in 1.1, no error. When I moved to ASP.Net 2.0 now I get the error you describe above!

UGH!!! Talk about a step backward.

The DropDownList implemenation is horrible from the ASP.Net 1.0 days. I don't understand why it is better to send a list down 2 times to a client once as HTML/text and the other time in the list. That's why we changed the DropDownList to never save the list in the viewstate (only the selected value) and we re-bind the list from the DB/cache or whatever. It worked great until ASP.Net 2.0.

I'll let you know if I come up with a solution short of catching the error in my DataBind() method, which I don't want to do!

Rick Strahl
March 21, 2007

# re: ListControl SelectedValue inconsistencies

The best way to deal with this is to always do any databinding in OnInit before any of the event triggers or ViewState updates fire. This way you get ensured that the right thing happens. Unfortunately that's not always possible. As soon as you assign the SelectedValue/Index manually the changed events start firing which results in mangled behavior.

To this day even knowing all of this I run into situations where things just don't work right and it takes all sorts of futzing to get it tweaked to work. In some situations using ViewState/ControlState is the only way.

The list control model is horribly designed because it clearly assumes it can use ViewState and once that's not there it gets nasty...

Steve W
March 21, 2007

# re: ListControl SelectedValue inconsistencies

Ok, I found the workaround for my subclassed DropDownList in ASP.net 2.0. At least for now it appears to work correctly. The other method AttemptSetBaseSelectedValue() attempts to reset the selected value from my stored one that from the overridden viewstate property. This makes the drop down carry a small viewstate. The only drawback is that you have to re-bind the list, but in most cases this is the lesser of the evil rather than sending too much data down to the client.

public override void DataBind()
{

base.ClearSelection();

this.Items.Clear();
base.SelectedValue = null;

base.DataBind();

this.AttemptSetBaseSelectedValue();
}

Steve W
March 21, 2007

# re: ListControl SelectedValue inconsistencies

Rick,

I just saw your last post about rebinding. We always bind in either page load or earlier, but this error occurred because of our AJAX libraries doing a delayed load on a drop down list that initialized with one item in the list with a value of "". When the user focusses on the drop down list we then invoke an AJAX call back to the server to obtain the list values from the server and output them. As far as the server is concerned the AJAX post back (which is what we call it) appears to be a normal post back. In the even handler we output the JS code to update the drop down list. Anyway the error would occur because the item with value of "" did not appear in the list at all, and now the new list to be databound doesn't have a value of "". What is weird is I call "ClearSelection" first then rebind and call a custom method we have that attempts to set the selected value if the item i still in the list.

This all worked fine in 1.1, in 2.0, I have to manually clear the items (which are empty anyway) then manually set "SelectedValue" to null, then rebind and it works like it did for us in the 1.1 days.

This is what it should do under all circumstances. I don't think a user should get an error when they rebind the list just because the old selected value is there. I would think that it should just clear the value. That's the behavior I would expect. Anyway thank god we can subclass things and take them as our own and fix problems like this. I just wanted to avoid a try catch() at all cost. I think that is a horrible way to code if you have to do that.

Thanks for the blog, you're blogs rock! You helped us determine when to move to ASP.Net 2.0 based on waiting for the Web Application project which seems to be pretty good for the most part.

Found one thing that sucks in the new 2.0. If we upgrade an old form, and we have a base page that has a property for the header control or footer control, once upgraded to the new design, each time edit the front page, it attempts to put the control reference in the design page, even though the control is already declared in the base. It issues a warning for the new item without new, but then our base code croaks because the control is null in the base, being redclared. It just does that automatically. Anyway that is for another blog.

Rick Strahl
March 21, 2007

# re: ListControl SelectedValue inconsistencies

Steve, you might want to consider using ControlState if you're just storing the SelectedValue.

The issue I'm talking about goes like this: Say you create your control to auto-assign the selected value in LoadControlState() (which occurs after OnInit() but before OnLoad()) so now you are setting a SelectedValue to a list that may not yet have been databound. So either you have to check for the contents of the list and not bind or you end up failing on the selectedvalue assign. If you do bind in Load() you have to pretty much assign the selected value after that in order for SelectedValue to make any sense. You see the dilemma here <s>...

Hmmm... another option might be to 'carry' the selected value in a variable and then assign it in OnPreRender() after events have fired. So any assignment to SelectedValue sets a temporary state variable and is only assigned explicitly in OnPreRender. That might work in most scenarios. Of course that may yet have other side effects with MS Ajax...

WanFactory
April 10, 2007

# re: ListControl SelectedValue inconsistencies

You had an earlier post (May 2006?) that suggested "manually" databinding (iterating through the datatable and adding listItems) instead of calling databind. Maybe that will help (it did for me).

I think that the trouble comes when calling databind on a dropdown when its current selected value does not fit into the list. In my case, I am trying to populate during the OnInit phase of the dropdown (inside a datagrid template column) and the selected value won't fit because the postback calls have not happened yet. In your case, you are re-populating but the selected value does not fit because you have just renamed. In both cases, we call databind() when the selected value (which we plan to throw away) does not fit the datasource.

On a more general note, I have found that by populating during OnInit of the dropdownlist, leaving viewstate enabled, and manually binding a datatable to the grid, I seem to be getting what I need. Smaller viewstate which only has selected value and not list contents and the ability to detect onchange events.

Rick Strahl
April 11, 2007

# re: ListControl SelectedValue inconsistencies

Yeah, with Viewstate on it almost always works. But with ViewState off all sorts of craziness pursues the worst of which is that assignments to SelectedValue after Init will cause change events to fire which in turn will fire the event handler if you have AutoPostback set to true. In most cases that can really hose you... Sometimes it's easier to put a button next to the list to submit to avoid that behavior at least so there's no event actually hooked up directly to the change event of the list.

The main issue is that the actual way that the value gets assigned is horribly complex and quite inconsistent based on a myriad of different settings. And leaving ViewState on for large lists is simply not an option in many situations. Even using Controlstate with custom classes can have unexpected issues. <shrug> I find this one of the more frustrating things in the ASP.NET control architecture - one of those things that I run into over and over and waste often an hour a pop on because it seems the situation is never quite the same so no easy patterns (that I can discern) apply.

drgbg
May 02, 2007

# re: ListControl SelectedValue inconsistencies

Try this just before you call DataBind():

Control.DataSource = Nothing
Control.DataBind()

Now try to DataBind to a new DataSource. There seems to be something funky going on under the hood that causes the SelectedValue property to retain the old value from the previous DataBind call. I hope it helps, it recently solved a similar problem with a DropDownList for me (I posted a more detailed solution to that here too).

Anonymous
May 04, 2007

# re: ListControl SelectedValue inconsistencies

After reading all these posts and experimenting, the simplest solution seems to be executing these two lines of code prior to databind():

ddlTarget.Items.Clear()
ddlTarget.SelectedValue = Nothing

This seems to successfully clear the selected value so that .NET won't try to re-apply it after it databinds with the new dataset.
You can, of course, copy the selected value to some variable first if you need to preserve it for other reasons.

Kevmeister
September 25, 2007

# re: ListControl SelectedValue inconsistencies

Why does the DropList actually have to perform a DataBind( ) operation as part of the PostBack when in many cases there is no practical reason to do this, other than to populate a list of items which then acts as a validation target for LoadPostData( ) occurs?

Our business logic takes care of validating our database operation and if the foreign-key (as a drop-list usually relates to one) does not exist, we have an error.

The only time I need to repopulate the drop-list is later on prior to rendering - that's when I know the data is going to be re-displayed to the user, and that seems the ideal time to populate the list.

Rick Strahl
September 25, 2007

# re: ListControl SelectedValue inconsistencies

Kev - not sure I follow. I suppose it depends on what your UI does. If you save data and then immediately go elsewhere, yeah that makes sense, but in most apps that I see you typically go back to the page after the data's been saved and you need to at the very least display the selected value in the list for consistency.

If you have to figure out exactly when to databind() and when not to it can get kinda messy quickly. You still have validation error scenario and in most apps the ability to re-edit the data even after a successful save operation.

Kevmeister
September 25, 2007

# re: ListControl SelectedValue inconsistencies

Rick, to clarify: Our app is typically a record-listing with a breakout to another page to "add a record". The details are entered, user clicks Save, and the record is saved and the user returns back to their list. The only time we stay on the page is if there is an error.

All this is currently just speculation, because I'm yet to see how it works in practice, but ...

OnPreRender( ) gets around the messyness nicely because the DropDownList( ) implementation calls EnsureDataBound, meaning you can bind the data very late. The trick, as noted earlier in these posts, is to make sure your Items container is empty and then set the SelectedValue - the ListControl caches the value so a future-occurring DataBind( ) will utilise the cached value.

So rather than re-populating a drop-list just for the sake of a PostBack operation, you can delay it a little so that if the PostBack is successful (ie. the form is saved, and you are going elsewhere), then you haven't wasted effort re-loading that data.

Either way, that delayed population of the drop-down-list will pay off either as less load on the database server (if you go straight to the database server for the data - as we do), or less memory consumed on the web-server (if you cache the table in a Session object), or less bandwidth consumed (by not having to store the items in ViewState - which I consider only workable on a LAN and even then only for small-moderately sized lists).

PS: Great blog, by the way.

Kenneth E.
November 08, 2007

# re: ListControl SelectedValue inconsistencies

This did it for me. Thanks for the post.

ddlist.Items.Clear();
ddlist.SelectedValue = null;

Iñaki Salinas
February 13, 2008

# re: ListControl SelectedValue inconsistencies

Hello, I have had an inconsistencies problem with SelectValue of a CheckBoxList inside a DetailView (bound with
SelectedValue="<%#Eval("acolumn")%>"
. I don't know if the problem is the same as yours, but I resolved mine like this:

First, I bind the item with a valid value, the first one.
Protected Sub chkBoxList_DataBinding(ByVal sender As System.Object, ByVal e As System.EventArgs)
        Dim box As CheckBoxList = CType(sender, CheckBoxList)
        chkbox.SelectedValue = chkbox.Items(0).Value
End Sub


Then, cause I don't want this value selected, I uncheck it once the item is bound
Protected Sub chkBoxList_DataBound(ByVal sender As System.Object, ByVal e As System.EventArgs)
        Dim chkbox As CheckBoxList = CType(sender, CheckBoxList)
        chkbox.SelectedIndex = -1
        chkbox.SelectedValue = Nothing
End Sub


Hope it helps

PärM
May 22, 2008

# re: ListControl SelectedValue inconsistencies


It is quite interresting to see "Inside the box" that the exception of SelectedValue only is thrown on postback calls.
I noticed this when I had my Selected value setting within try/catch. With postback calls this worked perfectly (or at least as I axpected). But with non-postback calls, the setting with illegal values just passed without notice, until it crashed later on when it was outside my control.

Any idea why that is the case?
Why is the exception suspended because of a non-postback call?

vipul
October 14, 2008

# Dropdownlist Selected value

i bind dropdownlist ,after binding they contain only one record ,i tried to go selectedindexchanged but they cant go

Saber
November 01, 2011

# re: ListControl SelectedValue inconsistencies

This worked for me in Dotnet 4.0 with DropDownList:
my_DDL.Items.Clear();
my_DDL.SelectedValue = null;

Rick McMullan
December 12, 2019

# re: ListControl SelectedValue inconsistencies

I've found it much better to use SelectedIndex throughout if you possibly can. So long as you never assigned a SelectedValue programatically, it'll never look for one.


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