ArgumentException when adding ComboBox column to DataGridView with same DataSource

12,866

Solution 1

try assigning the name of the datafield for the ValueMember property of clm2. While you're specifying that the value type is typeof(smsType), you're not telling the ComboBox column which field to use for the value.

EDIT
Wait a second: Is your smsType some complex type or something? I'm not sure if any restrictions apply here, but for a sample you should use something like int or string or so (anything you'd normally expect to be stored as a database field).

Also, of course, the type of the DataGridView's underlying data source's column (in your example called "Type") must also be of the same type as the ValueMember!

EDIT 2
On your second comment: Imagine a database table called "tbl" that contains (among others) one column called "Type" that is of type Integer. You're displaying the contents of that table in your DataGridView and you want the user to be able to select values for the "Type" column from a combo box. This is about the scenario you're talking about.

  1. it is not possible to store complex types in like the one you're using in database columns, so you can not use complex types for the Value field in a DataGridViewComboBoxColumn.
  2. To perform the data binding for the entire grid, you must bind the grid to the database table "tbl". To create the DataGridViewComboBoxColumn, you need to assign a list of possible values to the column and tell the column the field in the DataGridView's datasource it stores the selected value to, which field is used as a display value and which field is used as the value that is stored in the underlying datasource's column.

That means in the sample (assuming the data source for the column contains properties "Value" and "Name"):

DataGridViewComboBoxColumn col = new ...
col.DataSource = columnDataSource;
col.DisplayMember = "Name";
col.ValueMember = "Value";
col.DataPropertyName = "Type";

This is all. However, the type of the property you assign to "ValueMember" can not be a complex type (class/struct) if I recall correctly...

Solution 2

DataGridViewComboBoxColumn limits input to values in the DataSource. I had this same problem. I was trying to set the field value outside the DGV. I was binding the DGV to a DataTable. If I set a DataRow["somefield"] to a value not in DataSource, I would receive the error you are receiving.

I eventually created a descendant of DataGridViewColumn that support a ComboBox editor and allowed for values not in the DataSource.

I can post the code if you would like to see it.

EDIT: Here's a ComboBox column example

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms.VisualStyles;

namespace YourNamespaceHere
{
    /// <summary>
    /// DataGridView TextBox column with Items support.
    /// </summary>

    public class DropTextBoxColumn : DataGridViewColumn
    {
        [Browsable(false)]
        public IEnumerable<string> Items { get; set; }

        public ComboBoxStyle DropDownStyle { get; set; }

        public DropTextBoxColumn() : base(new DropTextBoxCell()) 
        {
            DropDownStyle = ComboBoxStyle.DropDown;
        }

        private DataGridViewCell cellTemplate = new DropTextBoxCell();
        public override DataGridViewCell CellTemplate
        {
            get
            {
                return cellTemplate;
            }
            set
            {
                // Ensure that the cell used for the template is a DropTextBoxCell.
                if (value != null &&
                    !value.GetType().IsAssignableFrom(typeof(DropTextBoxCell)))
                {
                    throw new InvalidCastException("Must be a DropTextBoxCell");
                }
                cellTemplate = value;
            }
        }
    }

    public class DropTextBoxCell : DataGridViewTextBoxCell
    {
        [Browsable(false)]
        public string[] Items { get; set; }

        public DropTextBoxCell() : base() { }


