I promise I've looked at the bazillion posts claiming this same issue, so please forgive me that I am still stumped.
I have a VS2008 smart device project containing a strongly-typed dataset. The user is allowed to input values and save them, each time creating a new record in the dataset. Should the user wish to edit values, a modal child form is displayed with several combo boxes containing the possible values for acceptable input to edit the row. Each combo box is bound in the constructor for the child form in the fashion below:
With cmbSize
.DataSource = frmMain.dstConfig.Sizes
.DisplayMember = "Display"
.ValueMember = "Value"
.DataBindings.Add("SelectedValue", trowNewRow, "SIZE", True, DataSourceUpdateMode.OnPropertyChanged)
End With
dstConfig is a dataset of tables containing the constraints. Binding to the source's display and value members works fine.
trowNewRow is a reference to the row in the dataset conveyed from the main form when the user initiates an edit procedure, to the child form by passing the row as a parameter "ByRef". The strong-typing is preserved. It occurred to me there may be some sort of a disconnection that I am not aware of when this occurs. I pass the reference by performing a "Select" procedure on the dataset and filtering by a unique ID field corresponding to the row that is to be edited. The result is a single-item array of strongly-typed rows of the same schema as the dataset's from which I pass the first (and only) item as my object.
After the user submits the changes by clicking an OK button, a procedure is triggered to evaluate whether or not a change was actually performed. The best way I thought to do this was by checking the RowState of trowNewRow. The RowState, however, remains "Added" regardless of the changes. Manually checking the values of the row indicates the changes have indeed been recorded. I've verified the AcceptChanges procedure of trowNewRow is not explicitly being called by any of my code.
I have tried the following:
1.) Calling the EndEdit procedure of trowNewRow
2.) Manually performing a WriteValue on the combo boxes
3.) Calling the EndCurrentEdit procedure on the combo boxes' BindingManagerBase objects
4.)Every combination of the above
Thank you in advance for any ideas or solutions.
A DataRow contains two sets of data - original and current - and the RowState reflects the relationship between them. If there is no original data but there is current data then the RowState is Added. As #Plutonix says, no amount of editing the current data is going to add original data so the RowState remains Added even if you make further changes. If there is no current data but there is original data then the RowState is Deleted. If the current data matches the original data then the RowState is Unchanged, otherwise it's Modified.
When you call Update on a data adapter or table adapter, the InsertCommand is executed for each Added row, the UpdateCommand is executed for each Modified row and the DeleteCommand is executed for each Deleted row. After a successful save, the adapter implicitly calls AcceptChanges. That will remove all Deleted rows from the DataTable and copy the current values in Added and Modified rows over the original values, changing the RowState to Unchanged.
So, the RowState is for tracking changes since the last save to the database. You can't use it to determine whether the user has made any changes in the UI unless you are saving those changes to the database after every edit. If you want finer-grained change-tracking then you have to implement it yourself. Personally, I don't bind in those cases but wait until the user clicks OK to push the data into the DataRow. That also allows you to cancel the latest edit without losing prior edits.
Related
I want to know if the rows have changed values in my Datatable, how to check that?
I got a Datatable has data and I make some changes on this data by grid view so Can I check which rows have changed.
Each DataRow has a RowState. When you populate a DataTable by calling Fill on a data adapter, all rows start out Unchanged. When you add a row the value will be Added, when you edit an existing row the value will be Modified and when you delete an existing row the value will be Deleted.
Note that, when you call Fill on a data adapter, the rows will be initially added with a RowState of Added but then AcceptChanges is called and they will all be set to Unchanged. If you set AcceptChangesDuringFill to False first, they will all remain Added. This is useful when, for instance, you want to retrieve from one database and then insert into another.
When you call Update on a data adapter to save changes, AcceptChanges is called afterwards and all Added and Modified rows become Unchanged and all Deleted rows are removed. If you set AcceptChangesDuringUpdate to False first, they will all remain as they were. This is useful when, for instance, you want to save changes to multiple tables from one DataTable. You would then call AcceptChanges manually if you wanted to keep using the DataTable.
Note that each DataRow contains two versions of its data: original and current. When you get data from a field in a DataRow you will get the current version by default, but you can specify which version you want. If the RowState is Added then the original version is empty and, if the the RowState is Deleted then the current version is empty. When AcceptChanges is called, the current version is copied over the original version in rows that were Added or Modified.
You can also call GetChanges on a DataTable and that will either return Nothing or a new DataTable containing just the changed rows. You can also specify one or more DataRowState values and get just changes of that type.
I have a dataview grid bound to a datasource at run time. The datasource is filled from an access database via a DataAdapter. The data fills and displays correctly, and updates to existing rows seem to work OK but I have two problems:
When I type something in a new row and then press return or switch to a different row, I want the DataAdapter to add that row then and there to the database so I can retrieve the Autonumber index of the new record from Access and use that to add an associated record in a different table (Entries, a many to many linking table). This isn't happening. In the RowLeave event I have adapter.Update(dsSentences) and then I check for the new row, but the RowCount doesn't reflect its presence even though the newly added data is visible in the grid, and the adapter.Update doesn't seem to have triggered the Insert query that I specified in the DataAdapter. So nothing is added.
(edit: OK, so the new row has not yet been added when this event is fired. Which event should I then use to commit the data and retrieve the Autonumber primary key for my new record? I've tried UserAddedRow but that one fires before you've entered any data into the new row.)
THe second problem is that I need to update the data independently and then have the grid reflect those changes. How do I do that? Is there some call that will force the grid to get the updated data from the DataAdapter via the Dataset? Any help would be much appreciated. I'm almost ready to dtop the whole idea of binding data and do it all through code, Data binfing is supposed to save time but I'm finding it labyrinthine and unpredictable.
FWIW here's the query I'm using to fill the grid:
PARAMETERS nIdCollection Long;
SELECT tblSentences.IdSentence, tblSentences.SentenceText, tblSentences.SentenceParsed, Not IsNull([tblSentences]![SentenceParsed]) AS HasParsed, Entries.IdEntry
FROM tblSentences INNER JOIN Entries ON tblSentences.IdSentence = Entries.IdSentence
WHERE (((Entries.IdCollection)=[nIdCollection]))
ORDER BY Entries.SortValue;
As you can see, it requires a record in Entries. After I've entered a new record in tblSentences, before there are any entries the IdEntry will be null assuming it shows up at all. That's why I need to intercept directly after the Insert, add the record to Entries and requery to keep everything in order. You could do it all in an SQL stored procedure but I have to use Access.
Edit: After a lot of googling I've come to the conclusion that what I'm trying to do = add a record to a table through an additional INSERT query apart from the one handled by the DataAdapter, every time a new row is added - simply can't be done if you are using data binding. I am going to have to delete all my code and start from scratch populating the grid through code (unbound). I think it's the only way to do what I want. I will leave this here as a warning to anyone else not to make my mistake of trying to use Data binding when your data is coming from more than one table. Bad mistake.
I have a datatable called DTAllCustomers which I update on a record by record basis, however I now need to change one field in nearly all records of the datatable. I want to quickly update the database with the updated info in the datatable.
I have tried:
Dim objCmdBuilder As New SQLiteCommandBuilder(AllCustomersAdapter)
DTAllCustomers.AcceptChanges()
AllCustomersAdapter.UpdateCommand = objCmdBuilder.GetUpdateCommand()
AllCustomersAdapter.Update(DTAllCustomers)
Each command is processed, however the underlying database is not altered?
I have searched for some time but can't find what I am doing wrong.
Any help is greatly appreciated
Brad
The issue is the fact that you are calling AcceptChanges. Once you have accepted the changes, there are no more changes, so what is Update going to save? That Update method already calls AcceptChanges implicitly once it has saved those changes to the database. It's very rare that you will have to call AcceptChanges explicitly. Here are some scenarios where you might manipulate the accepting of changes.
When you call Fill on a data adapter, the method will internally add a bunch of DataRows to the DataTable. In that case, the RowState of each will be Added, just like when you add a row yourself. That's generally not what you want when retrieving data though, so Fill implicitly calls AcceptChanges by default. Let's say that you want to retrieve data from one database and insert it into another. In that case, you do want all the rows to be Added. To do that, you set the AcceptChangesDuringFill property of the data adapter to False.
You have a single DataTable containing changes that you want to save to two different database tables. In that case, you would need two different data adapters. You would need the first Updte call to not accept the changes so they are still available to the second data adapter. In that case, you set the AcceptChangesDuringUpdate property of the first data adapter to False and the changes remain after calling Update. You might allow AcceptChanges to be called implicitly by the second data adapter or you might set AcceptChangesDuringUpdate on it too and then call AcceptChanges explicitly.
You have two DataTables in a parent/child relationship and you want to save changes from both to the database. In that case, you need to save child deletes before parent deletes and save parent inserts before child inserts. To do that, you would call GetChanges on the DataTables to get subsets to save in the appropriate order. Because you're not passing the original DataTables to Update, their changes won't be accepted implicitly, so you need to call AcceptChanges explicitly on each DataTable after a successful save.
I have an Access Form which pulls records from a table. The user will interact with the records, sometimes in chronological order, and sometimes by jumping direct to a specific record.
The table uses a field called ID as the primary key which auto-numbers when a record is created. This is what the team will use to jump directly to a record.
I need to create a textbox where the ID number can be inputted and then a button to carry out some code to jump to the specified record if it exists.
I have attempted to put bits of code together what I have found online but so far have had no such luck in making anything work.
I would be very grateful if anybody could advise me and provide a code sample to make this work! :)
Make use of the RecordSetClone and Bookmark properties.
Use the RecordSetClone property to obtain a copy of the form's recordset that you can manipulate without affecting the "live" recordset used by the form.
Locate the appropriate record in the cloned recordset (perhaps using the FindFirst method), and obtain the Bookmark which uniquely identifies such record in the recordset.
Set the Bookmark property of the form to the Bookmark value obtained from the cloned recordset in order to change the current record displayed by the form to the record found.
I have an Infragistics Ultragrid on a winform. How can I tell if a row is added or just modified? The DataChanged property will just tell me if Data has changed, not if it was added (so I can put the row data in an SQL Insert statment) or if it was modified (so I can put it in an UPDATE sql statement.)
For Each row As UltraGridRow In GroupMetadataGrid.Rows
If row.DataChanged Then
Debug.WriteLine("Saving Changed Row")
End If
Next
Thanks in advance.
UltraGrid does not track if the row has changed or was added. Look at the answer from Mike Saltzman in this thread in their forum.
This is his answer:
The grid does not track this. If you edit a row in the grid and move to another row, all changes in the previous row are committed to the data source.
The grid doesn't deal with the data base directly, it only deals with it's local data source. So if you are looking to track changes in the data source that need to be written to the database, then the data source needs to take care of that. The DataSet and DataTable class have built-in support for tracking pending changes.
The only thing you might have to be concerned about with the grid is that the current ActiveRow in the grid may have pending changes that need to be written to it's DataSource. For this you can use DataChanged. The grid will automatically commit changes when it loses focus, but if you need to manually force the changes to be committed, you can use the grid.UpdateData method, or the Update method on any individual row.