C#: DataGridViewCheckBoxCell strange behaviour [duplicate] - c#

I have a DataGridView that loads data from a DataTable, along with an unbound column of DataGridViewCheckBoxCells. The rows in the DataGridView are compared with a separate DataTable with values the user has saved, and if there is a match, the checkbox for that row should check.
Here is the code that compares the values and sets the checkbox value to 'true':
foreach (int j in selectedObjectives)
{
foreach (DataGridViewRow r in dgvObjectives.Rows)
{
if (j == Convert.ToInt32(r.Cells["ObjectiveID"].Value))
{
dgvObjectives.CurrentCell = r.Cells["Select"];
((DataGridViewCheckBoxCell)r.Cells["Select"]).Value = true;
//dgvObjectives.InvalidateCell(r.Cells["Select"]);
//dgvObjectives.EndEdit();
//dgvObjectives.CommitEdit(DataGridViewDataErrorContexts.Commit);
}
if (Convert.ToInt32(r.Cells["ObjectiveID"].Value) == selectedIndex)
{
r.Selected = true;
}
}
}
When I call the method to perform this action during the form load private void WQMDrill_Load(object sender, EventArgs e), the values are set correctly, but the checkboxes do not check. However, when called after the form is finished loading, the code works perfectly. Unfortunately for me, I absolutely need for these to check during the load process.
I hope I was clear with my issue, any help on this matter would be greatly appreciated. As you can see, I have tried to invalidate the cell alone, as well as the entire DataGridView control. I also have
private void dgvObjectives_CurrentCellDirtyStateChanged(object sender, EventArgs e)
{
if (this.dgvObjectives.CurrentCell.ColumnIndex == 0)
{
this.dgvObjectives.CommitEdit(DataGridViewDataErrorContexts.Commit);
}
}
That doesn't fire during this time. Thank you.

You can put your checkbox selection and update logic in the DataBindingComplete eventhandler, this fires after the FormLoad but before anything is displayed to the user.

I'm not certain that calling CommitEdit will actually fire the Paint on the cell. Try handling the CellMouseUp event and firing EndEdit if the column is a checkbox column.
private void dgvObjectives_CellMouseUp(object sender, DataGridViewCellMouseEventArgs e)
{
if (dgvObjectives.Columns[e.ColumnIndex] is DataGridViewCheckBoxColumn)
{
dgvObjectives.EndEdit();
}
}

I had the same problem, and tried a lot of different ways to deal with it, most failed, except when I tried this.BeginInvoke(new CDelegate()).

Related

Automatic insertion of rows to DataGrid View without using a timer in Winforms

A row is to be automatically added in a datagridView of Winforms according to value changes in a text box.
A text box (textBox1) is used in the form to input the value. With the change in the value a row is to be inserted in the datagridview (dataGridView1)
I have used the following code for implementing the same,
private void timer1_Tick(object sender, EventArgs e)
{
int value;
value = Convert.ToInt32(textBox1.Text);
if(value == 2)
{
string[] row1 = {"Value is 2"};
dataGridView1.Rows.Add(row1);
}
}
The result I was expecting to get was a single row inserted in the dataGridView1.
I am getting the same row inserted a number of times since the code is running continuously inside the timer, timer1.
Can anyone help me with getting the expected result?
Can it be done without using a timer?
The usual approach would be to subscribe to the TextBox.TextChanged event:
//maybe in the form constructor
textBox1.TextChanged += HandleTextChanged;
Then you would need to implement a method HandleTextChanged somewhat like this (in the same class):
private void HandleTextChanged(object sender, EventArgs e)
{
if(int.TryParse(textBox1.Text, out var number))
{
if(number == 2)
{
string[] newRow = { "Value is 2" };
dataGridView1.Rows.Add(newRow);
}
}
}
For further information on events in WinForms, I propose you have a look at the documentation on learn.microsoft.com. Generally speaking WinForms is event-driven, so it's definitely useful to get used to the concept.
If you want to insert a new row according to the change in TextBox, you can use TextChanged event.
You delegate will be called each time the text is changed.
private void textbox_TextChanged(object sender, EventArgs e)
{
// place your code here for adding a row.
}
The textbox has a multitude of events, which you can inspect in the designer, by clicking it and selecting in the Properties window the yellow flash on the top.
if you want to add your textbox always as row when you finsihed editing the textbox,
use the apropiate event (Leave maybe) and add your row in there.
You could have your timer event tick once and then disable it:
private void timer1_Tick(object sender, EventArgs e)
{
int value;
value = Convert.ToInt32(textBox1.Text);
if(value == 2)
{
string[] row1 = {"Value is 2"};
dataGridView1.Rows.Add(row1);
}
timer1.Enabled = false; //<--disable timer1 once your job is done
}