        protected override void Paint(Graphics graphics, Rectangle clipBounds, Rectangle cellBounds, int rowIndex, DataGridViewElementStates cellState, object value, object formattedValue, string errorText, DataGridViewCellStyle cellStyle, DataGridViewAdvancedBorderStyle advancedBorderStyle, DataGridViewPaintParts paintParts)
        {
            base.Paint(graphics, clipBounds, cellBounds, rowIndex, cellState, value, formattedValue, errorText, cellStyle, advancedBorderStyle, paintParts);

            //draw a drop down button
            if ( (cellState & DataGridViewElementStates.Selected) != 0) 
            {
                var cb = cellBounds;
                var r = new Rectangle(cb.Right - cb.Height, cb.Top, cb.Height, cb.Height);
                //ComboBoxRenderer.DrawTextBox(graphics, cb, formattedValue as string, this.Style.Font ?? DataGridView.Font, ComboBoxState.Normal);
                ComboBoxRenderer.DrawDropDownButton(graphics, r, ComboBoxState.Normal);            
            }
        }
        public override void InitializeEditingControl(int rowIndex, object
            initialFormattedValue, DataGridViewCellStyle dataGridViewCellStyle)
        {
            // Set the value of the editing control to the current cell value.
            base.InitializeEditingControl(rowIndex, initialFormattedValue,
                dataGridViewCellStyle);
            DropTextBoxEditingControl ctl =
                DataGridView.EditingControl as DropTextBoxEditingControl;

            var value = this.Value.ToString();

            ctl.Loading = true;
            DropTextBoxColumn col = DataGridView.Columns[this.ColumnIndex] as DropTextBoxColumn;
            ctl.DropDownStyle = col.DropDownStyle;

            ctl.Items.Clear();
            if (col.Items != null)
                ctl.Items.AddRange(col.Items.ToArray());

            ctl.EditingControlFormattedValue = value;
            ctl.Loading = false;

        }

        public override Type EditType
        {
            get
            {
                // Return the type of the editing contol that CalendarCell uses.
                return typeof(DropTextBoxEditingControl);
            }
        }

        public override Type ValueType
        {
            get
            {
                // Return the type of the value that CalendarCell contains.
                return typeof(string);
            }
        }

        public override object DefaultNewRowValue
        {
            get
            {
                // Use the current date and time as the default value.
                return string.Empty;
            }
        }
    }

    class DropTextBoxEditingControl : ComboBox, IDataGridViewEditingControl
    {
        DataGridView dataGridView;
        private bool valueChanged = false;
        int rowIndex;
        public bool Loading { get; set; }
        int originalIndex = -1;

        public DropTextBoxEditingControl()
        {
            //this.Format = DateTimePickerFormat.Short;
            DropDownStyle = ComboBoxStyle.DropDown;
            FlatStyle = FlatStyle.Flat;     
        }

        // Implements the IDataGridViewEditingControl.EditingControlFormattedValue 
        // property.
        public object EditingControlFormattedValue
        {
            get
            {
                return Text;
            }
            set
            {

                if (value is String)
                {
                    if (DropDownStyle == ComboBoxStyle.DropDown)
                        Text = value.ToString();
                    else
                    {
                        SelectedIndex = originalIndex = Items.IndexOf(value);                        
                    }                    
                }
            }
        }

        // Implements the 
        // IDataGridViewEditingControl.GetEditingControlFormattedValue method.
        public object GetEditingControlFormattedValue(
            DataGridViewDataErrorContexts context)
        {
            return EditingControlFormattedValue;
        }

        // Implements the 
        // IDataGridViewEditingControl.ApplyCellStyleToEditingControl method.
        public void ApplyCellStyleToEditingControl(
            DataGridViewCellStyle dataGridViewCellStyle)
        {
            this.Font = dataGridViewCellStyle.Font;
        }

        // Implements the IDataGridViewEditingControl.EditingControlRowIndex 
        // property.
        public int EditingControlRowIndex
        {
            get
            {
                return rowIndex;
            }
            set
            {
                rowIndex = value;
            }
        }

        // Implements the IDataGridViewEditingControl.EditingControlWantsInputKey 
        // method.
        public bool EditingControlWantsInputKey(
            Keys key, bool dataGridViewWantsInputKey)
        {
            // Let the DateTimePicker handle the keys listed.
            //switch (key & Keys.KeyCode)
            //{
            //    case Keys.Left:
            //    case Keys.Up:
            //    case Keys.Down:
            //    case Keys.Right:
            //    case Keys.Home:
            //    case Keys.End:
            //    case Keys.PageDown:
            //    case Keys.PageUp:
            //        return true;
            //    default:
            //        return !dataGridViewWantsInputKey;
            //}

            return DroppedDown;

        }

        // Implements the IDataGridViewEditingControl.PrepareEditingControlForEdit 
        // method.
        public void PrepareEditingControlForEdit(bool selectAll)
        {
            // No preparation needs to be done.
        }

        // Implements the IDataGridViewEditingControl
        // .RepositionEditingControlOnValueChange property.
        public bool RepositionEditingControlOnValueChange
        {
            get
            {
                return false;
            }
        }

