C# Winforms how to set Datagridview DataGridViewCheckBoxCell via code

Pat Hanks 141 Reputation points
2023-08-28T18:00:42.5866667+00:00

I am stumped on finding a solution to allow me to set a hidden DataGridViewCheckBoxCell checked state with code. The application is using a DataGridView to present a recipe on the form. Users are allowed to add/change the rows of the recipe. I want to track which rows have been changed to prompt the user for additional actions. I am using the CurrentCellDirtyStateChanged event to capture the change in state. When I try to set the cell to true or false with the following statement, it will cause an exception "Specified cast is not valid.". Note this is only occurring when I am adding a new row to the gridview. If I comment out the line below all rows added to the datagrid are indeed appended to the datatable.

ComponentsDataGridView.CurrentRow.Cells[columnIndex].Value = true

The datagrid is bound to a temp datatable and that column is added as typeof(bool). When the temp table is populated I set the column to false. I have confirmed the value is indeed false after it is populated for all the rows are appended.

In the debugger I confirmed ComponentsDataGridView.CurrentRow.Cells[columnIndex].ValueType returns the value of "Boolean".

As well I confirmed ComponentsDataGridView.CurrentRow.Cells[columnIndex].GetType() returns "System.Windows.Forms.DataGridViewCheckBoxCell" to make sure the index is correct.

The last item I checked is ComponentsDataGridView.CurrentRow.Cells[columnIndex].Value is DBNull returns true.

I have also tried setting ComponentCategoryDirty.TrueValue = true; & ComponentCategoryDirty.FalseValue = false; but it didn't make a difference.

I must be missing something, I just can't seem to find what. Is it possible to set the default state of the DataGridViewCheckBoxCell to false? Am I using the wrong event to set the value in?

Windows Forms
Windows Forms
A set of .NET Framework managed libraries for developing graphical user interfaces.
1,873 questions
C#
C#
An object-oriented and type-safe programming language that has its roots in the C family of languages and includes support for component-oriented programming.
10,648 questions
0 comments No comments
{count} votes

Accepted answer
  1. Jiale Xue - MSFT 43,046 Reputation points Microsoft Vendor
    2023-08-29T03:06:02.9966667+00:00

    Hi @Pat Hanks , Welcome to Microsoft Q&A,

    Did you forget to set the target column to be the checkbox column?

    I created a BindingList with Boolean properties as a demonstration. Use the AddButton demo to add a row with default false. Use EditButton to demonstrate modifying the boolean property of the target cell.

    using System;
    using System.ComponentModel;
    using System.Windows.Forms;
    
    namespace _8_29_x
    {
        public partial class Form1 : Form
        {
            public Form1()
            {
                InitializeComponent();
            }
    
            public PersonBindingList people = new PersonBindingList();
            private void Form1_Load(object sender, EventArgs e)
            {
                people.Add(new Person(1, "a", true));
                people.Add(new Person(2, "b", false));
                people.Add(new Person(3, "c", true));
                dataGridView1.DataSource = people;
    
            }
    
            private void AddButton_Click(object sender, EventArgs e)
            {
                people.Add(new Person(people.Count + 1, "New Person", false));
            }
    
            private void EditButton_Click(object sender, EventArgs e)
            {
                Boolean adult = (Boolean)dataGridView1.CurrentRow.Cells["adult"].Value;
                dataGridView1.CurrentRow.Cells["adult"].Value = !adult;
            }
        }
        public class Person : INotifyPropertyChanged
        {
            public int Id { get; set; }
            private String _name;
            public String Name
            {
                get { return _name; }
                set
                {
                    _name = value;
                    NotifyPropertyChanged(nameof(Name));
                }
            }
            private Boolean _adult;
            public Boolean Adult
            {
                get { return _adult; }
                set
                {
                    _adult = value;
                    NotifyPropertyChanged(nameof(Adult));
                }
            }
    
            public event PropertyChangedEventHandler PropertyChanged;
            protected void NotifyPropertyChanged(String propertyName)
            {
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
            }
    
            public Person(int id, string name, bool adult)
            {
                Id = id;
                Name = name;
                Adult = adult;
            }
        }
    
        public class PersonBindingList : BindingList<Person>
        {
            protected override void OnListChanged(ListChangedEventArgs e)
            {
                if (e.ListChangedType == ListChangedType.ItemAdded)
                {
                    // Automatically add a new row in the DataGridView when a new item is added
                    if (IsSortedCore) // Check if sorting is applied
                    {
                        ApplySortCore(SortPropertyCore, SortDirectionCore); // Reapply sorting to include the new item
                    }
                }
                base.OnListChanged(e);
            }
        }
    }
    

    enter image description here

    Best Regards,

    Jiale


    If the answer is the right solution, please click "Accept Answer" and kindly upvote it. If you have extra questions about this answer, please click "Comment". 

    Note: Please follow the steps in our documentation to enable e-mail notifications if you want to receive the related email notification for this thread.


2 additional answers