C# DataGridView detecting select all button

I'm developing a C# Windows Form program and I've implemented a DataGridView on it. Now, after setting the data source, when I click the top left button on the datagridview, it selects all rows, just like Microsoft Excel. However, I dynamically hide and show rows on it, and after clicking that button I realized that it also selects the invisible ones. I don't want to implement "SelectionChanged" event because I constantly select some rows and normally I can't select the invisible ones. Only this button selects it. I'm looking for an event like this:
datagridView1_SelectAllClicked(object sender, EventArgs e)
{
// do stuff
}
Something like this will also work since I don't have to check all selections:
dataGridView1_SelectionChanged(object sender, EventArgs e)
{
if(dataGridView1.IsSelectAllCells())
{
// do stuff
}
}
If I have to, I will add the event to deselect the invisible rows, but I prefer some solution like the first one. Any advices? Thanks in advance.
Edit: I'm checking "dataGridView1.SelectedRows" property on button clicks only, not after the selection was made. So, some function that I can implement to button click events will also solve my problem.
The DataGridView class provides the AreAllCellsSelected methode:
Returns a value indicating whether all the DataGridView cells are currently selected. (MSDN)
With that we can get a solution like your second one:
private void dataGridView1_SelectionChanged(object sender, EventArgs e)
{
DataGridView view = sender as DataGridView;
if (view.AreAllCellsSelected(true))
{
foreach (DataGridViewRow row in view.Rows)
{
//deselect all invisible rows
if (!row.Visible)
row.Selected = false;
}
}
}
I've managed to solve it finally, the solution was simpler than I expected.
private void RemoveInvisibleSelection()
{
if (dataGridView1.SelectedRows.Count == dataGridView1.Rows.Count)
{
for (int i = 0; i < dataGridView1.SelectedRows.Count; i++)
if (!dataGridView1.SelectedRows[i].Visible)
dataGridView1.SelectedRows[i--].Selected = false; // decreased the index value since SelectedRows property loses an object
}
}

Datagridview Datagridcheckbox cell values not matching GUI

I have a unbound DataGridView with 6 columns, the first being a DataGridCheckBoxColumn. When a user clicks on a checkbox cell, I want to determine what cells have been checked and what ones are not. Here is my code:
private void UpdateSelectedPlaces()
{
//Clear out the places list each time the user selects a new item (or items)
_selectedPlaces.Clear();
foreach (DataGridViewRow row in placesGridView.Rows)
{
if (row.Cells[0].Value != null && row.Cells[0].Value.Equals(true))
{
_selectedPlaces.Add((TripPlace)row.DataBoundItem);
}
}
}
//Click event handler
private void placesGridView_CellContentClick(object sender, DataGridViewCellEventArgs e)
{
UpdateSelectedPlaces();
}
I am finding that the DataGridCheckBoxCells are not holding the correct value at the time of the click. This occurs for all rows. There seems to be no pattern really. I was hoping that the event was just not called at the right time (I.e. the checking of the checkbox was yet to be completed) but I cannot prove that.
In short, even though the GUI displays a checked checkbox, the back end thinks the checkbox is not checked when using .Value Is there a simpler way to just determine if each cell[0] is checked or not checked in a datagridview?
I am finding that the DataGridCheckBoxCells are not holding the
correct value at the time of the click.
This is normal and by design because you are using DataGridView.CellContentClick event. From MSDN:
Use this event to detect button clicks for a DataGridViewButtonCell or
link clicks for a DataGridViewLinkCell. For clicks in a
DataGridViewCheckBoxCell, this event occurs before the check box
changes value, so if you do not want to calculate the expected value
based on the current value, you will typically handle the
DataGridView.CellValueChanged event instead. Because that event occurs
only when the user-specified value is committed, which typically
occurs when focus leaves the cell, you must also handle the
DataGridView.CurrentCellDirtyStateChanged event.
The corrected code is similar to the other answer but unfortunately, the reason of the problem was not explained in that answer.
private void UpdateSelectedPlaces()
{
//Clear out the places list each time the user selects a new item (or items)
_selectedPlaces.Clear();
foreach (DataGridViewRow row in placesGridView.Rows)
{
if (row.Cells[0].Value != null && row.Cells[0].Value.Equals(true))
{
_selectedPlaces.Add((TripPlace)row.DataBoundItem);
}
}
}
private void placesGridView_CellValueChanged(object sender, DataGridViewCellEventArgs e)
{
UpdateSelectedPlaces();
}
private void placesGridView_CurrentCellDirtyStateChanged(object sender, EventArgs e)
{
if (placesGridView.IsCurrentCellDirty)
{
placesGridView.CommitEdit(DataGridViewDataErrorContexts.Commit);
}
}
private void placesGridView_CellValueChanged(object sender, DataGridViewCellEventArgs e)
{
Boolean bl;
if (placesGridView.Columns[e.ColumnIndex].Name == "name of check column")
{
DataGridViewCheckBoxCell checkCell = (DataGridViewCheckBoxCell)placesGridView.Rows[e.RowIndex].Cells[2]; //2 number of check column
//bl is the check box current condition.
//Change only this in your list eg list[e.RowIndex] = bl; No need to check all rows.
bl = (Boolean)checkCell.Value;
}
}
private void placesGridView_CurrentCellDirtyStateChanged(object sender, EventArgs e)
{
if (placesGridView.IsCurrentCellDirty)
{
placesGridView.CommitEdit(DataGridViewDataErrorContexts.Commit);
}
}

