Why does setting the SelectedValue
of a ComboBox
to null
cause an ArgumentNullException
?
The Exception only occurs if the ComboBox is actually part of a Form.
I can set SelectedValue
to all kinds of values or types that don't make sense, but I can't set it to null
.
It's not that SelectedValue
can not be null
. In fact, its value is null
at the time I'm trying to set it to null
.
In my real code, this doesn't happen in the constructor, and I'm not excplicitly setting it to null
. The code is using a variable which happens to be null
. I can fix it by checking of the variable isn't null
before trying to set the SelectedValue
. But what I don't understand is why I can't set it to a null
value.
Code edit: DataSource now contains an item where the ValueMembers value is actually null
using System.Collections.Generic;
using System.Windows.Forms;
public class Form1 : Form {
public Form1() {
var comboBox1 = new ComboBox();
Controls.Add(comboBox1);
comboBox1.ValueMember = "Key";
comboBox1.DisplayMember = "Value";
comboBox1.DataSource = new List<Record> {
new Record {Key = "1", Value = "One"},
new Record {Key = null, Value = "null"}
};
comboBox1.SelectedItem = null; // no problem
comboBox1.SelectedValue = ""; // no problem
comboBox1.SelectedValue = new object(); // no problem
comboBox1.SelectedValue = null; // ArgumentNullException!!
}
}
public class Record {
public string Key { get; set; }
public string Value { get; set; }
}
Inspecting the implementation of the property in Reflector
it looks like this:
public object SelectedValue
{
get
{
if ((this.SelectedIndex != -1) && (this.dataManager != null))
{
object item = this.dataManager[this.SelectedIndex];
return this.FilterItemOnProperty(item, this.valueMember.BindingField);
}
return null;
}
set
{
if (this.dataManager != null)
{
string bindingField = this.valueMember.BindingField;
if (string.IsNullOrEmpty(bindingField))
{
throw new InvalidOperationException(SR.GetString("ListControlEmptyValueMemberInSettingSelectedValue"));
}
PropertyDescriptor property = this.dataManager.GetItemProperties().Find(bindingField, true);
int num = this.dataManager.Find(property, value, true);
this.SelectedIndex = num;
}
}
}
So this seems to hinge on this.dataManager
not being null.
If this.dataManager
isn't null, the setter will call Find()
with key
set to the value that you're setting SelectedValue
to:
internal int Find(PropertyDescriptor property, object key, bool keepIndex)
{
if (key == null)
{
throw new ArgumentNullException("key");
}
if (((property != null) && (this.list is IBindingList)) && ((IBindingList) this.list).SupportsSearching)
{
return ((IBindingList) this.list).Find(property, key);
}
if (property != null)
{
for (int i = 0; i < this.list.Count; i++)
{
object obj2 = property.GetValue(this.list[i]);
if (key.Equals(obj2))
{
return i;
}
}
}
return -1;
}
Which will throw an exception if key
is null.
I'm guessing that dataManager
is only set to non-null when the ComboBox is inserted into a container (e.g. a Form) which is why it doesn't blow up when it's not in a container.
(In fact, dataManager
will be set to non-null when you set the Control.DataSource
property to non-null.)
However, this doesn't seem quite right, because you reported a NullReferenceException
and this clearly throws an ArgumentNullException
.
[EDIT] It was indeed an ArgumentNullExeption
; OP has been updated accordingly.