绑定 DataGridView:当我在其外部单击时,DataGridViewComboBoxColumn 更改为不同的值

Bound DataGridView: the DataGridViewComboBoxColumn changes to a different value when I click outside it

我希望有人能帮我解决一个让我非常困惑的晦涩问题。

我希望设置一个 DataGridView,它允许用户 select 来自 DataGridViewComboBoxColumn 的一个选项,并且对于作为 DataGridView 的数据源的对象,用用户指定的对象进行更新在 ComboBox 中选择。

[注意我希望 DataGridViewComboBoxColumn 在下拉列表中显示多个 属性,因此我使用 DataTable 作为 DataGridViewComboBoxColumn 数据源。换句话说,我希望他们看到的描述是其他属性串联在一起的组合。]

我的代码有效,但是当我在 ComboBox 单元格外部单击时,该值会自动设置(它会设置回 BindingList 中的第一项)。我无法让它坚持用户 select 编辑的值。最终将错误的对象实例分配给了 DataGridView 的数据源。

我想post拍张照片,但我没有足够的代表。

所以我的问题是,当我在单元格外单击时,为什么 DataGridViewComboBoxColumn 会切换到其数据源(DataTable)中的第一项。我怎样才能让它在单元格中保留我 selected 的选项。我非常困惑。

我希望 post 将这么多代码上传到 Whosebug 网站是可以的,而不会让每个人感到厌烦。我已经尝试创建合适的通用示例来帮助任何其他尝试找出如何使用 DataGridViewComboBoxColumn select 并将一个对象分配给另一个对象的 属性 的灵​​魂。所以希望这对其他人有用。如果有人需要解决此类问题,我也尝试使其相对容易重新创建。

我已经尝试了各种方法,包括使用 List 作为 DataGridViewComboBoxColumn 的数据源,对 CurrentCellDirtyStateChanged 事件进行操作 - 都无济于事。

这是我的 2 类:

    public class CarPartChoice
    {
        public string Name { get; set; }
        public int Value { get; set; }
        public string Comment {get; set;}

        public CarPartChoice(string name, int value, string comment)
        {
            Name = name;
            Value = value;
            Comment = comment;
        }
    }

    public class Car
    {
        public string Manufacturer { get; set; }
        public string Model { get; set; }
        public CarPartChoice WheelChoice {get; set;}

        public Car(string maker, string model, CarPartChoice wheel)
        {
            Manufacturer = maker;
            Model = model;
            WheelChoice = wheel;
        }

        public Car(string maker, string model)
        {
            Manufacturer = maker;
            Model = model;
        }
    }

    public static class GlobalVariables
    {
        public static BindingList<CarPartChoice> GlobalChoiceList { get; set; }
        public static BindingList<Car> GlobalCarsList { get; set; }
    }