DataGridViewCheckBoxCell how to show checked when set during form load

I have a DataGridView that loads data from a DataTable, along with an unbound column of DataGridViewCheckBoxCells. The rows in the DataGridView are compared with a separate DataTable with values the user has saved, and if there is a match, the checkbox for that row should check.
Here is the code that compares the values and sets the checkbox value to 'true':
foreach (int j in selectedObjectives)
{
foreach (DataGridViewRow r in dgvObjectives.Rows)
{
if (j == Convert.ToInt32(r.Cells["ObjectiveID"].Value))
{
dgvObjectives.CurrentCell = r.Cells["Select"];
((DataGridViewCheckBoxCell)r.Cells["Select"]).Value = true;
//dgvObjectives.InvalidateCell(r.Cells["Select"]);
//dgvObjectives.EndEdit();
//dgvObjectives.CommitEdit(DataGridViewDataErrorContexts.Commit);
}
if (Convert.ToInt32(r.Cells["ObjectiveID"].Value) == selectedIndex)
{
r.Selected = true;
}
}
}
When I call the method to perform this action during the form load private void WQMDrill_Load(object sender, EventArgs e), the values are set correctly, but the checkboxes do not check. However, when called after the form is finished loading, the code works perfectly. Unfortunately for me, I absolutely need for these to check during the load process.
I hope I was clear with my issue, any help on this matter would be greatly appreciated. As you can see, I have tried to invalidate the cell alone, as well as the entire DataGridView control. I also have
private void dgvObjectives_CurrentCellDirtyStateChanged(object sender, EventArgs e)
{
if (this.dgvObjectives.CurrentCell.ColumnIndex == 0)
{
this.dgvObjectives.CommitEdit(DataGridViewDataErrorContexts.Commit);
}
}
That doesn't fire during this time. Thank you.
You can put your checkbox selection and update logic in the DataBindingComplete eventhandler, this fires after the FormLoad but before anything is displayed to the user.
I'm not certain that calling CommitEdit will actually fire the Paint on the cell. Try handling the CellMouseUp event and firing EndEdit if the column is a checkbox column.
private void dgvObjectives_CellMouseUp(object sender, DataGridViewCellMouseEventArgs e)
{
if (dgvObjectives.Columns[e.ColumnIndex] is DataGridViewCheckBoxColumn)
{
dgvObjectives.EndEdit();
}
}
I had the same problem, and tried a lot of different ways to deal with it, most failed, except when I tried this.BeginInvoke(new CDelegate()).

Triggering a checkbox value changed event in DataGridView

