Windows Develop Bookmark and Share   
 index > Windows Forms General > Failure to detect changes when multiple controls are bound to a single dataview?
 

Failure to detect changes when multiple controls are bound to a single dataview?

I have a simple database for which I've written a vb.net interface. The interface consists of a DataGridView control and a RichTextBox, both bound to a single DataView. The setup is as follows:

A DataSet that is filled with data from a database file.
A DataView is then created with it's table set to the single table in the DataSet.
A DataGridView is created with it's datasource set to the DataView.
A RichTextBox is created and a databinding is added pointing to the DataView's last column of data.

I'm trying to have the last column of the DGV be the same as the RichTextBox--both allowing editing of the value in the last column of the dataset at any time. However, changes are not always being "detected" by the dataset and .HasChanges.

Clicking a SaveButton (effectively leaving the TextBox and checking the DS.HasChanges property) informs us that there have been no changes to the DS. If I change rows in the DGV, then the .HasChanges property seems to indicate that there are changes and all is well.

-Firstly, is there a way to avoid having to force the user the change rows in order to detect the changes?
-Second, is there a way to force the dataset to believe that it has changes, essentially a manner of manually setting the (ReadOnly) property of .haschanges to true?

smarr  Saturday, March 24, 2007 3:25 PM

Include the following statement in your SaveToDB_Click Sub, before checking for changes:

RTB.DataBindings("Text").BindingManagerBase.EndCurrentEdit()

Andrej

Andrej Tozon  Sunday, March 25, 2007 9:15 AM

Hi,

for the first question, try calling DataGridView's EndEdit() method to post changes, made in DGV, to underlying table.

Secondly, dataset itself has changes, if any table in it has at least one row, that's been changed. To have dataset report it has changes, you can set any of included row's RowState to modified:

myDataSet.Tables[0].Rows[0].SetModified();

However, I see no point in doing this...

Andrej

Andrej Tozon  Saturday, March 24, 2007 9:51 PM
Using EndEdit in the dgv does not work (or at least, have the desired effect).

Keep in mind that I am *not* editing the grid view cell--I am editing the *RichTextBox*. The RTB is not informing the dataset that it has changes.

I attempted your suggestion of SetModified, placing it in the RTB's TextChanged event so that upon entering data into the RTB, the dataset would consider itself changed. However, this seems to result in errors of "SetAdded and SetModified can only be called on DataRows with Unchanged DataRowState." This does seem a bit contradictory, since you would think that the point of "SetModified" would be to do just that.
smarr  Saturday, March 24, 2007 10:27 PM

Have you tried setting your RTB-DataSource binding's DataSourceUpdateMode property to OnPropertyChanged (the default is OnValidation)?

Andrej

Andrej Tozon  Saturday, March 24, 2007 10:53 PM
I just tried that. No difference in behavior.

When should the RTB be automatically updating it's datasource?
smarr  Sunday, March 25, 2007 12:58 AM

Whenever its contents change or on validation, depends on binding setting. Can you post your code you're using for binding RTB to data source?

Andrej

Andrej Tozon  Sunday, March 25, 2007 1:16 AM
I've thinned the code out to a minimum single form example for demonstration:
The following assumptions are made:
An access database file of a single table, a few columns. The last column is named "Description"
A DataGridView named DGV.
A RichTextBox named RTB.
A Button named SaveToDB


Public Class Form1
Dim DS As New DataSet

Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
Dim MyDBFile As String = Environment.CurrentDirectory & "\JobDatabase.mdb" 'The database file
Dim TableName As String = "ShotsTable" 'The only table in the Database
Dim DBConnection As New OleDb.OleDbConnection("Provider=Microsoft.Jet.OLEDB.4.0;" & "Data Source=" & MyDBFile)
Dim SQLCommand As String = "Select * from " & TableName
Dim DA As New OleDb.OleDbDataAdapter(SQLCommand, DBConnection) 'our data adapter
Dim DV As New DataView 'A dataview, for possible filtering (currently no filtering being performed).

'Fill the dataset with the data
DA.Fill(DS, TableName)

'Set the dataset and bind the grid control
DV.Table = DS.Tables(TableName)
DGV.DataSource = DV

