I'm trying to update a value in the database after the user finishes editing a cell. The DataGridView is populated manually (and dynamically) due to some complexities involved in DataBinding this particular data set.
In the event handler for CellEndEdit, I have called my method to update the value back to the database, and it works as planned. The trouble occurs when I attempt to repopulate the DataGridView. For reference, this method is called PopulateAccounts().
Once a value is entered in the cell, everything works perfectly if the user presses Enter on their keyboard or clicks another control on the form. However, clicking on another cell in the same DataGridView results in the following error:
Operation is not valid because it results in a reentrant call to the SetCurrentCellAddressCore function.
The PopulateAccounts() method includes DataGridView.Rows.Clear() which throws the error. I've researched this error in a few related SO questions, and it seems to be related to threading, which I'm clueless about. One suggested fix is to invoke my PopulateAccounts() method:
BeginInvoke(new MethodInvoker(PopulateAccounts));
This works, but it causes the DataGridView to select all cells preceding the one that was edited (see screenshot below)
Again, this only happens when clicking out of the cell and into another. Otherwise, i.e. pressing Enter or clicking on another control, it simply selects the first cell.
Here is the code for PopulateAccounts() for reference:
// Populates the DataGridView's account records.
private void PopulateAccounts()
{
dgvShed.Rows.Clear();
using (PassShedEntities context = new PassShedEntities(conString))
{
foreach (Account acct in context.Account.Where(a => a.Category_id == (int)lstCategories.SelectedValue))
{
dgvShed.Rows.Add();
foreach (DataGridViewColumn col in dgvShed.Columns)
{
Credential cred = (from c in context.Credential
join f in context.Field on c.Field_id equals f.Id
join a in context.Account on c.Account_id equals a.Id
where c.Account_id == acct.Id
where f.Label == col.Name
select c).SingleOrDefault();
dgvShed.Rows[dgvShed.Rows.Count - 1].Cells[col.Name].Tag = cred.Id;
dgvShed.Rows[dgvShed.Rows.Count - 1].Cells[col.Name].Value = cred.Value;
}
}
}
}
Once a value is entered in the cell, everything works perfectly if the user presses Enter on their keyboard or clicks another control on the form
The above actions doesn't change the CurrentCell and the grid repopulation works fine.
However, clicking on another cell in the same DataGridView results in the following error:
When you click on another cell the CurrentCell of the datagridview is changing and grid internally calls the SetCurrentCellAddressCore to do the cell change. This call internally raises CellEndEdit event which triggers the SetCurrentCellAddressCore again and this cycle goes on resulting in infinite loop. The DataGridView detects this loop and throws exception resulting in the error you have mentioned.
In the above case the my guess is that DataGridView somehow goofed up the selection of cells. You could simply clear the selection at the end of PopulateAccounts with DataGridView.ClearSelection() method.
I've been wrestling with just about the exact same problem and just came up with a solution that works with my specifics. I moved all the code from the CellEndEdit event to the CellValueChanged event (which fires right before the former) and now both options for ending the cell-edit: the enter-stroke and clicking off to another cell, work great.
I'm not sure what the perfectly proper way to use the events is, but this seems to be working!
Related
I'm loading a list of databases from my app.config, and I want to check my datagridview checkboxes on these databases. When I debug the method it assigns a true value, but it doesn't change on UI and the value of checkbox is still false.
The method runs before showing my Windows Forms, after InitializeComponent(). If I click on the button, that calls the same Method after Windows Forms is loaded it works as a charm. Not automatically though.
I'm doing this because I want to recreate my datagridview with all checks every time I launch the application, and it's been really bugging me. I tried refreshing and updating dataGridView, commiting updates, ending updates before I assign a value, I also tried setting property "TrueValue" on checkbox column to true, but it didn't help.
private void CheckDatabasesFromConfig()
{
for (int i = 0; i < SQLDatabases.RowCount; i++)
{
if (listOfDatabasesToBackup.Contains(SQLDatabases.Rows[i].Cells["Database Name"].Value))
{
SQLDatabases.Rows[i].Cells["Backup"].Value = true;
}
}
}
I want to open the application and see checkboxes checked according to this list on my datagridview, without clicking anything.
"Database Name" in Cells["Database Name"] is not a valid identifier so "if" expression is always false.
correct your column name defined in dataGridView.
I managed to solve the problem - I'm sorry for misinformation. I had the datagridview in second page of the tabcontrol, and since I didn't open it, the controls didn't load.
It helped to perform a simple tabPage2.show() before calling my method.
Issue Solved - Posted for reference.
Hi, I have a strange error occurring.
I have a DataGridView that I am using to store a list of records, these records are being updated from a source by a BackgroundWorker. There is a CheckBox Column that allows the user to select records from the DataGridView for processing.
When I check the CheckBox the first row(and only the first row), the checkbox does not seem to refresh correctly and displays as unchecked. The checkbox is still being treated as checked and the logic tied to it is working correctly. This is a cosmetic issue that may confuse users.
any other CheckBox in the DataGridView draws correctly at all times.
Clicking anywhere on the DataGridView causes the CheckBox state to redraw correctly.
It seems like a redrawing issue, but I am having trouble tracking down why it is drawing like this.
Some details of relevance.
It is necessary for me to reload the dataset with each refresh.
I have set the cell highlight colors to be the same as the non-highlighted ones.
I have a routine that checks the Checked state of the CheckBoxes, records the records that have been selected and rechecks the checkboxes when the dataset has been replaced. -This is working correctly
See the following code.
private void BgwDocketList_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
var checkedRows = from DataGridViewRow r in Dgv_Batch.Rows
where Convert.ToBoolean(r.Cells[0].Value) == true
select r;
List<int> L = new List<int>();
foreach (DataGridViewRow x in checkedRows)
{
L.Add((int)x.Cells[1].Value);
}
Dgv_Batch.DataSource =e.Result;
foreach (DataGridViewRow v in Dgv_Batch.Rows)
{
if (L.Count(x=>x==(int)v.Cells[1].Value)>0)
{
v.Cells[0].Value = true;
}
}
}
Have tried changing the selected cell programmatically
I have tried setting focus to the DataGridView in an attempt to programmatically recreate the effect of clicking on the control. This does not redraw the checkbox. only a mouse click seems to do this.
...Ok...
While Writing this I have managed to solve the problem. I guess it helps to write down the steps to ensure they are checked out.
The issue was that after updating the checkboxes I needed to use
DataGridView.EndEdit()
I would have expected that routine to have worked on the last record last as opposed to the first. I guess this relates back to the DataGridView.DataGridViewSelectedRowCollection containing a reversed index.
I have posted this Question anyway in the hope that It may help someone with the same issue.
Utilize
DataGridView.EndEdit();
This will solve the problem.
I am using the DataGridView Control for reading and writing an XML file through XML Serialization.
I have an issue as explained below:
I read an XML file and populate DataGridView controls with the deserialized object.
I update all the values on the DataGridView on the cell.
I choose the File Save As option without losing focus on the last cell.
After this, the value of the particular cell is not updated. If I intentionally shift focus away (say I click on another cell on the same grid) the value is updated.
Can anyone suggest any solution for this?
The best way (though quick and dirty) is to assign the currentCell value to Nothing.
For example, in the save method, do:
dgvMyGrid.CurrentCell = Nothing
and then proceed further.
It's because the edited cell value is not committed to the DataSource until it's validated, which happens when the cell lose focus. If you want to commit the modifications immediately, you can handle the CurrentCellDirtyStateChanged event, and call the CommitEdit method in the handler :
void dataGridView1_CurrentCellDirtyStateChanged(object sender,
EventArgs e)
{
if (dataGridView1.IsCurrentCellDirty)
{
dataGridView1.CommitEdit(DataGridViewDataErrorContexts.Commit);
}
}
If I understand you correctly, a cell is in editing mode and you're trying to programmatically stop editing and pass the value to the underlying datasource?
I'm using a somewhat "dirty" approach to do that in one of my applications:
if (dataGridView1.CurrentCell.IsInEditMode)
{
int y = dataGridView1.CurrentCellAddress.Y;
int x = dataGridView1.CurrentCellAddress.X;
if (y > 0)
dataGridView1.CurrentCell = dataGridView1.Rows[y - 1].Cells[x];
else
dataGridView1.CurrentCell = dataGridView1.Rows[y + 1].Cells[x];
dataGridView1.CurrentCell = dataGridView1.Rows[y].Cells[x];
}
That piece of code first checks whether the current cell is in edit mode. Then it changes the current cell programmatically (either to the previous row or the next row in case we're in the first row). After that, it restores the current cell selection.
You would call this code in your "File Save As" handler.
I had the same situation and I was even using accelerator keys for save button for saving the grid values. When I click on Save button focus lost from DGV and hence cell value is committed, but when I use accelerator keys focus doesn't lost from DGV hence no committing of the cell value.
After looking at the Amit Karmakar answer out of curiosity I tried that answer and it worked. To find out more details I went into debugging of the DGV and found that it is really same thing as commitedit which somehow doesn't work if you use it in the save button click.
When we set CurrentCell of DGV to null, before setting it to null DGV first gets the edited value and pushes it in to cell value and then sets CurrentCell REFERENCE to null. Here it doesn't mean that it is setting underlying DGV cell to null. Hence this works perfectly for the above problem.
Note: This solution may not work perfectly when you have validating events for the cell and if user enters invalid data which will fail validation. In this case setting current cell to null also fails as it cannot push the value to cell.
I gave this explanation as I've raised question on Amit Karmakar answer asking how can it be possible. I thought it may help some other, so dropped this explanation as answer.
OK, this is UGLY but it works to get the FINAL CHANGES from the grid WITHOUT having to move to another row:
With DataGridView1
.DataSource = Nothing
.DataSource = gridDataTable
Dim changedFoo As DataTable = gridDataTable.GetChanges
End With
However I still like the answer from Amit Karmakar the best.
I've added the 'DataGridView1.CurrentCell = Nothing' to the DataGridView1 LostFocus event.
You can get the value of a cell which is not yet committed using the EditedFormattedValue property for the current cell, as below
dataGridView1.CurrentCell.EditedFormattedValue
There are 2 distinct UI concept: CellValueChanging vs CellValueChanged.
Quoted from DevExpress Documentation:
The CellValueChanging event is raised each time the edited value is being changed (the user types or deletes a character, chooses a value from the dropdown list, etc.).
On the other hand, CellValueChanged is raised when user had done cell editing by hitting enter or clicking outside of the active cell.
Now my problem is, I have a combobox type column, and I want to always skip the CellValueChanging and make the change final. The current behavior is when user select an item from the combobox, the change doesn't take effect immediately(e.g, the view will not be resorted as per the change). The change is not accepted until user hit enter or click outside of the cell.
---------------07/26/2013 2:25PM update---------------
Sorry that my previous question description misled everybody, so I'll rephrase it.
Here is the current behavior:
Pic1: beginning state. Rows are sorted alphabetically by Target.
Pic2: Change the 2nd row value from B to D
Pic3: After a single mouse left click on item D, the dropdown disappears and the cell value changes to D. However, the rows are not resorted
Pic4: After clicking outside the cell or hitting enter, the rows are resorted.
What I want to achieve is to skip the step in Pic3. In other word, I want any changes committed immediately, without having to make an extra kepboard or mouse click.
What I am showing here is a simplified example of my application. I can't move my CellValueChanged event handler logic to CellValueChanging or EditValueChanged because it would cause some errors.
You want to make some UI change once user edit the cell value, right? I think generally you have to give up handling the CellValueChanged event, but use the CellValueChanging Event instead:
pseudocode:
HandleCellValueChanging(object sender, CellValueChangingEventArgs e)
{
// Get underlying object
// and write the value direct into the object
var rowObj = (YourType)gridview.GetRow(gridView.FocusedRowHandle);
rowObj.TargetField = e.NewValue; // e.Value or e.NewValue, not sure
// Then Do your UI effect
}
Another option is to use RepositoryItemEditor and handle the key-up and/or Mouse-up event instead.
If you want to to catch the moment when an end-user changes some value in gridview's cell editor (e.g. select item in combobox), please handle the EditValueChanged event of a corresponding RepositoryItem. To immediately post this value (make the changes final), you need to call the PostEditor method of a corresponding container:
repositoryItemComboBox1.EditValueChanged += repositoryItemComboBox1_EditValueChanged;
//...
void repositoryItemComboBox1_EditValueChanged(object sender, EventArgs e) {
gridView1.PostEditor();
// do something else if it needed
}
Related example: E3234 - How to update a row's style immediately after a an inplace editor's value is changed
I am using a WinUltraGrid to enter new rows into our database. When a new row is being entered and the user finishes entering the data, I am checking for null values in the BeforeRowUpdate event which occurs when the user leaves that row by clicking the save button or by clicking another row in the grid. If any of the columns are null then I set e.cancel to true to cancel the event after displaying a user friendly message.
My problem is that when I return to the grid, my partially entered row is gone and I have to start over. Any ideas on how to keep the partial data in the new row at the top of my grid? I am using VS 2008 and C# .Net.
Thanks
In general (I don't have experience with the WinUltraGrid component), setting Cancel to true on an event args object does just what you're seeing - cancels the row update event. I suspect that the data is probably rebound after that, clearing the row. What you'd probably need to do is to set up some sort of post-processing after you set the cancel (though timing could be tricky, if the component is rebinding under the covers). What I assume might work would be to save the non-null values in a custom object or dictionary, then after the event is cancelled, go back, check the dictionary/custom object, and push the values into the new row again, setting focus as needed.