这是我创建我的 dataGridView:

        private void Form1_Load(object sender, EventArgs e)
        {
            //Setup the wheel choices to be selected from the DataGridViewComboBoxColumn.
            CarPartChoice myWheelChoice = new CarPartChoice("Chrome", 19, "This is the chromes wheels option.");
            CarPartChoice myWheelChoice2 = new CarPartChoice("HubCaps", 16, "This is the nasty plastic hubcaps option.");
            BindingList<CarPartChoice> tempBLChoice = new BindingList<CarPartChoice>();
            tempBLChoice.Add(myWheelChoice);
            tempBLChoice.Add(myWheelChoice2);
            GlobalVariables.GlobalChoiceList = tempBLChoice;

            //Setup the cars to populate the datagridview.
            Car car1 = new Car("Vauxhall", "Astra");
            Car car2 = new Car("Mercedes", "S-class");
            BindingList<Car> tempListCars = new BindingList<Car>();
            tempListCars.Add(car1);
            tempListCars.Add(car2);
            GlobalVariables.GlobalCarsList = tempListCars;

            dataGridView1.AutoGenerateColumns = false;
            dataGridView1.CurrentCellDirtyStateChanged += new EventHandler(dataGridView1_CurrentCellDirtyStateChanged);

            // Set up 2 DataGridViewTextBox columns, one to show the manufacturer and the other to show the model.
            DataGridViewTextBoxColumn manufacturer_col = new DataGridViewTextBoxColumn();
            manufacturer_col.DataPropertyName = "Manufacturer";
            manufacturer_col.Name = "Manufacturer";
            manufacturer_col.HeaderText = "Manufacturer";
            DataGridViewTextBoxColumn model_col = new DataGridViewTextBoxColumn();
            model_col.DataPropertyName = "Model";
            model_col.Name = "Model";
            model_col.HeaderText = "Model";

            // Create a DataTable to hold the Wheel options available for the user to choose from. This DT will be the DataSource for the 
            //  ...combobox column
            DataTable wheelChoices = new DataTable();
            DataColumn choice = new DataColumn("Choice", typeof(CarPartChoice));
            DataColumn choiceDescription = new DataColumn("Description", typeof(String));
            wheelChoices.Columns.Add(choice);
            wheelChoices.Columns.Add(choiceDescription);
            foreach (CarPartChoice wheelchoice in GlobalVariables.GlobalChoiceList)
            {
                wheelChoices.Rows.Add(wheelchoice, wheelchoice.Name + " - " + wheelchoice.Value.ToString() + " - " + wheelchoice.Comment);
            }

            // Create the Combobox column, populated with the wheel options so that user can pick one.
            DataGridViewComboBoxColumn wheelOption_col = new DataGridViewComboBoxColumn();
            wheelOption_col.DataPropertyName = "WheelChoice";
            wheelOption_col.Name = "WheelChoice";
            wheelOption_col.DataSource = wheelChoices;
            wheelOption_col.ValueMember = "Choice";
            wheelOption_col.DisplayMember = "Description";
            wheelOption_col.ValueType = typeof(CarPartChoice);

            // Add the columns and set the datasource for the DGV.
            dataGridView1.Columns.Add(manufacturer_col);
            dataGridView1.Columns.Add(model_col);
            dataGridView1.Columns.Add(wheelOption_col);
            dataGridView1.DataSource = GlobalVariables.GlobalCarsList;
        }

更新: 我尝试使用 "BindingSource" 对象并将 BindingSource 的数据源设置为 DataTable。那没有区别。我还尝试让 Car 实现 "INotifyPropertyChanged" 接口,但没有任何区别。

我注意到的一件事是 datagridview 中的 Car 项确实更新了其 WheelChoice 属性。 Car 对象确实已使用我从 ComboBox select 编辑的值进行了更新。我认为这只是一个显示问题,DataGridViewComboBoxColumn 只是没有填充正确的值。还是一头雾水。

感谢那些留下评论的人。我终于找到了答案。

我的答案是简单地为我的 "CarPartChoice" class 定义一个重写的 "ToString()" 方法(即 class 用于提供项目通过单击 ComboBoxColumn 查找)。我猜想当我将控制权从它移开时,它无法用漂亮的字符串格式化我的单元格。

因此,对于将来想要这样做的任何人,这里有一个完整的示例,说明如何使用 datagridview 更新某个 class 的对象列表,并使用 DataGridViewComboBoxColumn 提供用户可以选择对象,并将他们选择的对象(他们从下拉列表中选择)填充到列表中(准确地说:在列表中的对象字段中,该对象类型为由用户在下拉列表中选择)。是的,我知道这是一个非常可怕的句子。

这是完成此任务的完整代码(我原以为这是人们经常想做的事情)。