I have a grid view that has a check box column, and I want to trigger a drawing event as soon as the value of the cell is toggled. I tried the ValueChaged and the CellEndEdit and BeginEdit, and chose the selection mode as CellSelect. As for the the first 2 events, the event was triggered upon the finishing of the edit mode, like moving out of the current cell, or going back and forth. It's just a weird behavior.
Is there anything that triggers the event on the grid view as soon as the cell value is changed?
I use the CellContentClick event, which makes sure the user clicked the checkbox. It DOES fire multiple times even if the user stays in the same cell. The one issue is that the Value does not get updated, and always returns "false" for unchecked. The trick is to use the .EditedFormattedValue property of the cell instead of the Value property. The EditedFormattedValue will track with the check mark and is what one wishes the Value had in it when the CellContentClick is fired.
No need for a timer, no need for any fancy stuff, just use CellContentClick event and inspect the EditedFormattedValue to tell what state the checkbox is going into / just went into. If EditedFormattedValue = true, the checkbox is getting checked.
A colleague of mine recommends trapping the CurrentCellDirtyStateChanged event. See http://msdn.microsoft.com/en-us/library/system.windows.forms.datagridview.currentcelldirtystatechanged.aspx.
Another way is to handle the CellContentClick event (which doesn't give you the current value in the cell's Value property), call grid.CommitEdit(DataGridViewDataErrorContexts.Commit) to update the value which in turn will fire CellValueChanged where you can then get the actual (i.e. correct) DataGridViewCheckBoxColumn value.
private void grid_CellContentClick(object sender, DataGridViewCellEventArgs e)
{
grid.CommitEdit(DataGridViewDataErrorContexts.Commit);
}
private void grid_CellValueChanged(object sender, DataGridViewCellEventArgs e)
{
// do something with grid.Rows[e.RowIndex].Cells[e.ColumnIndex].Value
}
Target .NET framework: 2.0
Small update.... Make sure you use EditedFormattedValue instead of value as I tried value but it never give right status that is checked/unchecked most of the site still use value but as used in latest c# 2010 express below is one way to access..
grdJobDetails.Rows[e.RowIndex].Cells[0].EditedFormattedValue
Also _CellValueChanged event suggested or used by few must be usable for some cases but if you are looking for every check/uncheck of cell make sure you use _CellContentClick else per my notice I see not every time _CellValueChanged is fired.. that is if the same checkbox is clicked over & over again it does not fire _CellValueChanged but if you click alternately for example you have two chekbox & click one after other _CellValueChanged event will be fired but usually if looking for event to fire everytime the any cell is check/uncheck _CellValueChanged is not fired.
Try hooking into the CellContentClick event. The DataGridViewCellEventArgs will have a ColumnIndex and a RowIndex so you can know if a ChecboxCell was in fact clicked. The good thing about this event is that it will only fire if the actual checkbox itself was clicked. If you click on the white area of the cell around the checkbox, it won't fire. This way, you're pretty much guaranteed that the checkbox value was changed when this event fires. You can then call Invalidate() to trigger your drawing event, as well as a call to EndEdit() to trigger the end of the row's editing if you need that.
I finally implemented it this way
private void dataGridView1_CellMouseClick(object sender, DataGridViewCellMouseEventArgs e)
{
if (e.ColumnIndex >= 0 && e.RowIndex >= 0)
{
if (dataGridView1[e.ColumnIndex, e.RowIndex].GetContentBounds(e.RowIndex).Contains(e.Location))
{
cellEndEditTimer.Start();
}
}
}
private void dataGridView1_CellEndEdit(object sender, DataGridViewCellEventArgs e)
{ /*place your code here*/}
private void cellEndEditTimer_Tick(object sender, EventArgs e)
{
dataGridView1.EndEdit();
cellEndEditTimer.Stop();
}
I had the same issue, but came up with a different solution:
If you make the column or the whole grid "Read Only" so that when the user clicks the checkbox it doesn't change value.
Fortunately, the DataGridView.CellClick event is still fired.
In my case I do the following in the cellClick event:
if (jM_jobTasksDataGridView.Columns[e.ColumnIndex].CellType.Name == "DataGridViewCheckBoxCell")
But you could check the column name if you have more than one checkbox column.
I then do all the modification / saving of the dataset myself.
"EditingControlShowing" event doesn't fire on checkbox value change. Because display style of the checkbox cell doesn't not change.
The workaround i have used is as below. (I have used CellContentClick event)
private void gGridView1_CellContentClick(object sender, DataGridViewCellEventArgs e)
{
if (string.Compare(gGridView1.CurrentCell.OwningColumn.Name, "CheckBoxColumn") == 0)
{
bool checkBoxStatus = Convert.ToBoolean(gGridView1.CurrentCell.EditedFormattedValue);
//checkBoxStatus gives you whether checkbox cell value of selected row for the
//"CheckBoxColumn" column value is checked or not.
if(checkBoxStatus)
{
//write your code
}
else
{
//write your code
}
}
}
The above has worked for me. Please let me know if need more help.
I found a simple solution.
Just change the cell focus after click on cell.
private void DGV_CellContentClick(object sender, DataGridViewCellEventArgs e)
{
if (e.ColumnIndex == "Here checkbox column id or name") {
DGV.Item(e.ColumnIndex, e.RowIndex + 1).Selected = true;
//Here your code
}
}
Don't forget to check if the column of your (ckeckbox + 1) index exist.
Every one of the CellClick and CellMouseClick answers is wrong, because you can change the value of the cell with the keyboard and the event will not fire. Additionally, CurrentCellDirtyStateChanged only fires once, which means if you check/uncheck the same box multiple times, you will only get one event. Combining a few of the answers above gives the following simple solution:
private void dgvList_CurrentCellDirtyStateChanged(object sender, EventArgs e)
{
if (dgvList.CurrentCell is DataGridViewCheckBoxCell)
dgvList.CommitEdit(DataGridViewDataErrorContexts.Commit);
}
private void dgvList_CellValueChanged(object sender, DataGridViewCellEventArgs e)
{
// Now this will fire immediately when a check box cell is changed,
// regardless of whether the user uses the mouse, keyboard, or touchscreen.
//
// Value property is up to date, you DO NOT need EditedFormattedValue here.
}
cellEndEditTimer.Start();
this line makes the datagridview update the list of checked boxes
Thank you.
I found a combination of the first two answers gave me what I needed. I used the CurrentCellDirtyStateChanged event and inspected the EditedFormattedValue.
private void dgv_CurrentCellDirtyStateChanged(object sender, EventArgs e)
{
DataGridView dgv = (DataGridView)sender;
DataGridViewCell cell = dgv.CurrentCell;
if (cell.RowIndex >= 0 && cell.ColumnIndex == 3) // My checkbox column
{
// If checkbox checked, copy value from col 1 to col 2
if (dgv.Rows[cell.RowIndex].Cells[cell.ColumnIndex].EditedFormattedValue != null && dgv.Rows[cell.RowIndex].Cells[cell.ColumnIndex].EditedFormattedValue.Equals(true))
{
dgv.Rows[cell.RowIndex].Cells[1].Value = dgv.Rows[cell.RowIndex].Cells[2].Value;
}
}
}
Use this code, when you want to use the checkedChanged event in DataGrid View:
private void grdBill_CellContentClick(object sender, DataGridViewCellEventArgs e)
{
grdBill.CurrentCell = grdBill.Rows[grdBill.CurrentRow.Index].Cells["gBillNumber"];
}
private void grdBill_CellEndEdit(object sender, DataGridViewCellEventArgs e)
{
calcBill();
}
private void calcBill()
{
listBox1.Items.Clear();
for (int i = 0; i < grdBill.Rows.Count - 1; i++)
{
if (Convert.ToBoolean(grdBill.Rows[i].Cells["gCheck"].Value) == true)
{
listBox1.Items.Add(grdBill.Rows[i].Cells["gBillNumber"].Value.ToString());
}
}
}
Here, grdBill = DataGridView1, gCheck = CheckBox in GridView(First Column), gBillNumber = TextBox in Grid (Second column).
So, when we want to fire checkchanged event for each click, first do the CellContentClick it will get fire when user clicked the Text box, then it will move the current cell to next column, so the CellEndEdit column will get fire, it will check the whether the checkbox is checked and add the "gBillNumber" in list box (in function calcBill).
Working with an unbound control (ie I manage the content programmatically), without the EndEdit() it only called the CurrentCellDirtyStateChanged once and then never again; but I found that with the EndEdit() CurrentCellDirtyStateChanged was called twice (the second probably caused by the EndEdit() but I didn't check), so I did the following, which worked best for me:
bool myGridView_DoCheck = false;
private void myGridView_CurrentCellDirtyStateChanged(object sender, EventArgs e)
{
if (!myGridView_DoCheck)
{
myGridView_DoCheck = true;
myGridView.EndEdit();
// do something here
}
else
myGridView_DoCheck = false;
}
Using the .EditedFormattedValue property solves the problem
To be notified each time a checkbox in a cell toggles a value when clicked, you can use the CellContentClick event and access the preliminary cell value .EditedFormattedValue.
As the event is fired the .EditedFormattedValue is not yet applied visually to the checkbox and not yet committed to the .Value property.
private void dataGridView1_CellContentClick(object sender, DataGridViewCellEventArgs e)
{
var checkbox = dataGridView1.CurrentCell as DataGridViewCheckBoxCell;
bool isChecked = (bool)checkbox.EditedFormattedValue;
}
The event fires on each Click and the .EditedFormattedValue toggles

Categories

Resources