Using CellDirtyStateChanged event with CheckBoxColumns - c#

I have the following code that I am using in order to get the total count of employees selected within a datagridview.
I want it to calculate as soon as a user checks a check box or when a user unchecks it.
private void dgvEmployeesToProcess_CellDirtyStateChanged(object sender, EventArgs e)
{
try
{
if (dgvEmployeesToProcess.CurrentCell.ColumnIndex == 4)
{
txtNoOfEmpToProcess.Text = string.Empty;
int count = 0;
foreach (DataGridViewRow row in dgvEmployeesToProcess.Rows)
{
DataGridViewCheckBoxCell checkBox = (DataGridViewCheckBoxCell)row.Cells[4];
if (Convert.ToBoolean(checkBox.Value) == true)
{
count = count + 1;
}
}
// show total count in textbox
txtNoOfEmpToProcess.Text = count.ToString();
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
The problem that I am having is that when I check the checkbox in row[0], ie. the first, checkBox.Value is always null and count = 0. When I click on a second checkbox, count = 1 and so forth.
What do I need to do in order to get the count correct? Do I need to use something other than CellDirtyStateChanged?

Add this line at the start of dgvEmployeesToProcess_CellDirtyStateChanged:
dgvEmployeesToProcess.CommitEdit(DataGridViewDataErrorContexts.Commit);
This will commit the changes (in example check/uncheck a particular column) so you will get the actual result.

Related

How to set focus to the last row in a DataGridView?

How to set focus to the absolute last row?
I tried all the solutions I found in StackOverflow, but it always goes to the Row before the last, not the last one.
when I edit a value in the Quantity Column and press enter, I add two rows: the colored one which is read-only and also another row where I can add the next item.
Now, when I press Enter after typing the quantity, the focus goes to second row quantity: I need it to go to the first Column (Barcode) of the last row.
private void onCellEditEnd(object sender, DataGridViewCellEventArgs e)
{
if(header == barcode)
{
dataGridView1.Rows[dataGridView1.CurrentRow.Index].Cells[1].Value = item.Qty;
dataGridView1.Rows[dataGridView1.CurrentRow.Index].Cells[2].Value = item.Item;
dataGridView1.Rows[dataGridView1.CurrentRow.Index].Cells[3].Value = item.Mrp;
dataGridView1.Rows[dataGridView1.CurrentRow.Index].Cells[4].Value = item.Discount;
dataGridView1.Rows[dataGridView1.CurrentRow.Index].Cells[5].Value = item.Cgst;
dataGridView1.Rows[dataGridView1.CurrentRow.Index].Cells[6].Value = item.Sgst;
dataGridView1.Rows[dataGridView1.CurrentRow.Index].Cells[7].Value = item.Amount;
dataGridView1.Rows.Add();
dataGridView1.Rows[dataGridView1.CurrentRow.Index+1].Cells[1].Value = item.Details;
dataGridView1.Rows[dataGridView1.CurrentRow.Index+1].Cells[4].Value = item.Discount;
dataGridView1.Rows[dataGridView1.CurrentRow.Index+1].Cells[5].Value = item.Cgst;
dataGridView1.Rows[dataGridView1.CurrentRow.Index+1].Cells[6].Value = item.Sgst;
dataGridView1.Rows[dataGridView1.CurrentRow.Index + 1].ReadOnly = true;
dataGridView1.Rows.Add();
}
dataGridView1.CurrentCell = dataGridView1.Rows[dataGridView1.CurrentRow.Index].Cells[2];
}
else if(header == quantity)
{
dataGrid.CurrentCell = dataGrid.Rows[2].Cell[0]; //tried this
dataGridView1.Rows[dataGridView1.RowCount - 1].Selected = true; //tried this
}
}
The second row is part of the first row, so on the CellEndEdit of Quantity I want the focus to go to the 3rd Row, in the Barcode Column, which I have added in the last line of code, using dataGridView1.Rows.Add();
To select a specific row you must use
dataGrid.Rows[index].Selected = true;
Wherever you make a change and you want the last row to be selected, you must use the following code :
dataGrid.Rows[dataGrid.RowCount - 1].Selected = true;
I had the same problem as you a couple weeks ago this is what I did.
private void gridControl1_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Enter)
{
int indexOfRecent = gridView1.DataRowCount - 1;
gridView1.FocusedRowHandle = indexOfRecent;
}
}

Blocked from editing DataGridView record

