Why does setting ComboBox.SelectedValue to null cause a ArgumentNullException?

comecme picture comecme · May 15, 2013 · Viewed 8k times · Source

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; }
}

Answer

Matthew Watson picture Matthew Watson · May 15, 2013

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.