EndEdit on BindingSource updates DataTable, but rowstate still unchanged - c#

I have a bindingsource which has a datasource which is a datatable.
All the winforms controls has added databindings to the bindingsource
I do a value change in the GUI (writes to the controls' .text property)
then on Save i do following
bsSending.EndEdit();
((DataRowView)this.bsSending.Current).Row) now contains the new values, but the RowState is still unchanged. How can this be possible? I haven't any calls to AcceptChanges() before I make the value changes in the GUI

UPDATE:
Don't know why, but it seems that calling the specific row's EndEdit does the trick. The row's parent is a datatable and the table's dataset is the datasource of the bsSending Datasource.
Calling bsSending.EndEdit() only updates the values but doesn't update the rowstate.
I have surfed the .net for similiar problems and they indicate that when calling AcceptChanges() on the dataset BEFORE binding data, then you may get this error (values updated to dataset but rowstate remains unchanged). I haven't seen any solutions to the problem, though, so I keep my workaround solution

I know it's old post.
It can be solved by calling DataRowView.EndEdit directly, but in my case I found the exact reason:
I accidentally bound two properties of one control to different columns.
(In my case I simultaneously bound Devexpress' TextEdit control - EditValue and Text properties to different columns of the underlaying table).
Maybe it will help someone even in 2016+, because it was nasty bug to catch.

I have a similar issue. I have a grid on the first tab page and textboxes on the second tab page, all binded to the same binding source.
I change the row content, the content get changed in the grid but the row start is Unchanged.
My code was:
DataRow dataRow = ((DataRowView)bindingSource1.Current).Row;
if(dataRow.RowState != DataRowState.Modified)
I expected to have a Modified rowState.
The missing code was:
bindingSource1.EndEdit();
Entire solution here:
private void tabControl1_SelectedIndexChanged(object sender, EventArgs e)
{
if (tabControl1.SelectedIndex == 0)
{
bindingSource1.EndEdit();
DataRow dataRow = ((DataRowView)bindingSource1.Current).Row;
if(dataRow.RowState != DataRowState.Modified)
{
return;
}
DialogResult userOption = MessageBox.Show("Save?", "Confirm", MessageBoxButtons.YesNo, MessageBoxIcon.Question);
if (userOption == System.Windows.Forms.DialogResult.Yes)
{
Save();
}
}
}
My solution:

I have run into the similar situation in one form and I realized that problem was in binding made to labels ToolTip property. First I realized, that when I changed value for null from null to some string, the problem disappears, but after some other changes made to the project, the problem appeared again. When I deleted binding to the ToolTip property, problem disappeared again. Still I do not know if for ever.

#Jan Strnad 's answer enlightened me and helped me fix my issue.
Problem: I was using a binding source in win forms. When binding the controls in the form, I miss clicked and bound a field to the form's Text property in (DataBindings). The same bindingsource field was bound (correctly) to a combobox. Because of this double binding on the field, the rowstate never changed from Unmodified.
Fix: when rows don't update and you use a binding source, first check that the binds are correctly put in place.

Yes, I have the same experience. I am using Visual Studio 2010 and .NET Framework v.4.0. I am working with DataGridView control which is bound to a DataTable.
If the user tries to close the form while the cell value is still in edit mode, I want DataGrid to end the edit mode and ask the user whether he wants to save or lose the changes.
This is the code which works for me:
if (dgv.IsCurrentCellInEditMode)
{
dgv.EndEdit();
updatedData.Rows[dgv.CurrentCell.RowIndex].EndEdit();
}
if (updatedData.GetChanges() != null && updatedData.GetChanges().Rows.Count > 0)
{
// if there are changes, update the dataset
}
I want to point out that I needed to call both - EndEdit() on DataGridView to let it end the editing. Then call EndEdit() on the DataTable to mark the row 'modified'.

To commit row changes programmatically, call the form's Validate method. If your data source is a BindingSource, you can also callBindingSource.EndEdit.
see
IsCurrentRowDirty

Related

How can I track changes of a DataGrid WPF MVVM

I have a WPF/MVVM application where I want to track when the user changes some data in the datagrid. The DataGrid has a ICollectionView as source which is filled by a DataTable:
private ICollectionView _planningCollectionView;
public ICollectionView PlanningCollectionView
{
get => _planningCollectionView;
set
{
if (_planningCollectionView == value) return;
_planningCollectionView = value;
OnPropertyChanged();
}
}
...
//_planningView is my DataTable
public MainViewModel()
{
...
PlanningCollectionView = CollectionViewSource.GetDefaultView(_planningView);
...
}
The goal is, to make a little status bar where the user is able to see if there are unsaved changes.
On top of that, i want to verify, if the user didnt put the original data back. For example if a cell had the value "50" on the initial DataGrid load and the user changes it to "100" then the status should show "Unsaved changes". When the user changes it back to "50" the status should go back to "No unsaved changes."
My Approach:
I tried using the "CellEditEnding" and the "RowEditEnding" events to track when the user finished his input. This is followed by a InvokeCommandAction which calls a command defined in my MainViewModel. The Command just points to a method which only sets a DataTable with all the changes of my first DataTable
public void SetEdited()
{
DataTable planningViewChanges = _planningView.GetChanges();
}
The Outcome:
When I put a breakingpoint in my method I see that it gets called after i clicked besides the edited cell. But I think its suspicious that the cell didnt end the ending completely when my breakpoint stops.
And when I look in my method and inspect my DataTable object "planningViewChanges" it is null.
When I continue the application and the editing visually ends in the cell and I try to change another cell or I call my Command through a button and I inspect the DataTable "planningViewChanges" again it has one row.
So obviously, the commit of the data in the DataGrid to the source DataTable is working, but not right after the "CellEditEnding" or "RowEditEnding" event...
My Second Approach
I googled and browsed many other questions that could be similar to this issue. One approach was using the "LostFocus" even on the DataGrid, but this didnt fired after the commit too. When i tried to change the UpdateSourceTrigger option to "LostFocus"or "PropertyChanged" the commit didnt started before the event either...
In Addition
Another approach I've read was using a list of a custom class. But this isnt suitable for my problem because the DataGrid columns can vary with their number dynamically.
My Question
Does anybody know how to get an event that is fired after the editing of a cell and after the commit? Or is there a solution that is more fitted for the MVVM Pattern?
Many answers on similar questions didnt really had much in common and everybody had his/her own opinion on what is MVVM like and what is not..
I found another event that fires right after the commit. The event "CurrentCellChanged" works, but must be treated with caution. It fires everytime you change the current selected cell (as you can see on the name) so even when you start editing the cell. I made some changes in my called method so my whole logic doesnt fire everytime but only if i want to track changed cells(rows).

Checking datagridview checkbox from code doesn't check it on UI

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.

Programmatically setting a datagridview cell value, but dataAdapter.Update() does not update the DB [duplicate]

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

DataGridView row is being cleared at validation

I have a DataGridView which I need to run a CellValidating event to ensure that only valid values are selected from a ComboBox. This is needed as the ComboBox contains dummy rows used to display the category, with the fields the user can select listed underneath each category.
Whilst I have the validation code working fine, there is an unwelcome side-effect that all values are being wiped from the row being validated. I have stripped the code in the Event handler down to this, and the issue still occurs:
private void dgvInformation_CellValidating(object sender, DataGridViewCellValidatingEventArgs e)
{
DataGridView dgv = this.dgvInformation;
DataGridViewCell cell = dgv.Rows[e.RowIndex].Cells[e.ColumnIndex];
}
If I remove the
DataGridViewCell cell = dgv.Rows[e.RowIndex].Cells[e.ColumnIndex];
line then the issue does not occur.
The DGV is unbound which I believe is causing the issue. As a test I have made a simple form and populated the DGV values unbound, and each time the CellValidating event fires that row is wiped out, but when I create a List<> and use that as the DataSource the values are not wiped out. Could this be a bug with unbound DGVs?
Many thanks
I have problems with the sentense: "to ensure that only valid values are selected from a ComboBox." Using a combobox should actually prevent wrong values beeing tipped in controls like textbox, why dont you just show valid values in the combobox, or validate after all sellections have been made if you need to validate a combination of comboboxes sellection, therefor you may need somthing like a Submit button to run the validation routine.
If you still think you have to validate the combobox after each sellection then you ahve to run the validation somehow on a SelectionChanged event of the combobox.

How to refresh data driven combo box on a winform

I have a winform with a combo box thats filled from a query in the database. If I add a field to the database, the new field won't show up in the form until I close it and reopen it.
I was able to put in a MessageBox.Show() and once that popped up, I closed it, and saw the new data in the combo box.
EDIT:
Let me clarify a bit. I have a dropdown combo box, and that is populated by a table adapter. I just did the databinding with the GUI so I'm not sure how it works.
What I want is, I want the new data that I enter to be refreshed when I come back to it. I have a seperate window to manage the data, and then I close it I want the combo box to be updated with what I just saved.
Is this possible? I tried to do it on form load but that doesn't work either, I think because the form is already loaded.
The Refresh method is not for that. What you want to achieve is refreshing the data binding. This would be sth like this:
cb.DataBindings[0].ReadValue();
Another way is using a data source that supports change notification. Such a data source fires ListChanged event with appropriate parameters to trigger update of controls that are bound to it.
you have to fill your table adapter from dataset again by
youTableAdapter.Fill(yourdataSet.tablename);
then you have to reassign datasource
this.combobox.DataSource = this.yourBindingSource;
in the end you may refresh your combobox
combobox.Refresh();
It depends on what your datagrid is bound to. Repopulating the datasource (like a DataTable or a customized BindingList) should automatically repopulate the grid, assuming the data source's ListChanged event is properly fired.
If I understand this correctly, one thing you could do is use a ListChanged event like what was mentioned but it seems as if that didn't work.
TableAdapters aren't really a table in the standard sense but a temporary storage area.
Look in your Form1_Load function (or whatever you named your form, just using the default) and look for a tableadapter.fill method. this.comboboxTableAdapter.Fill(yourdataset name). This is what actually fills your dataset.
Create a function that fills those datasets (if you have more than one) and then call that function on a ListChanged event, or even on the Activate event of the form. This way when you go into that child form and change the data, when you come back to main form the data will be there.
I hope this helps, and good luck with your project.
Here is what worked for me. When I refreshed the secondary dataset (first line), the combo box didn't originally pick up the new values, so I reassigned the dataTable as the DataSource (third line).
boundDataSet = proxy.ReadResources();
DataGridViewComboBoxColumn nameColumn = dataGrid.Columns["Name"] as DataGridViewComboBoxColumn;
nameColumn.DataSource = boundDataSet.Table;
cmbguest.DataSource = null;
loadguestDetails();
make the dataset null and rebind the combobox then u can refresh the data driven combobox in winforms
Child Form inner;
private void UpdateAccount_FormClosed(object sender, FormClosedEventArgs e)
{
ParentForm parentForm= (ParentForm )Application.OpenForms["ParentFormName"];
parentForm.GetAccounts();
}
Parent Form inner;
public void GetAccounts()
{
AccountData lastSelectedItem = (AccountData)cbAccounts.SelectedItem;
cbAccounts.Items.Clear();
List<AccountData> accountDatas = AccountXML.ReadAccountXML();
if (accountDatas != null)
foreach (var item in accountDatas)
{
cbAccounts.Items.Add(item);
}
if(lastSelectedItem != null)
{
cbAccounts.SelectedText = lastSelectedItem.AccountName;
}
}

Categories