Is it possible to block duplicate records, but also edit a selected record? I am working on something similar to this.
Example
When I have numerous records and I update the 2nd or 3rd one, I expect a warning whenever I try to use the first record's ID; that is working nicely. However, whenever I try to edit the first record's name, city, etc, the duplicate ID error pops up, since I did not alter the ID; it is counting itself as a duplicate. No idea what to do. I tried using breakpoints, but I am not seeing anything of interest. Thanks.
private void btnUpdate_Click(object sender, EventArgs e)
{
if (dgvProfiles.SelectedCells.Count <= 0)
{
MessageBox.Show("No record was selected to update.");
}
else {
for (int row = 0; row < dgvProfiles.Rows.Count; row++)
{
for (int col = 0; col < dgvProfiles.Columns.Count; col++)
{
if (dgvProfiles.Rows[row].Cells[col].Value != null &&
dgvProfiles.Rows[row].Cells[col].Value.Equals(txtEmail.Text.Trim()))
{
MessageBox.Show("Duplicate email was entered.");
return;
}
else if (dgvProfiles.Rows[row].Cells[col].Value != null &&
dgvProfiles.Rows[row].Cells[col].Value.Equals(txtID.Text.Trim()))
{
MessageBox.Show("Duplicate ID was entered.");
return;
}
}
}
DataGridViewRow newDataRow = dgvProfiles.Rows[indexRow];
newDataRow.Cells[0].Value = txtID.Text;
newDataRow.Cells[1].Value = txtName.Text;
newDataRow.Cells[4].Value = txtEmail.Text;
newDataRow.Cells[5].Value = txtCity.Text;
newDataRow.Cells[6].Value = cbxState.Text;
}
}
Trying using the Validation events.
And if you bind your DataGridView to a DataSet it will be easier to walk the values in the DataSet to find duplicates. See DataSource property.

Row index provided is out of range. Parameter name: index