'Bind the RichTextBox to the dataview
RTB.DataBindings.Add(New Binding("Text", DV, "Description")) ' Description is the name of the last column in the database table
RTB.DataBindings.Item("Text").DataSourceUpdateMode = DataSourceUpdateMode.OnPropertyChanged
End Sub

Private Sub SaveToDB_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles SaveToDB.Click
Console.WriteLine(DS.HasChanges)
End Sub
End Class



Test Steps:
1-Select a row in the DGV. The RTB will display the data in the last column named Description).
2-Change the data in the RTB.
3-Click Save Button. The dataset.haschanges does not indicate any changes to the database.
4-Change rows in the DGV (the DGV is then updated with the RTB data.
5-Click Save Button. The dataset.haschanges now indicates the changes.
smarr  Sunday, March 25, 2007 2:00 AM

Include the following statement in your SaveToDB_Click Sub, before checking for changes:

RTB.DataBindings("Text").BindingManagerBase.EndCurrentEdit()

Andrej

Andrej Tozon  Sunday, March 25, 2007 9:15 AM
That appears to solve the problem. I will probably place this into the .LostFocus event of the RichTextBox.

Thank you very much for your help.

So am I to understand that the DataGridView does *not* require an .EndCurrentEdit(), but that is accomplished when you change rows; and that other controls such as RichTextBox require a manual .EndCurrentEdit to be performed before the bound dataset is able to update?

I must admit that this seems contradictory to the whole concept of binding--some controls automatically update the dataset, some don't.
smarr  Sunday, March 25, 2007 3:22 PM

You need to somehow forcethe value, you're currently editing,to update the datasource. EndCurrentEdit() pushes that changes to the underlying datasource manualy, as does moving to another record (channging DGV's current rowor otherwise). If you would be entering data directly into DGV, you should call its EndCurrentEdit *OR* change current row to push those changes to the datasource.

There's also the BindingSource component, which encapsulates some of this behaviour for you, making things a little easier. Normally, you'd put one on your form and bind it to the datasource (dataset, datatable) and then bind DGV and other simple controls (textbox, RTB) to it. BindingSource has methods like EndEdit, MovePrevious, MoveFirst, etc. to manipulate the datasource and its binding without having to deal with bound controls directly.

Andrej

Andrej Tozon  Sunday, March 25, 2007 4:16 PM

Personally, I think it's a bug with databinding, especially if you use the new DataSourceUpdateMode.OnPropertyChanged and it still doesn't work. I'm never certain where it's going to be ok and where it's not (it doesn't seem to have anything to do with changing rows in a DataGridView ... I haven't used them much in the past and my data sometimes binds just fine), so I've gotten around it by other means.

I used to encounter problems with the .EndCurrentEdit() solution not being reliable (granted, this was in 1.1, perhaps it's better in 2.0 and maybeI should re-think how I do this inmy homegrown framework), but here's how I currently handle this:

Data in a DataRow has several different versions. First, there's the original version. Then, when it's being edited, it becomes a Proposed version and once it's done being edited it becomes the Current version. Sometimes when editing, the row is left in the Proposed state and needs to be ended.

Here's a method I *always* call before I attempt to save data:

protected virtual void CommitProposedChanges(DataSet ds)

{

if (ds == null)

return;

for (int nTable = 0; nTable < ds.Tables.Count; nTable++)

{

for (int nRow = 0; nRow < ds.Tables[nTable].Rows.Count; nRow++)

{

if (ds.Tables[nTable].Rows[nRow].HasVersion(DataRowVersion.Proposed))

{

ds.Tables[nTable].Rows[nRow].EndEdit();

}

}

}

}

BonnieB  Sunday, March 25, 2007 4:43 PM

You can use google to search for other answers

Custom Search

More Threads

• How do you initiate textbox validation from code?
• How To force all MDI parent and child forms to close on MDIParent Exit or MDIParent Close
• How to make button to create new folder
• how to make a control display things outside its boundaries
• how to set length on a treeview node name?
• A threading problem
• Office Vba versus .net windows forms
• How to make one property update another in a propetygrid
• SplitterControl inside a TabControl Resize issue
• How to: Line Graph using Crystal Reports .NET