You are here:   Home
  |  Login

Programmer Newsletter

Minimize

Master Programmer

Minimize

Find articles on computer software, programming, ASP.net, Sql server, databases, C#, websites, Internet, Windows, Outlook macros.

Programming Articles

Minimize
11

Using Asp.net 4.

You are trying to set a value that is not in the list control

This error occurs when you try to set the SelectedValue of a ListControl to a value that is not in the list.

has a SelectedValue which is invalid because it does not exist in the list of items. Parameter name: value

According to Microsoft http://msdn.microsoft.com/en-us/library/system.web.ui.webcontrols.listcontrol.selectedvalue.aspx this error throws an ArgumentOutOfRange exception.

You could put the setting of SelectValue in a try - catch block or you can test to make sure the value is in the list before setting.

One way to handle the condition, if only just for debugging, is to insert the item into the top of the list. That way, you can see what item is causing the problem. Here is how.

DropDownList listcontrol = ... // Set and populated your list
string val = ...               // Set your value
ListItem item = listcontrol.Items.FindByValue(val);
if (item == null)              // Did not find value in list
{
item = new ListItem("Missing Value=" + val, val); // Create item with value
listcontrol.Items.Insert(0, item); // Add at top
}
listcontrol.SelectedValue = val; // Select value will work now

If Asp.net binds your list to a data source, you have no opportunity to test if the value is in the list

To determine exactly when the data is bound, I created event handlers for DataBinding and DataBound, and overrode the DataBind and SelectedValue methods, adding a trace message. The markup looks like this, where br:dropdownlist is derived from DropDownList with DataBind and SelectedValue overridden.

<DetailsView ...
<asp:TemplateField ..
<EditItemTemplate>
<br:dropdownlist Selected_Value_Bind='<%# Bind("CountryID") %>' ...
<asp:LinqDataSource ...

Note that Selected_Value_Bind in the above markup is a wrapper I wrote for SelectedValue. It simply adds messages to the trace and executes SelectedValue. Here is the sequence from the trace.

Parent DataBind begin
  Parent event DataBinding
  Children Init
  Children Load
  For each child:
    DataBind begin
      Event DataBinding - Items.Count=0,SelectValue=""
      Selected_Value_Bind begin
        Items.Count=0, SelectValue=""
        SelectedValue
        Items.Count=0, SelectValue=""
      Selected_Value_Bind end
      Event DataBound - Items.Count=66,SelectValue="newvalue"
    DataBind end
  Parent event DataBound
Parent DataBind end

As you can see, the Items list is not populated when SelectedValue is executed.

How does the selected item get set when the Items have not yet been populated?

According to Microsoft the Eval method (and therefore Bind) "returns a string containing the value of that field from the current record in the data source", from this page http://msdn.microsoft.com/en-us/library/ms178366.aspx.

It seems then that the process must be something like this.

DataBind begin
  SelectedValue - Get value from data source using Eval or Bind and store somewhere
  Populate list
  Assign selected item
DataBind end

Between the end of SelectValue and the beginning of DataBound the items are populated and then the list Selected Value is set. You have no opportunity to check that the value is in the list.

When Does Data Get Bound if You Wrap Your List in a Class

Instead of inheriting from say DropDownList, as in the above example, say you wrap your list in another control and create a SelectValue property to bind to the list. For example you create a control called Child with this property and mark up.

public string SelectValue
{
get { return dropdownlist1.SelectValue; }
set { dropdownlist1.SelectValue = value; }
}

<asp:DropDownList ID="dropdownlist1" ..

You put Child in a Parent control that has a data source and you bind to say the "CountryID" field, like this.

<my:Child SelectValue='<%# Bind("CountryID") %>' ...

Here is the sequence of events.

Parent DataBind begin
  Parent event DataBinding
  Children Init
  Children Load
  For each child:
    DataBind begin
      Event DataBinding - Items.Count=0,SelectedValue=""
      Selected_Value_Bind begin
        Items.Count=0, SelectValue=""
        SelectedValue
           dropdownlist1 SelectedValue
        Items.Count=0, SelectValue=""
      Selected_Value_Bind end
        dropdownlist1 DataBind begin
           Event DataBinding - Items.Count=0,SelectedValue=""
           Event DataBound - Items.Count=66,SelectedValue=""
        dropdownlist1 DataBind end

      Event DataBound - Items.Count=66,SelectedValue="newvalue"
    DataBind end
  Parent event DataBound
Parent DataBind end

The items in yellow are the new steps in the process. You can see that the DropDownList now sets its SelectedValue before it calls its DataBinding event. This does not help us. What we needed was the other way around.

Save the Binding Value and Set Later

Here is an idea. Save the binding value and do not set the SelectedValue of the list control until the DataBound event.

You can do this either by inheriting directly from say DropDownList or you can put the code in a wrapper class. Create a new property, say Selected_Value_Bind that you use in the binding markup instead of SelectedValue. You can then save the binding value for setting in the DataBound event.

protected bool selected_value_bind_set = false; // Flag to see if SelectedValue was set
protected string selected_value_bind = string.Empty; // Save the selected value from Bind
// Use a new property only for Eval and Bind, because the value only gets set in the DataBound event
public string Selected_Value_Bind 
{
get { return dropdownlist1.SelectedValue; } // Return the selected value of the list control
set
  { 
  selected_value_bind = value;      // Save the binding value
  selected_value_bind_set = true; // Flag value as set
  }
}

protected virtual void Event_Data_Bound(object sender, EventArgs e)
{
if (selected_value_bind_set) // SelectedValue bound, now see if value in list
  {
  string val = selected_value_bind; // Use the value that was saved from the Bind
  ListItem item = dropdownlist1.Items.FindByValue(val);
  if (item == null)          // Did not find value in list
    {
item = new ListItem("Missing Value=" + val, val); // Create item with value dropdownlist1.Items.Insert(0, item); // Add at top
} dropdownlist1.SelectedValue=val; } } // And in the markup, you need to attach the event handler and bind to the data
<DropDownList ID="dropdownlist1"
OnDataBound="Event_Data_Bound" Selected_Value_Bind='<%# Bind("CountryID") %>'

This seems to work, but I do not know if it is a good idea. I do not know what Asp.net is doing in that mysterious place between SelectedValue and loading the data.

Posted in: Asp.net

Post Rating

Comments

There are currently no comments, be the first to post one.

Post Comment

Only registered users may post comments.

Reccomend Programmer.bz

Minimize

Share/Bookmark Bookmark and Share