向大家致以最诚挚的问候。

    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            //Setup the wheel choices to be selected from the DataGridViewComboBoxColumn.
            CarPartChoice myWheelChoice = new CarPartChoice("Chrome", 19, "This is the chromes wheels option.");
            CarPartChoice myWheelChoice2 = new CarPartChoice("HubCaps", 16, "This is the nasty plastic hubcaps option.");
            CarPartChoice myWheelChoice3 = new CarPartChoice("Iron", 15, "These are metal wheels.");
            CarPartChoice myWheelChoice4 = new CarPartChoice("Spoked", 15, "This is the fancy classic hubcaps option.");
            CarPartChoice myWheelChoice5 = new CarPartChoice("solid", 13, "This wheels has no spokes or holes.");
            CarPartChoice myWheelChoice6 = new CarPartChoice("SpaceHubCaps", 17, "Newly developed space hubcaps.");

            BindingList<CarPartChoice> tempBLChoice = new BindingList<CarPartChoice>();
            tempBLChoice.Add(myWheelChoice);
            tempBLChoice.Add(myWheelChoice2);
            tempBLChoice.Add(myWheelChoice3);
            tempBLChoice.Add(myWheelChoice4);
            tempBLChoice.Add(myWheelChoice5);
            tempBLChoice.Add(myWheelChoice6);

            GlobalVariables.GlobalChoiceList = tempBLChoice;

            //Setup the cars to populate the datagridview.
            Car car1 = new Car("Vauxhall", "Astra");
            Car car2 = new Car("Mercedes", "S-class");
            BindingList<Car> tempListCars = new BindingList<Car>();
            tempListCars.Add(car1);
            tempListCars.Add(car2);
            GlobalVariables.GlobalCarsList = tempListCars;

            dataGridView1.AutoGenerateColumns = false;
            dataGridView1.CurrentCellDirtyStateChanged += new EventHandler(dataGridView1_CurrentCellDirtyStateChanged);


            // Set up 2 DataGridViewTextBox columns, one to show the manufacturer and the other to show the model.
            DataGridViewTextBoxColumn manufacturer_col = new DataGridViewTextBoxColumn();
            manufacturer_col.DataPropertyName = "Manufacturer";
            manufacturer_col.Name = "Manufacturer";
            manufacturer_col.HeaderText = "Manufacturer";
            DataGridViewTextBoxColumn model_col = new DataGridViewTextBoxColumn();
            model_col.DataPropertyName = "Model";
            model_col.Name = "Model";
            model_col.HeaderText = "Model";

            // Create a DataTable to hold the Wheel options available for the user to choose from. This DT will be the DataSource for the 
            //  ...combobox column
            DataTable wheelChoices = new DataTable();
            DataColumn choice = new DataColumn("Choice", typeof(CarPartChoice));
            DataColumn choiceDescription = new DataColumn("Description", typeof(String));
            wheelChoices.Columns.Add(choice);
            wheelChoices.Columns.Add(choiceDescription);

            foreach (CarPartChoice wheelchoice in GlobalVariables.GlobalChoiceList)
            {
                wheelChoices.Rows.Add(wheelchoice, wheelchoice.Name + " - " + wheelchoice.Value.ToString() + " - " + wheelchoice.Comment);
            }

            // Create the Combobox column, populated with the wheel options so that user can pick one.
            DataGridViewComboBoxColumn wheelOption_col = new DataGridViewComboBoxColumn();
            wheelOption_col.DataPropertyName = "WheelChoice";
            wheelOption_col.DataSource = wheelChoices;
            wheelOption_col.ValueMember = "Choice";
            wheelOption_col.DisplayMember = "Description";
            wheelOption_col.ValueType = typeof(CarPartChoice);

            // Add the columns and set the datasource for the DGV.
            dataGridView1.Columns.Add(manufacturer_col);
            dataGridView1.Columns.Add(model_col);
            dataGridView1.Columns.Add(wheelOption_col);
            dataGridView1.DataSource = GlobalVariables.GlobalCarsList;
        }

        void dataGridView1_CurrentCellDirtyStateChanged(object sender, EventArgs e)
        {
            var grid = sender as DataGridView;
            if (grid.IsCurrentCellDirty)
                grid.CommitEdit(DataGridViewDataErrorContexts.Commit);
        }
    }

    public class CarPartChoice
    {
        public string Name { get; set; }
        public int Value { get; set; }
        public string Comment { get; set; }

        public CarPartChoice(string name, int value, string comment)
        {
            Name = name;
            Value = value;
            Comment = comment;
        }

        public override string ToString()
        {
            return Name.ToString() + " - " + Value.ToString() + " - " + Comment.ToString();
        }
    }

    public class Car
    {
        public string Manufacturer { get; set; }
        public string Model {get; set; }
        public CarPartChoice WheelChoice { get; set; } 

        public Car(string maker, string model, CarPartChoice wheel)
        {
            Manufacturer = maker;
            Model = model;
            WheelChoice = wheel;
        }

        public Car(string maker, string model)
        {
            Manufacturer = maker;
            Model = model;
        }
    }

    public static class GlobalVariables
    {
        public static BindingList<CarPartChoice> GlobalChoiceList { get; set; }
        public static BindingList<Car> GlobalCarsList { get; set; }
    }

我也遇到了同样的问题,但数据集不同。我试图向组合框添加一个键值对,每次我在外面单击时,它都会重置为第一项。我缺少的是我没有设置组合框的 displayMember 属性 。 (如果其他人遇到同样的问题,希望这会有所帮助)