I trying to move selected items from one gridview to another gridview, when the user has selected items(more than one item). It reads the first row and third row item in gridview and sends it to gridview2.
Gives me the above error in try catch. How do I read each row by row without jumping to read to the third row when user has selected a second row. I have been working on this for a week now. I am a beginner in C#. Thanks in advance.
// dgSubjectGridView2 is cleared, first Gridview(dgSubjectGridView) is moving selected item from its grid to dgSubjectGridView2
private void btnGo_Click(object sender, EventArgs e)
{
dgSubjectGridView2.Rows.Clear();
try
{
DataGridViewRow row = new DataGridViewRow();
//Counts the total number of rows in dgSubjectGridView
for (int i = 0; i < dgSubjectGridView.Rows.Count; i++)
{
row = dgSubjectGridView.Rows[i];
Boolean checkstate;
checkstate = Convert.ToBoolean(row.Cells[0].Value);
foreach (DataGridViewRow item in dgSubjectGridView.Rows)
{
if (checkstate == true)
{
dgSubjectGridView2.Rows.Add(false, item.Cells[1].Value.ToString());
dgSubjectGridView.Rows.RemoveAt(row.Index);
}
}
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
finally
{
//End
}
}
It seems that you are twice looping through the same GridView row collection. Replace your try block with the following code and see if it produce the desired result.
try
{
foreach (DataGridViewRow row in dgSubjectGridView.Rows)
{
Boolean checkstate = Convert.ToBoolean(row.Cells[0].Value);
if (checkstate == true)
{
dgSubjectGridView2.Rows.Add(false, row.Cells[1].Value.ToString());
dgSubjectGridView.Rows.RemoveAt(row.Index);
}
}
}

Compare 2 different columns in the datagridview

In my datagridview, four columns 1 & 2 are read only col 3 & 4 got number values. I want to compare that 4th column must be greater that 3rd column.
For example:
If the 4th column value is less than the 3rd column then I want to propose message doesn't navigate to another control.
My simple approach seems not working. How can I compare 2 specific columns for this kind of condition?
private void datagridview_CellValidating(object sender, CellValidatingEventArgs e)
{
try
{
int bbor = datagridview.CurrentCell.ColumnIndex;
int ebor = datagridview.CurrentCell.RowIndex;
if (ebor <= bbor)
{
MessageBox.Show("Please verify the value");
e.Cancel = true;
}
}
catch (Exception exception)
{
}
}
we meet again. Use the cell_click event:
private void dataGridView1_CellClick(object sender, DataGridViewCellEventArgs e)
{
if (e.ColumnIndex != 0)
{
if (Double.Parse(dataGridView1.Rows[e.RowIndex].Cells[e.ColumnIndex].Value.ToString()) <= Double.Parse(dataGridView1.Rows[e.RowIndex].Cells[e.ColumnIndex - 1].Value.ToString()))
{
MessageBox.Show("Please verify the value");
}
}
}
EDIT 1: This seems to work fine, lemme know.
private void dataGridView1_CellValidating(object sender, DataGridViewCellValidatingEventArgs e)
{
if (e.ColumnIndex != 0)
{
if (Double.Parse(dataGridView1.Rows[e.RowIndex].Cells[e.ColumnIndex].Value.ToString()) <= Double.Parse(dataGridView1.Rows[e.RowIndex].Cells[e.ColumnIndex - 1].Value.ToString()))
{
MessageBox.Show("Please verify the value");
e.Cancel = true;
}
}
}
Edit 2: Updated for Telerik controls
private void radGridView1_CellValidating(object sender, Telerik.WinControls.UI.CellValidatingEventArgs e)
{
if (e.ColumnIndex != 0)
{
if (e.Value != null && radGridView1.Rows[e.RowIndex].Cells[e.ColumnIndex - 1].Value != null)
{
if (Double.Parse(e.Value.ToString()) <= Double.Parse(radGridView1.Rows[e.RowIndex].Cells[e.ColumnIndex - 1].Value.ToString()))
{
MessageBox.Show("error");
e.Cancel = true;
}
}
}
}
I'd see something more or less like that:
If you want to check all rows:
DataRow dr;
for(int i = datagridview.Rows.Count-1; i > 0; i--) {
dr = datagridview.Rows[i];
if(dr[e.ColumnIndex] > dr[e.ColumnIndex+1]){
//your message code
e.Cancel = true;
break; (or return;)
}
}
If you want to check only the current row where the cell is being edited:
DataRow dr = datagridview.Rows[e.RowIndex];
e.Cancel = dr[e.ColumnIndex] > dr[e.ColumnIndex+1];
if(e.Cancel)
//your message code
Maybe you will need to convert objects to int for comparison.
See the Rows Property for DataGridView http://msdn.microsoft.com/en-us/library/system.windows.forms.datagridview.rows.aspx
this.dataGridView1[col, row].Value
references a specific cell for each row
foreach (Row r in this.dataGridView1.Rows) {
if (r.Cells[3].Value <= r.Cells[2].Value ) {
System.Console.WriteLine ("error");
}
}
For your validation check you'll want to use the FormattedValue property to see what value your user wants to insert in the cell they've edited. You can't use the current cell value because it doesn't update to the new value until after the CellValidating completes without DataGridViewCellValidatingEventArgs.Cancel being set to true.
Something like this:
private void datagridview_CellValidating(object sender, DataGridViewCellValidatingEventArgs e) {
// This is the new proposed value the user entered; could be for column 3 or 4.
int newValue = int.Parse(e.FormattedValue.ToString());
// See which column fired the CellValidating event and use the new proposed value for it
// in place of the cell's actual value for purposes of our validation.
int col3Value = (e.ColumnIndex == 2) ? newValue : (int)dataGridView1[2, e.RowIndex].Value;
int col4Value = (e.ColumnIndex == 3) ? newValue : (int)dataGridView1[3, e.RowIndex].Value;
if (col3Value <= col4Value) {
MessageBox.Show("Please verify the value");
e.Cancel = true;
}
}
The code I show here is to demonstrate a solution to your problem. In your actual production code
you'll want to verify that casting from object to int is successful (through int.TryParse) or catch the exception that's raised when this operation fails. When this happens you can Cancel = true the cell validation and present to the user a message that he must enter a number.
And another quick note: don't use empty catch blocks (though I realize this probably isn't in your production code).

How to disable checking checkbox column in DataGridView - Windows forms?

I have a checkbox column in a DataGridView and I want to validate whether the user can check a checkbox by counting the number of checkboxes they click and deciding
then to disable checking.
Can someone guide me how to do this effectively??
Quick code sample:
public partial class DGVCheckBoxTesting : Form
{
private const int MAX_CHECKS = 5;
public DGVCheckBoxTesting()
{
InitializeComponent();
this.dataGridView1.Columns.Add("IntColumn", "IntColumn");
this.dataGridView1.Columns.Add(new DataGridViewCheckBoxColumn { Name = "BoolColumn" });
for (int i = 0; i <= 10; i++)
{
this.dataGridView1.Rows.Add(i, false);
}
this.dataGridView1.CellContentClick += new DataGridViewCellEventHandler(dataGridView1_CellContentClick);
this.dataGridView1.CellContentDoubleClick += new DataGridViewCellEventHandler(dataGridView1_CellContentDoubleClick);
}
private void dataGridView1_CellContentDoubleClick(object sender, DataGridViewCellEventArgs e)
{
this.ValidateCheckBoxState(e);
}
private void dataGridView1_CellContentClick(object sender, DataGridViewCellEventArgs e)
{
this.ValidateCheckBoxState(e);
}
private void ValidateCheckBoxState(DataGridViewCellEventArgs e)
{
if (e.ColumnIndex != 1) //bool column
{ return; }
this.dataGridView1.EndEdit();
bool value = (bool)this.dataGridView1.Rows[e.RowIndex].Cells[e.ColumnIndex].Value;
int counter = 0;
foreach (DataGridViewRow row in this.dataGridView1.Rows)
{
if (row.Cells[1].Value != null && (bool)row.Cells[1].Value)
{
counter++;
}
}
if (counter > MAX_CHECKS)
{
this.dataGridView1.Rows[e.RowIndex].Cells[e.ColumnIndex].Value = false;
this.dataGridView1.EndEdit();
}
}
}
Basically, what this code does is it adds an Integer column and a Bool column to the DataGridView. Then, on the CellContentClick event, if the checkbox column was clicked, first we commit the edit (if you don't do this, you'll have all kinds of trouble figuring out if the checkbox is checked or not). Then, we loop through the rows and count all the checked rows. Then, if the amount is greater than what we want to allow, we just set it back to false, and again re commit the edit. Test it out, it works. May not be the most elegant solution, but the DGV can be tricky with the checkboxes, so this is how I would do it.
EDIT: Just a small edit, I hooked into the ContentDoubleClick event as well, as I noticed you were able to beat the validation if you quickly clicked on the cell. Should work better now.

Categories

Resources