        // Implements the IDataGridViewEditingControl
        // .EditingControlDataGridView property.
        public DataGridView EditingControlDataGridView
        {
            get
            {
                return dataGridView;
            }
            set
            {
                dataGridView = value;
            }
        }

        // Implements the IDataGridViewEditingControl
        // .EditingControlValueChanged property.
        public bool EditingControlValueChanged
        {
            get
            {
                return valueChanged;
            }
            set
            {
                valueChanged = value;
            }
        }

        // Implements the IDataGridViewEditingControl
        // .EditingPanelCursor property.
        public Cursor EditingPanelCursor
        {
            get
            {
                return base.Cursor;
            }
        }
        protected override void OnSelectedItemChanged(EventArgs e)
        {
            if (Loading) return;

            // Notify the DataGridView that the contents of the cell
            // have changed.
            valueChanged = true;
            this.EditingControlDataGridView.NotifyCurrentCellDirty(true);
            base.OnSelectedItemChanged(e);
        }
        protected override void OnSelectedIndexChanged(EventArgs e)
        {
            if (Loading || DroppedDown) return;

            // Notify the DataGridView that the contents of the cell
            // have changed.
            valueChanged = true;
            this.EditingControlDataGridView.NotifyCurrentCellDirty(true);
            base.OnSelectedIndexChanged(e);


            SendKeys.Send("{ENTER}");
        }
        protected override void OnTextChanged(EventArgs e)
        {
            if (Loading) return;

            valueChanged = true;
            this.EditingControlDataGridView.NotifyCurrentCellDirty(true);
            base.OnTextChanged(e);
        }
        protected override void OnDropDownClosed(EventArgs e)
        {
            if (originalIndex != SelectedIndex)
            {
                valueChanged = true;
                this.EditingControlDataGridView.NotifyCurrentCellDirty(true);
            }
            base.OnDropDownClosed(e);

        }
        protected override void OnDropDown(EventArgs e)
        {
            //set dropdown width to accomodate items
            var g = CreateGraphics();      
            DropDownWidth = 
                Items.Cast<string>().Max(s => 
                {
                    var size = g.MeasureString(s, Font);
                    return size.Width.To<int>() + 30;
                });
            base.OnDropDown(e);
        }
        protected override void OnEnter(EventArgs e)
        {
            base.OnEnter(e);
            DroppedDown = true;
        }
    }
}

Here's example usage

var dc = new DropTextBoxColumn();
dc.Name = "FieldName";
dc.DataPropertyName = "FieldName";
dc.DropDownStyle = ComboBoxStyle.DropDownList;

var items = dc.Items = new string[]{ "one", "two", "three" };
items.Insert(0, "<None>");

dc.Items = items;
DirectGrid.Columns.Insert(1,dc);
Share:
12,866
Wahid Bitar
Author by

Wahid Bitar

Always trying to be better. It has two major meanings I have a lot of enhancements to do to myself I believe in Continous Development ^_^ LinkedIn | GitHub | Twitter

Updated on June 05, 2022

Comments

  • Wahid Bitar
    Wahid Bitar almost 2 years

    I have a DataGridView with custom columns.

    But when I add a "DataGridViewComboBoxColumn" and give to it a list of my model class as DataSource then I had the following error:

    System.ArgumentException: DataGridViewComboBoxCell value is not valid.


    New EDIT: 4/9/2009 "More Details"

    I've a class called SmsPart has these properties:

    public class SmsPart
    {
        public int ID
        public SmsPart Parent
        public string Name
        // and more
    }
    

    I've method called "GetSmsParts" return "List<SmsPart>".

    I want the Parent column in the DataGridView to be ComboBoxColumn to select which part is the parent of selected part.

    So for that reason I made "DataGridViewComboBoxColumn" and set it's Datasource the same DataSource for the hole DataGridView "Which is the GetsmsParts method":

        DataGridViewComboBoxColumn comboCulomn = new DataGridViewComboBoxColumn();
        comboCulomn.DataSource = listParts;
        comboCulomn.DataPropertyName = "Parent";
        comboCulomn.DisplayMember = "Name";
        comboCulomn.ValueMember = "ID";
        comboCulomn.Name = "Parent";
        dgvParts.Columns.Add(comboCulomn);
    

    But i always have this error message:

    System.ArgumentException: DataGridViewComboBoxCell value is not valid.