Sort by: Most helpful
  1. Karen Payne MVP 35,386 Reputation points
    2023-08-29T09:35:17.9566667+00:00

    Since your using a DataTable, to set a default value use DefaultValue as shown here

    DataTable dt = new DataTable();
    dt.Columns.Add(new DataColumn()
    {
        ColumnName = "Checked",
        DataType = typeof(bool),
        DefaultValue = false
    });
    

    In regards to setting an invisible DataGridView column value, you should use a BindingSource, set its source to the DataTable, the BindingSource is now the DataSource of the DataGridView.

    The following shows

    • Defining the DataTable
    • Adding several rows in different styles
    • Setting up the DataGridView (in this case column names are DataColumn names)
    • Toggle the checkbox column value of the current row between true/false
    • Toggle visibility of the checkbox column
    • Raw inspection of checkbox column values
    • Detect new row added, easy to expand on for other actions

    Mocked code

    using System.Data;
    using System.Diagnostics;
    
    #pragma warning disable CS8602 // Dereference of a possibly null reference.
    
    namespace WinFormsApp1;
    
    public partial class Form1 : Form
    {
        private BindingSource _bindingSource = new BindingSource();
        public Form1()
        {
            InitializeComponent();
    
            DataTable dt = new DataTable();
    
            dt.Columns.Add(new DataColumn()
            {
                ColumnName = "Checked",
                DataType = typeof(bool),
                DefaultValue = false
            });
            dt.Columns.Add(new DataColumn()
            {
                ColumnName = "FirstName",
                DataType = typeof(string),
                DefaultValue = ""
            });
            dt.Columns.Add(new DataColumn()
            {
                ColumnName = "LastName",
                DataType = typeof(string),
                DefaultValue = ""
            });
    
            dt.Rows.Add(null, "Karen", "Payne");
            dt.Rows.Add(true, "Mary", "Jones");
    
            DataRow row1 = dt.NewRow();
            row1["FirstName"] = "Bill";
            row1["LastName"] = "Adams";
            dt.Rows.Add(row1);
    
            DataRow row2 = dt.NewRow();
            row2["FirstName"] = "Sue";
            row2["LastName"] = "Gallagher";
            row2["Checked"] = true;
            dt.Rows.Add(row2);
    
            dt.AcceptChanges(); // only for this demo
    
            _bindingSource.DataSource = dt;
            dataGridView1.DataSource = _bindingSource;
            dt.RowChanged += RowChanged;
    
        }
    
        private void ToggleColumnVisibleButton_Click(object sender, EventArgs e)
        {
    
            dataGridView1.Columns["Checked"]!.Visible =
                !dataGridView1.Columns["Checked"]!.Visible;
        }
    
        private void ToggleCheckedButton_Click(object sender, EventArgs e)
        {
            if (_bindingSource.Current is not null)
            {
                DataRow row = ((DataRowView)_bindingSource.Current).Row;
                row.SetField("Checked", 
                    !row.Field<bool>("Checked"));
            }
    
        }
    
        private void InspectButton_Click(object sender, EventArgs e)
        {
            // Get at the DataTable of the BindingSource
            DataTable? table = (DataTable)_bindingSource.DataSource;
    
            var result = table
                .AsEnumerable()
                .Select(row => row.Field<bool>("Checked"))
                .ToList();
    
            foreach (var item in result)
            {
                Debug.WriteLine(item);
            }
    
            Debug.WriteLine("");
        }
    
        public void RowChanged(object sender, DataRowChangeEventArgs e)
        {
            if (e.Action != DataRowAction.Add) return;
            DataRow row = e.Row;
            // apostrophes are there in the event there is no value for first or last name
            Debug.WriteLine($"{row["Checked"]}  '{row["FirstName"]}'  '{row["LastName"]}'");
        }
    
        private void GetModificationsButton_Click(object sender, EventArgs e)
        {
            DataTable? table = (DataTable)_bindingSource.DataSource;
            DataTable? changedTable = table.GetChanges(DataRowState.Modified);
            if (changedTable is not null)
            {
                // there are changes, inspect the row(s) and column(s)
            }
            else
            {
                // no changes
            }
        }
    }
    

  2. Pat Hanks 141 Reputation points
    2023-08-31T19:04:07.4633333+00:00

    I want to thank you guys for the great suggestions and knowledge. After being able to get some uninterrupted head-down time on this I was able to find a solution and what I believe was the cause of my problem. Since I was using the CurrentCellDirtyStateChanged event, at this point there is no underlying datarow of the grid row to modify yet. I checked the row count of the ComponentDataView.Table. That I believe explains why I was getting a DBNull in the checkbox value, which was generating the invalid cast exception.

    Once I changed my approach to create a class object with properties that match by checkboxes I was able to set them to true/false in the CurrentCellDirtyStateChanged event.

    private partial class GridRowDirtyFlags
    {    
    	public bool Qty { get;set; }    
    	public bool Category { get; set; }    
    	public bool Item { get; set; }    
    	public bool UOM { get; set; }    
    	public void SetFlags(bool State)    
    	{        
    		Qty = State;        
    		Category = State;        
    		Item = State;        
    		UOM = State;    
    	}
    }
    
    

    Then in the RowValidating event, I am able to update the checkbox values without incident.

    I welcome any feedback on my approach.

    DataGridViewRow row = ComponentsDataGridView.Rows[e.RowIndex];	
    row.Cells[ComponentCategoryDirty.Index].Value = _gridRowDirtyFlags.Category;
    row.Cells[ComponentItemDirty.Index].Value = _gridRowDirtyFlags.Item;
    row.Cells[ComponentQtyDirty.Index].Value = _gridRowDirtyFlags.Qty;
    row.Cells[ComponentUOMDirty.Index].Value = _gridRowDirtyFlags.UOM;
    // reset the flags
    _gridRowDirtyFlags.SetFlags(false);