I have recently found a strange behaviour inside of the ASP.NET DropDownList that I hope someone could explain.
Basically the problem I am running into is when databinding prior to postback and then setting the SelectedValue
to a value that doesn't exist within the list of dataitems the call simply has no effect. However on postback the same call will fail with a ArgumentOutOfRangeException()
'cmbCountry' has a SelectedValue which is invalid because it does not exist in the list of items. Parameter name: value
I'm using the following code.
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
cmbCountry.DataSource = GetCountries();
cmbCountry.DataBind();
cmbCountry.SelectedValue = ""; //No effect
}
else
{
cmbCountry.SelectedValue = ""; //ArgumentOutOfRangeException is thrown
}
}
protected List<Country> GetCountries()
{
List<Country> result = new List<Country>();
result.Add(new Country() { ID = Guid.NewGuid(), Description = "Test" });
result.Add(new Country() { ID = Guid.NewGuid(), Description = "Test1" });
result.Add(new Country() { ID = Guid.NewGuid(), Description = "Test2" });
result.Add(new Country() { ID = Guid.NewGuid(), Description = "Test3" });
return result;
}
public class Country
{
public Country() { }
public Guid ID { get; set; }
public string Description { get; set; }
}
Could someone please clarify the cause of this behaviour for me and advise if there are any workarounds?
I'm not sure why it was designed this way, but the DropDownList only throws this exception on PostBack... here's the setter code from ILSpy:
public virtual string SelectedValue
{
get { ... }
set
{
if (this.Items.Count != 0)
{
if (value == null || (base.DesignMode && value.Length == 0))
{
this.ClearSelection();
return;
}
ListItem listItem = this.Items.FindByValue(value);
/********** Checks IsPostBack here **********/
bool flag = this.Page != null &&
this.Page.IsPostBack &&
this._stateLoaded;
if (flag && listItem == null)
{
throw new ArgumentOutOfRangeException("value",
SR.GetString("ListControl_SelectionOutOfRange", new object[]
{
this.ID,
"SelectedValue"
}));
}
if (listItem != null)
{
this.ClearSelection();
listItem.Selected = true;
}
}
this.cachedSelectedValue = value;
}
}
You can get around this by setting the SelectedValue to null instead of an empty string.
DropDownList > SelectedValue Property > ArgumentOutOfRangeException
The selected value is not in the list of available values and view state or other state has been loaded (a postback has been performed). For more information, see the Remarks section.
Source: MSDN
DropDownList > SelectedValue Property > Remarks
(...) When the selected value is not in the list of available values and a postback is performed, an ArgumentOutOfRangeException exception is thrown. (...)
Source: MSDN
Also, I've made the following test:
!IsPostBack
, added a list with 4 items as datasource, IDs 1~4SelectedValue = 5
combo.Items.Add(new ListItem()...)
with ID 5I expected to see ID 5 as the current selected item at the combo, but it didn't happened.
After all, looks like this behavior is by design. I haven't found more information about, so the following are just my thoughts: it feels like, after arbitrary setting the control's datasource, developer is free to select a non-existing item, which will have simply no effect. However, after binding the viewstate on a postback processing, control's list is validated (or something like it), so it must be manipulated accordingly.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With