简体   繁体   中英

C# Why isn't it possible to set the selectedValue of a dynamically created combobox?

I create a combobox in a class and want to set the selected value for that combobox. But when I do that, the selectedValue stays null and when I try to set the selectedIndex I get a ArgumentOutOfRangeException.

Code:

public Control GenerateList(Question question)
{
    // Clear the local givenAnswer collection
    _givenAnswer.Clear();

    // Get a list with answer possibilities
    List<QuestionAnswer> answers = question.GetAnswerSort();

    // Get a collection of given answers
    Collection<QuestionnaireAnswer> givenAnswers = question.GetGivenAnswers();

    _givenAnswer = givenAnswers;

    ComboBox cmb = new ComboBox();
    cmb.Name = "cmb";
    cmb.DisplayMember = "Answer";
    cmb.ValueMember = "Id";
    cmb.DataSource = answers;
    cmb.Dock = DockStyle.Top;

    // Check an answer is given to the question
    if (givenAnswers != null && givenAnswers.Count > 0)
    {
        cmb.Tag = givenAnswers[0].AnswerId;
        cmb.SelectedValue = givenAnswers[0].AnswerId; // answerId = 55, but SelectedValue stays null
    }

    cmb.SelectedIndex = 1; // For testting. This will throw a ArgumentOutOfRangeException
    cmb.DropDownStyle = ComboBoxStyle.DropDownList;
    cmb.SelectedIndexChanged += new EventHandler(cmb_SelectedIndexChanged);

    return cmb;
}

I hope someone can explain to me what is happening so I can understand why it isn't working.

Here is a complete little program what illustrates my problem. As you can see it doesn't set the SelectedValue, this stays null

namespace Dynamic_Create_Combo
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();

            GenerateControls gc = new GenerateControls();
            Control c = gc.GenerateCombo();

            this.SuspendLayout();
            this.Controls.Add(c);
            this.ResumeLayout(true);
        }
    }

    public class GenerateControls
    {
        public Control GenerateCombo()
        {
            // Create datasource
            Collection<Car> cars = new Collection<Car>();
            Car c = new Car();
            c.Id = 1;
            c.Name = "Car one";
            cars.Add(c);

            Car c1 = new Car();
            c1.Id = 2;
            c1.Name = "Car two";
            cars.Add(c1);

            Car c2 = new Car();
            c2.Id = 2;
            c2.Name = "Car three";
            cars.Add(c2);

            ComboBox cmb = new ComboBox();
            cmb.DropDownStyle = ComboBoxStyle.DropDownList;
            cmb.DataSource = cars;
            cmb.DisplayMember = "Name";
            cmb.ValueMember = "Id";

            cmb.SelectedValue = 2;

            return cmb;
        }
    }

    public class Car
    {
        private int _id;
        private string _name;

        public int Id
        {
            get { return _id; }
            set { _id = value; }
        }

        public string Name 
        {
            get { return _name; }
            set { _name = value; }
        }
    }
}

You've set the value member to be "Id" but you're trying to use "AnswerId" as the selected value.

Without more details, it's hard to say why setting SelectedIndex is throwing an ArgumentOutOfRangeException - perhaps the combobox is ignoring all values which don't have an "Id" property, thus giving you no values, so selecting index 1 is impossible?

EDIT: Okay, so it looks like it's only actually doing the binding when it becomes visible - or at some stage in the process. I've tried a few things to accelerate this, but they don't appear to help. What you can do is defer your selection:

EventHandler visibleChangedHandler = null;
visibleChangedHandler = delegate {
    cmb.SelectedIndex = 2;
    cmb.VisibleChanged -= visibleChangedHandler; // Only do this once!
};
cmb.VisibleChanged += visibleChangedHandler;

It's an ugly workaround, but it should at least help you to get going for the moment.

This is just a guess, but maybe, the ComboBox doesn't bind the data in the DataSource until it's drawn. Check cmb.Items.Count in the line before the SelectedIndex = 1 . If it is 0 try to first add the cmb to the Form before assigning SelectedIndex .

EDIT:

    public Control GenerateCombo() 
    { 
        // Create datasource 
        Collection<Car> cars = new Collection<Car>(); 
        Car c = new Car(); 
        c.Id = 1; 
        c.Name = "Car one"; 
        cars.Add(c); 

        Car c1 = new Car(); 
        c1.Id = 2; 
        c1.Name = "Car two"; 
        cars.Add(c1); 

        Car c2 = new Car(); 
        c2.Id = 2; 
        c2.Name = "Car three"; 
        cars.Add(c2); 

        ComboBox cmb = new ComboBox(); 
        cmb.DropDownStyle = ComboBoxStyle.DropDownList; 
        cmb.DataSource = cars; 
        cmb.DisplayMember = "Name"; 
        cmb.ValueMember = "Id"; 

        // add this: 
        EventHandler visibleChangedHandler = null; 
        visibleChangedHandler = delegate { 
            cmb.SelectedIndex = 2; 
            cmb.VisibleChanged -= visibleChangedHandler;
        }; 
        cmb.VisibleChanged += visibleChangedHandler; 

        // delete this: cmb.SelectedValue = 2; 

        return cmb; 
    } 

您需要设置 cmb.DataBindings, 可能会有所帮助。

Maybe because the combo is created during the Form_load event. Try to create your combo in the constructor and set the selection in Form_Load.

public class Form1
{


private ComboBox _comboBox1;
private Car _audi = new Car("Audi)");
private Car _porsche = new Car("Porsche");
private Car _vw = new Car("VW");

private void Form1_Load(object sender, EventArgs e)
{
    this._comboBox1.SelectedItem = _vw;

}


public Form1()
{
    Load += Form1_Load;
    // This call is required by the designer.
    InitializeComponent();

    // Add any initialization after the InitializeComponent() call.
    this._comboBox1 = new ComboBox();
    this._comboBox1.DataSource = {
        _audi,
        _porsche,
        _vw
    };
    this.Controls.Add(this._comboBox1);
}



private class Car
{

    public string Name { get; set; }

    public override string ToString()
    {
        return this.Name;

    }

    public Car(name)
    {
        this.Name = name;
    }
}

}

You may want to try SelectedItem instead of SelectedValue , and assigning the class that you want to it. For example:

Car c1 = new Car();
// snip
cmb.SelectedItem = c1;

Since more than one item can have a value of 2 (as far as the ComboBox knows, anyway), I think you will have difficulty setting the value via SelectedValue .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM