.NET Forum / Windows Forms / WinForm Data Binding / July 2004
Error recovery durring AddNew
|
|
Thread rating:  |
Gary Shell - 14 Jul 2004 16:23 GMT I have a simple data form that can edit and add records to a table. In my "OK Button" code I EndCurrentEdit and then perform a UPDATE method on the data adapter. It works just fine adding and editing data.
One field on the table is a foreign key and I have referential integrity enforced on the server. When adding a new record I catch the exception raised by the Update method if the user supplies invalid data and display an error message. I then allow the user to fix the problem and hit the OK button again. The problem is when I get to the Update method again the underlying row in the dataset seems to be no longer bound to my controls. That is, if I go in and look at the contents of the item collection for the row being added the field that triggered the exception is still null. It does not have the value in the control that the user just corrected.
Is there something I need to tell the dataset or more likely the data adapter that we are "retrying" the update?
Sign me somewhat confused, but impressed, by this .Net databinding.
Gary
a - 14 Jul 2004 18:11 GMT Post some code for this. I am wondering a few things: are you binding to datasets? Do you have the relation in the dataset? That would let you catch the violation prior to going to the server in the call to EndCurrentEdit.
Kevin
> I have a simple data form that can edit and add records to a table. In my > "OK Button" code I EndCurrentEdit and then perform a UPDATE method on the [quoted text clipped - 16 lines] > > Gary Gary Shell - 14 Jul 2004 18:22 GMT I am bound to a dataset. There is no relation in the dataset. The EndCurrentEdit does not make a trip to the server. (only an UPDATE method would do that.) The exception is actually occurring in the EndCurrentEdit because there is a No Nulls constraint on the column in question. I've dug into this a bit further and when the EndCurrentEdit is executed if the exception occurs ALL of the data in the items collection for the new row is set to "". I don't understand why that should be.
Gary
> Post some code for this. I am wondering a few things: are you binding to > datasets? Do you have the relation in the dataset? That would let you [quoted text clipped - 25 lines] > > > > Gary a - 14 Jul 2004 19:32 GMT Sorry, I thought you were gettin ghte error on the Update call, not the EndCurrentEdit.. Didn't know you had the constraint in the dataset.
Why not perform the check in the GUI before the call to EndCurrentEdit?
Kevin
> I am bound to a dataset. There is no relation in the dataset. The > EndCurrentEdit does not make a trip to the server. (only an UPDATE method [quoted text clipped - 40 lines] > > > > > > Gary Gary Shell - 14 Jul 2004 20:21 GMT Yeah, I was a bit obtuse in my initial description.
You are correct I could add code to check for the null first. But I am trying to figure out why the EndCurrentEdit clears the dataset's item collection when an exception occurs. I want to know what to expect when some OTHER exception occurs, not just the one for this constraint.
Gary
> Sorry, I thought you were gettin ghte error on the Update call, not the > EndCurrentEdit.. Didn't know you had the constraint in the dataset. [quoted text clipped - 56 lines] > > > > > > > > Gary a - 15 Jul 2004 17:00 GMT > You are correct I could add code to check for the null first. But I am > trying to figure out why the EndCurrentEdit clears the dataset's item [quoted text clipped - 70 lines] > > > > > > > > > > Gary Gary Shell - 15 Jul 2004 22:03 GMT First let me say I'm glad we are still talking. I was afraid I might have pissed you off in our exchange about crossposting.
The GUI is pretty simple. I will distill it further though to make this brief. I have a text field called MetricName bound to txtMetricName. I have a MetricClass field (with a no null constraint) bound to a combo box's SelectedValue property. The list portion of the combo is bound to a table called MetricClasses. There are a few other fields including a couple of booleans.
If I add a new record all fields on the form are blanked, as expected. I then type a MetricName and NOTHING else, then hit my update code, the EndCurrentEdit method throws an exception about the null state of MetricClass, again as expected.
I have a watch variable set to me.dsMetric.ItemArray and just before the EndCurrentEdit I can see the text I typed into the txtMetricName textbox in the correct ItemArray element. (I can also see the contents of the other textboxes and checkboxes as well.) But as soon as the EndCurrentEdit throws the exception, the entire ItemArray is reset. By that I mean, all Items are cleared to a valid state for a new row, i.e. all non-nullable fields are set to "", all nullable fields are set to null and all fields with declared default values have their correct defaults (only my booleans have default declared some true some false). These same values from this ItemArray are also populated back to the bound controls, wiping out whatever I'd typed into the various text boxes.
What happens to your ItemArray after such an exception. What happens to your form controls? I'm desperately trying to figure out a way to preserve their state so I can retry the EndCurrentEdit. That brings up a point, after your exception is thrown, and you click save a second time exactly what does your code do? Does it do another EndCurrrentEdit and if that works you use an UPDATE method on the dataadapter?
Hope this is a bit clearer.
And thanks for sticking with me on this!!!!!!
Try Me.cm.EndCurrentEdit() Me.lblInfo.Text = "Record Successfully Saved" booDirty = FalseCatch ex As Exception Me.lblInfo.Text = ex.MessageEnd Try This works for me. I have some columns that do not allow Nulls. When I click save, if there is an error (a no-null is still DBNull.Value) it shows it to the user. They can then fix it and click save and all is well.Explain your GUI to me a little better. Kevin "Gary Shell" <gshell@fuse.net> wrote in message news:uN6ODfdaEHA.596@TK2MSFTNGP11.phx.gbl...> Yeah, I was a bit obtuse in my initial description. > > You are correct I could add code to check for the null first. But I am > trying to figure out why the EndCurrentEdit clears the dataset's item > collection when an exception occurs. I want to know what to expect when > some OTHER exception occurs, not just the one for this constraint. > > Gary > > > "a" <a@a.com> wrote in message > news:%231%23ozDdaEHA.556@tk2msftngp13.phx.gbl... > > Sorry, I thought you were gettin ghte error on the Update call, not the > > EndCurrentEdit.. Didn't know you had the constraint in the dataset. > > > > Why not perform the check in the GUI before the call to EndCurrentEdit? > > > > Kevin > > > > "Gary Shell" <gshell@fuse.net> wrote in message > > news:%231gacccaEHA.1248@TK2MSFTNGP11.phx.gbl... > > > I am bound to a dataset. There is no relation in the dataset. The > > > EndCurrentEdit does not make a trip to the server. (only an UPDATE > method > > > would do that.) The exception is actually occurring in the > EndCurrentEdit > > > because there is a No Nulls constraint on the column in question. I've > > dug > > > into this a bit further and when the EndCurrentEdit is executed if the > > > exception occurs ALL of the data in the items collection for the new row > > is > > > set to "". I don't understand why that should be. > > > > > > Gary > > > > > > "a" <a@a.com> wrote in message > > > news:%23FF0gWcaEHA.3480@TK2MSFTNGP11.phx.gbl... > > > > Post some code for this. I am wondering a few things: are you > binding > > to > > > > datasets? Do you have the relation in the dataset? That would let > you > > > > catch the violation prior to going to the server > > > > in the call to EndCurrentEdit. > > > > > > > > Kevin > > > > > > > > > > > > > > > > > > > > "Gary Shell" <gshell@fuse.net> wrote in message > > > > news:OlCz$ZbaEHA.2520@TK2MSFTNGP12.phx.gbl... > > > > > I have a simple data form that can edit and add records to a table. > > In > > > my > > > > > "OK Button" code I EndCurrentEdit and then perform a UPDATE method > on > > > the > > > > > data adapter. It works just fine adding and editing data. > > > > > > > > > > One field on the table is a foreign key and I have referential > > integrity > > > > > enforced on the server. When adding a new record I catch the > > exception > > > > > raised by the Update method if the user supplies invalid data and > > > display > > > > an > > > > > error message. I then allow the user to fix the problem and hit the > > OK > > > > > button again. The problem is when I get to the Update method again > > the > > > > > underlying row in the dataset seems to be no longer bound to my > > > controls. > > > > > That is, if I go in and look at the contents of the item collection > > for > > > > the > > > > > row being added the field that triggered the exception is still > null. > > > It > > > > > does not have the value in the control that the user just corrected. > > > > > > > > > > Is there something I need to tell the dataset or more likely the > data > > > > > adapter that we are "retrying" the update? > > > > > > > > > > Sign me somewhat confused, but impressed, by this .Net databinding. > > > > > > > > > > Gary
Gary Shell - 15 Jul 2004 23:06 GMT Another quick thought. I wonder if this has anything to do with the fact that my AddNew procedure uses the technique described below. I don' t add the new row to the bindingcontext. I add it directly to the dataset. This does seem to work OK when no exceptions are thrown. But I wonder if when the exception IS thrown if the fact that the BindingContext didn't know I anything about the added row is causing the weirdness I see.
Here's the pertinent text from a file found on Microsoft's site:
However, these auto-generated programs have a major limitation. If the data being accessed has any fields that cannot be null (because the database schema does not allow nulls), then the program generated by the wizard will be unable to add records. When the Add button is pressed, an error message will indicate the first field in the record that is not allowed to be null. (If you don't have the latest service packs applied, you may not see the error message but the program will still refuse to add a record.)
The problem is that the Data Form Wizard uses the BindingContext object to add a row to the bound DataTable. Here is the code that fails in the btnAdd_Click event routine:
Me.BindingContext(objProducts, "Products").AddNew() The solution is to bypass the BindingContext object for new rows. Below is the typical code to add a new row. This code should replace the single line of code above:
Dim dr As DataRow dr = objProducts.Tables("Products").NewRow dr.Item("ProductName") = "" dr.Item("Discontinued") = False ' Set any other fields that cannot null to default values. objProducts.Tables("Products").Rows.Add(dr) First let me say I'm glad we are still talking. I was afraid I might have pissed you off in our exchange about crossposting.
The GUI is pretty simple. I will distill it further though to make this brief. I have a text field called MetricName bound to txtMetricName. I have a MetricClass field (with a no null constraint) bound to a combo box's SelectedValue property. The list portion of the combo is bound to a table called MetricClasses. There are a few other fields including a couple of booleans.
If I add a new record all fields on the form are blanked, as expected. I then type a MetricName and NOTHING else, then hit my update code, the EndCurrentEdit method throws an exception about the null state of MetricClass, again as expected.
I have a watch variable set to me.dsMetric.ItemArray and just before the EndCurrentEdit I can see the text I typed into the txtMetricName textbox in the correct ItemArray element. (I can also see the contents of the other textboxes and checkboxes as well.) But as soon as the EndCurrentEdit throws the exception, the entire ItemArray is reset. By that I mean, all Items are cleared to a valid state for a new row, i.e. all non-nullable fields are set to "", all nullable fields are set to null and all fields with declared default values have their correct defaults (only my booleans have default declared some true some false). These same values from this ItemArray are also populated back to the bound controls, wiping out whatever I'd typed into the various text boxes.
What happens to your ItemArray after such an exception. What happens to your form controls? I'm desperately trying to figure out a way to preserve their state so I can retry the EndCurrentEdit. That brings up a point, after your exception is thrown, and you click save a second time exactly what does your code do? Does it do another EndCurrrentEdit and if that works you use an UPDATE method on the dataadapter?
Hope this is a bit clearer.
And thanks for sticking with me on this!!!!!!
Gary "a" <a@a.com> wrote in message news:%23IWxKToaEHA.3508@TK2MSFTNGP09.phx.gbl... Try Me.cm.EndCurrentEdit() Me.lblInfo.Text = "Record Successfully Saved" booDirty = FalseCatch ex As Exception Me.lblInfo.Text = ex.MessageEnd Try This works for me. I have some columns that do not allow Nulls. When I click save, if there is an error (a no-null is still DBNull.Value) it shows it to the user. They can then fix it and click save and all is well.Explain your GUI to me a little better. Kevin "Gary Shell" <gshell@fuse.net> wrote in message news:uN6ODfdaEHA.596@TK2MSFTNGP11.phx.gbl...> Yeah, I was a bit obtuse in my initial description. > > You are correct I could add code to check for the null first. But I am > trying to figure out why the EndCurrentEdit clears the dataset's item > collection when an exception occurs. I want to know what to expect when > some OTHER exception occurs, not just the one for this constraint. > > Gary > > > "a" <a@a.com> wrote in message > news:%231%23ozDdaEHA.556@tk2msftngp13.phx.gbl... > > Sorry, I thought you were gettin ghte error on the Update call, not the > > EndCurrentEdit.. Didn't know you had the constraint in the dataset. > > > > Why not perform the check in the GUI before the call to EndCurrentEdit? > > > > Kevin > > > > "Gary Shell" <gshell@fuse.net> wrote in message > > news:%231gacccaEHA.1248@TK2MSFTNGP11.phx.gbl... > > > I am bound to a dataset. There is no relation in the dataset. The > > > EndCurrentEdit does not make a trip to the server. (only an UPDATE > method > > > would do that.) The exception is actually occurring in the > EndCurrentEdit > > > because there is a No Nulls constraint on the column in question. I've > > dug > > > into this a bit further and when the EndCurrentEdit is executed if the > > > exception occurs ALL of the data in the items collection for the new row > > is > > > set to "". I don't understand why that should be. > > > > > > Gary > > > > > > "a" <a@a.com> wrote in message > > > news:%23FF0gWcaEHA.3480@TK2MSFTNGP11.phx.gbl... > > > > Post some code for this. I am wondering a few things: are you > binding > > to > > > > datasets? Do you have the relation in the dataset? That would let > you > > > > catch the violation prior to going to the server > > > > in the call to EndCurrentEdit. > > > > > > > > Kevin > > > > > > > > > > > > > > > > > > > > "Gary Shell" <gshell@fuse.net> wrote in message > > > > news:OlCz$ZbaEHA.2520@TK2MSFTNGP12.phx.gbl... > > > > > I have a simple data form that can edit and add records to a table. > > In > > > my > > > > > "OK Button" code I EndCurrentEdit and then perform a UPDATE method > on > > > the > > > > > data adapter. It works just fine adding and editing data. > > > > > > > > > > One field on the table is a foreign key and I have referential > > integrity > > > > > enforced on the server. When adding a new record I catch the > > exception > > > > > raised by the Update method if the user supplies invalid data and > > > display > > > > an > > > > > error message. I then allow the user to fix the problem and hit the > > OK > > > > > button again. The problem is when I get to the Update method again > > the > > > > > underlying row in the dataset seems to be no longer bound to my > > > controls. > > > > > That is, if I go in and look at the contents of the item collection > > for > > > > the > > > > > row being added the field that triggered the exception is still > null. > > > It > > > > > does not have the value in the control that the user just corrected. > > > > > > > > > > Is there something I need to tell the dataset or more likely the > data > > > > > adapter that we are "retrying" the update? > > > > > > > > > > Sign me somewhat confused, but impressed, by this .Net databinding. > > > > > > > > > > Gary
a - 15 Jul 2004 23:44 GMT Yeah . . . . I neglected to see you are not using a CurrencyManager.
That will solve all your problems (and may create new ones.) I have a hard time getting the MS Solution to work in my live Apps. In their solution, you would need to set your Non-Nullable field to something (0 or ""), otherwise you get the error on .Rows.Add(dr)
The following does not work for all controls (checkbox deos not work), but it does work for TextBox and ComboBox:
Public WithEvents cm As CurrencyManager
Private Sub frmTaskEntry_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load Me.cboType.DataBindings.Add(New System.Windows.Forms.Binding("SelectedValue", TaskData.Data.dsTaskData1, "tblTask.TypeRef")) Me.cboType.DataSource = TaskData.Data.dsTaskData1 Me.cboType.DisplayMember = "tblType.Name" Me.cboType.ValueMember = "tblType.ID"
cm = Me.BindingContext(TaskData.Data.dsTaskData1, "tblTask")
cm.AddNew()
' USED TO PROGRAMMATICALLY SET A VALUE IN THE NEW ROW drv = CType(cm.Current, DataRowView) 'Set ProjectRef drv.Item("ProjectRef") = intProjectRef cm.Refresh()
End Sub
Private Sub butSave_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles butSave.Click Try Me.cm.EndCurrentEdit() Me.lblInfo.Text = "Record Successfully Saved" booDirty = False Catch ex As Exception Me.lblInfo.Text = ex.Message End Try End Sub
'--------
The drv = CType(cm.Current, DataRowView) 'Set ProjectRef drv.Item("ProjectRef") = intProjectRef
part needs to be done on columns bound to CheckBoxes, NumericUpDowns, DateTimePickers . . .
Check out the CurrencyManager, which is the result of me.BindingContext when the binding is complex.
Kevin
Another quick thought. I wonder if this has anything to do with the fact that my AddNew procedure uses the technique described below. I don' t add the new row to the bindingcontext. I add it directly to the dataset. This does seem to work OK when no exceptions are thrown. But I wonder if when the exception IS thrown if the fact that the BindingContext didn't know I anything about the added row is causing the weirdness I see.
Here's the pertinent text from a file found on Microsoft's site:
However, these auto-generated programs have a major limitation. If the data being accessed has any fields that cannot be null (because the database schema does not allow nulls), then the program generated by the wizard will be unable to add records. When the Add button is pressed, an error message will indicate the first field in the record that is not allowed to be null. (If you don't have the latest service packs applied, you may not see the error message but the program will still refuse to add a record.)
The problem is that the Data Form Wizard uses the BindingContext object to add a row to the bound DataTable. Here is the code that fails in the btnAdd_Click event routine:
Me.BindingContext(objProducts, "Products").AddNew() The solution is to bypass the BindingContext object for new rows. Below is the typical code to add a new row. This code should replace the single line of code above:
Dim dr As DataRow dr = objProducts.Tables("Products").NewRow dr.Item("ProductName") = "" dr.Item("Discontinued") = False ' Set any other fields that cannot null to default values. objProducts.Tables("Products").Rows.Add(dr) "Gary Shell" <gshell@fuse.net> wrote in message news:ubx868qaEHA.1764@TK2MSFTNGP10.phx.gbl... First let me say I'm glad we are still talking. I was afraid I might have pissed you off in our exchange about crossposting.
The GUI is pretty simple. I will distill it further though to make this brief. I have a text field called MetricName bound to txtMetricName. I have a MetricClass field (with a no null constraint) bound to a combo box's SelectedValue property. The list portion of the combo is bound to a table called MetricClasses. There are a few other fields including a couple of booleans.
If I add a new record all fields on the form are blanked, as expected. I then type a MetricName and NOTHING else, then hit my update code, the EndCurrentEdit method throws an exception about the null state of MetricClass, again as expected.
I have a watch variable set to me.dsMetric.ItemArray and just before the EndCurrentEdit I can see the text I typed into the txtMetricName textbox in the correct ItemArray element. (I can also see the contents of the other textboxes and checkboxes as well.) But as soon as the EndCurrentEdit throws the exception, the entire ItemArray is reset. By that I mean, all Items are cleared to a valid state for a new row, i.e. all non-nullable fields are set to "", all nullable fields are set to null and all fields with declared default values have their correct defaults (only my booleans have default declared some true some false). These same values from this ItemArray are also populated back to the bound controls, wiping out whatever I'd typed into the various text boxes.
What happens to your ItemArray after such an exception. What happens to your form controls? I'm desperately trying to figure out a way to preserve their state so I can retry the EndCurrentEdit. That brings up a point, after your exception is thrown, and you click save a second time exactly what does your code do? Does it do another EndCurrrentEdit and if that works you use an UPDATE method on the dataadapter?
Hope this is a bit clearer.
And thanks for sticking with me on this!!!!!!
Gary "a" <a@a.com> wrote in message news:%23IWxKToaEHA.3508@TK2MSFTNGP09.phx.gbl... Try Me.cm.EndCurrentEdit() Me.lblInfo.Text = "Record Successfully Saved" booDirty = FalseCatch ex As Exception Me.lblInfo.Text = ex.MessageEnd Try This works for me. I have some columns that do not allow Nulls. When I click save, if there is an error (a no-null is still DBNull.Value) it shows it to the user. They can then fix it and click save and all is well.Explain your GUI to me a little better. Kevin "Gary Shell" <gshell@fuse.net> wrote in message news:uN6ODfdaEHA.596@TK2MSFTNGP11.phx.gbl...> Yeah, I was a bit obtuse in my initial description. > > You are correct I could add code to check for the null first. But I am > trying to figure out why the EndCurrentEdit clears the dataset's item > collection when an exception occurs. I want to know what to expect when > some OTHER exception occurs, not just the one for this constraint. > > Gary > > > "a" <a@a.com> wrote in message > news:%231%23ozDdaEHA.556@tk2msftngp13.phx.gbl... > > Sorry, I thought you were gettin ghte error on the Update call, not the > > EndCurrentEdit.. Didn't know you had the constraint in the dataset. > > > > Why not perform the check in the GUI before the call to EndCurrentEdit? > > > > Kevin > > > > "Gary Shell" <gshell@fuse.net> wrote in message > > news:%231gacccaEHA.1248@TK2MSFTNGP11.phx.gbl... > > > I am bound to a dataset. There is no relation in the dataset. The > > > EndCurrentEdit does not make a trip to the server. (only an UPDATE > method > > > would do that.) The exception is actually occurring in the > EndCurrentEdit > > > because there is a No Nulls constraint on the column in question. I've > > dug > > > into this a bit further and when the EndCurrentEdit is executed if the > > > exception occurs ALL of the data in the items collection for the new row > > is > > > set to "". I don't understand why that should be. > > > > > > Gary > > > > > > "a" <a@a.com> wrote in message > > > news:%23FF0gWcaEHA.3480@TK2MSFTNGP11.phx.gbl... > > > > Post some code for this. I am wondering a few things: are you > binding > > to > > > > datasets? Do you have the relation in the dataset? That would let > you > > > > catch the violation prior to going to the server > > > > in the call to EndCurrentEdit. > > > > > > > > Kevin > > > > > > > > > > > > > > > > > > > > "Gary Shell" <gshell@fuse.net> wrote in message > > > > news:OlCz$ZbaEHA.2520@TK2MSFTNGP12.phx.gbl... > > > > > I have a simple data form that can edit and add records to a table. > > In > > > my > > > > > "OK Button" code I EndCurrentEdit and then perform a UPDATE method > on > > > the > > > > > data adapter. It works just fine adding and editing data. > > > > > > > > > > One field on the table is a foreign key and I have referential > > integrity > > > > > enforced on the server. When adding a new record I catch the > > exception > > > > > raised by the Update method if the user supplies invalid data and > > > display > > > > an > > > > > error message. I then allow the user to fix the problem and hit the > > OK > > > > > button again. The problem is when I get to the Update method again > > the > > > > > underlying row in the dataset seems to be no longer bound to my > > > controls. > > > > > That is, if I go in and look at the contents of the item collection > > for > > > > the > > > > > row being added the field that triggered the exception is still > null. > > > It > > > > > does not have the value in the control that the user just corrected. > > > > > > > > > > Is there something I need to tell the dataset or more likely the > data > > > > > adapter that we are "retrying" the update? > > > > > > > > > > Sign me somewhat confused, but impressed, by this .Net databinding. > > > > > > > > > > Gary
Gary Shell - 16 Jul 2004 02:35 GMT One quick question, since I am binding the SelectedValue to my main table and the datasource, displaymember and valuemember to a "lookup" table am I really doing Complex binding? (Obviously, I am confused by the difference.)
Thanks for this code. If my idea posted in my other reply (temporarily turning off Constraint checking) does not pan out, I'll DEFINITELY use this.
Thanks again.
Yeah . . . . I neglected to see you are not using a CurrencyManager.
That will solve all your problems (and may create new ones.) I have a hard time getting the MS Solution to work in my live Apps. In their solution, you would need to set your Non-Nullable field to something (0 or ""), otherwise you get the error on .Rows.Add(dr)
The following does not work for all controls (checkbox deos not work), but it does work for TextBox and ComboBox:
Public WithEvents cm As CurrencyManager
Private Sub frmTaskEntry_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load Me.cboType.DataBindings.Add(New System.Windows.Forms.Binding("SelectedValue", TaskData.Data.dsTaskData1, "tblTask.TypeRef")) Me.cboType.DataSource = TaskData.Data.dsTaskData1 Me.cboType.DisplayMember = "tblType.Name" Me.cboType.ValueMember = "tblType.ID"
cm = Me.BindingContext(TaskData.Data.dsTaskData1, "tblTask")
cm.AddNew()
' USED TO PROGRAMMATICALLY SET A VALUE IN THE NEW ROW drv = CType(cm.Current, DataRowView) 'Set ProjectRef drv.Item("ProjectRef") = intProjectRef cm.Refresh()
End Sub
Private Sub butSave_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles butSave.Click Try Me.cm.EndCurrentEdit() Me.lblInfo.Text = "Record Successfully Saved" booDirty = False Catch ex As Exception Me.lblInfo.Text = ex.Message End Try End Sub
'--------
The drv = CType(cm.Current, DataRowView) 'Set ProjectRef drv.Item("ProjectRef") = intProjectRef
part needs to be done on columns bound to CheckBoxes, NumericUpDowns, DateTimePickers . . .
Check out the CurrencyManager, which is the result of me.BindingContext when the binding is complex.
Kevin
"Gary Shell" <gshell@fuse.net> wrote in message news:ujIH7fraEHA.1000@TK2MSFTNGP12.phx.gbl... Another quick thought. I wonder if this has anything to do with the fact that my AddNew procedure uses the technique described below. I don' t add the new row to the bindingcontext. I add it directly to the dataset. This does seem to work OK when no exceptions are thrown. But I wonder if when the exception IS thrown if the fact that the BindingContext didn't know I anything about the added row is causing the weirdness I see.
Here's the pertinent text from a file found on Microsoft's site:
However, these auto-generated programs have a major limitation. If the data being accessed has any fields that cannot be null (because the database schema does not allow nulls), then the program generated by the wizard will be unable to add records. When the Add button is pressed, an error message will indicate the first field in the record that is not allowed to be null. (If you don't have the latest service packs applied, you may not see the error message but the program will still refuse to add a record.)
The problem is that the Data Form Wizard uses the BindingContext object to add a row to the bound DataTable. Here is the code that fails in the btnAdd_Click event routine:
Me.BindingContext(objProducts, "Products").AddNew() The solution is to bypass the BindingContext object for new rows. Below is the typical code to add a new row. This code should replace the single line of code above:
Dim dr As DataRow dr = objProducts.Tables("Products").NewRow dr.Item("ProductName") = "" dr.Item("Discontinued") = False ' Set any other fields that cannot null to default values. objProducts.Tables("Products").Rows.Add(dr) "Gary Shell" <gshell@fuse.net> wrote in message news:ubx868qaEHA.1764@TK2MSFTNGP10.phx.gbl... First let me say I'm glad we are still talking. I was afraid I might have pissed you off in our exchange about crossposting.
The GUI is pretty simple. I will distill it further though to make this brief. I have a text field called MetricName bound to txtMetricName. I have a MetricClass field (with a no null constraint) bound to a combo box's SelectedValue property. The list portion of the combo is bound to a table called MetricClasses. There are a few other fields including a couple of booleans.
If I add a new record all fields on the form are blanked, as expected. I then type a MetricName and NOTHING else, then hit my update code, the EndCurrentEdit method throws an exception about the null state of MetricClass, again as expected.
I have a watch variable set to me.dsMetric.ItemArray and just before the EndCurrentEdit I can see the text I typed into the txtMetricName textbox in the correct ItemArray element. (I can also see the contents of the other textboxes and checkboxes as well.) But as soon as the EndCurrentEdit throws the exception, the entire ItemArray is reset. By that I mean, all Items are cleared to a valid state for a new row, i.e. all non-nullable fields are set to "", all nullable fields are set to null and all fields with declared default values have their correct defaults (only my booleans have default declared some true some false). These same values from this ItemArray are also populated back to the bound controls, wiping out whatever I'd typed into the various text boxes.
What happens to your ItemArray after such an exception. What happens to your form controls? I'm desperately trying to figure out a way to preserve their state so I can retry the EndCurrentEdit. That brings up a point, after your exception is thrown, and you click save a second time exactly what does your code do? Does it do another EndCurrrentEdit and if that works you use an UPDATE method on the dataadapter?
Hope this is a bit clearer.
And thanks for sticking with me on this!!!!!!
Gary "a" <a@a.com> wrote in message news:%23IWxKToaEHA.3508@TK2MSFTNGP09.phx.gbl... Try Me.cm.EndCurrentEdit() Me.lblInfo.Text = "Record Successfully Saved" booDirty = FalseCatch ex As Exception Me.lblInfo.Text = ex.MessageEnd Try This works for me. I have some columns that do not allow Nulls. When I click save, if there is an error (a no-null is still DBNull.Value) it shows it to the user. They can then fix it and click save and all is well.Explain your GUI to me a little better. Kevin "Gary Shell" <gshell@fuse.net> wrote in message news:uN6ODfdaEHA.596@TK2MSFTNGP11.phx.gbl...> Yeah, I was a bit obtuse in my initial description. > > You are correct I could add code to check for the null first. But I am > trying to figure out why the EndCurrentEdit clears the dataset's item > collection when an exception occurs. I want to know what to expect when > some OTHER exception occurs, not just the one for this constraint. > > Gary > > > "a" <a@a.com> wrote in message > news:%231%23ozDdaEHA.556@tk2msftngp13.phx.gbl... > > Sorry, I thought you were gettin ghte error on the Update call, not the > > EndCurrentEdit.. Didn't know you had the constraint in the dataset. > > > > Why not perform the check in the GUI before the call to EndCurrentEdit? > > > > Kevin > > > > "Gary Shell" <gshell@fuse.net> wrote in message > > news:%231gacccaEHA.1248@TK2MSFTNGP11.phx.gbl... > > > I am bound to a dataset. There is no relation in the dataset. The > > > EndCurrentEdit does not make a trip to the server. (only an UPDATE > method > > > would do that.) The exception is actually occurring in the > EndCurrentEdit > > > because there is a No Nulls constraint on the column in question. I've > > dug > > > into this a bit further and when the EndCurrentEdit is executed if the > > > exception occurs ALL of the data in the items collection for the new row > > is > > > set to "". I don't understand why that should be. > > > > > > Gary > > > > > > "a" <a@a.com> wrote in message > > > news:%23FF0gWcaEHA.3480@TK2MSFTNGP11.phx.gbl... > > > > Post some code for this. I am wondering a few things: are you > binding > > to > > > > datasets? Do you have the relation in the dataset? That would let > you > > > > catch the violation prior to going to the server > > > > in the call to EndCurrentEdit. > > > > > > > > Kevin > > > > > > > > > > > > > > > > > > > > "Gary Shell" <gshell@fuse.net> wrote in message > > > > news:OlCz$ZbaEHA.2520@TK2MSFTNGP12.phx.gbl... > > > > > I have a simple data form that can edit and add records to a table. > > In > > > my > > > > > "OK Button" code I EndCurrentEdit and then perform a UPDATE method > on > > > the > > > > > data adapter. It works just fine adding and editing data. > > > > > > > > > > One field on the table is a foreign key and I have referential > > integrity > > > > > enforced on the server. When adding a new record I catch the > > exception > > > > > raised by the Update method if the user supplies invalid data and > > > display > > > > an > > > > > error message. I then allow the user to fix the problem and hit the > > OK > > > > > button again. The problem is when I get to the Update method again > > the > > > > > underlying row in the dataset seems to be no longer bound to my > > > controls. > > > > > That is, if I go in and look at the contents of the item collection > > for > > > > the > > > > > row being added the field that triggered the exception is still > null. > > > It > > > > > does not have the value in the control that the user just corrected. > > > > > > > > > > Is there something I need to tell the dataset or more likely the > data > > > > > adapter that we are "retrying" the update? > > > > > > > > > > Sign me somewhat confused, but impressed, by this .Net databinding. > > > > > > > > > > Gary
Gary Shell - 16 Jul 2004 06:25 GMT Well I fixed it and didn't have to resort to a currency manager at all! My problems most assuredly were due to the stupid work around to the AddNew dilemma. This workaround had me create my own data row and add it directly to the dataset bypassing the forms BindingContext. I wanna shoot the person who came up with that fix. Bypassing the BindingContext like that and then latter trying to use it to do the EndCurrentEdit and such is just plain STUPID! My gut told me that from day one and I ignored my gut. (Grumble, Grumble 32 hours of debugging time latter...) Here is the scoop:
Turning off the Constraint test IS possible, there is a Dataset.EnbleConstraint property that controls that. But when I did turn it off, I got a weird error on the Me.BindingContext(me.dsMetric, "Metric").AddNew statement. That error said "DataBinding could not find a row in the list that is suitable for all bindings".
While doing a Google search on that error I uncovered a discussion about why the AddNew method was throwing an exception on the fields with a No Null constraint and as it turns out, it REALLY has nothing to do with those constraints at all. The problem is with the Boolean fields defined in the same data set. I know, I know that doesn't sound right. Trust me. Read this:http://groups.Google.com/groups?hl=en&lr=&ie=UTF-8&frame=right&thÕc90598e5d95dba &seekm=eVR4w7tVDHA.2024%40TK2MSFTNGP12.phx.gbl#link6 (scroll to message 6 in the thread), (Sorry, I know that will word-wrap, I don't know how to avoid that. If you can't read that: basically he said the problem is in the XSD definition of the data set. He tells how to edit that XSD.) Well, I thought what's another few minutes lost and tried what he suggested. I added the default value info to the XML for each Boolean field in the schema. I then got rid of my Dataset.EnableConstraints=off code and Voila! the AddNew worked just fine. Ooooooooo. We're on to something here... I can jettison the manual add of a new row to the underlying data set. I won't have to bypass the BindingContext. This is starting to sound better already.
So, next I went to my Commit code and change it to a simple Me.BindingContext(Me.dsMetric, "Metric").EndCurrentEdit() sqldaMetric.Update(Me.dsMetric)
Just like your code. Well it worked!!! I tried a new record and filled in my MetricName field and left my MetricClass field blank. The EndCurrentEdit threw a No Nulls Allowed exception as expected and (trumpet flourish) LEFT MY DATASET AND CONTROLS CONTENTS INTACT!!! Now I could go back to my form and all of the contents were still there. All I had to do was select a MetricClass from the dropdown and click my update button again. My data was written to my SQLServer. Whew!!!!!
Again, I was able to do this all using the forms own BindingContext. Since IT created the new row, IT knew exactly how to keep my bound controls in sync. What a surprise. <grin>
Two other items. First, I do have these controls on a tab page so I made sure in the FormLoad event I had a "tabPage.BindingContext = Me.BindingContext" for each tab page. (Unsure, I added one for the actual TabControl itself as well, just for good measure.) This apparently overcomes the problem of each container maintaining its own BindingContext. (A case CAN be made to not do this, I realize. But in most situations one is enough, I think.)
Second, I noticed an anomaly with the ComboBox. If a ComboBox is programmatically populated and the user does not physically select a value, choosing to accept the displayed value, the BindingContext does not know to use this displayed value. (I'm guessing that because the BindingContext gets no event notifying it of the data.) On my form, I actually had a MetricClass and a MetricSubClass combobox. A selection in the Class causes a query of the data base to repopulate the SubClass combobox. If I didn't ever click in the SubClass combobox, the EndEdit threw an No Nulls Exception about the MetricSubClass field. Digging into the watch window I drilled down into the Binding Context and found it had a DataRowView. So I declared my own DataRowView and set it to this one. I could then drill down on that and found the itemarray. Sure enough there was still a null there. So I added this:
'Get the context's actual DataRowView so we can manipulate it theDataRowView = Me.BindingContext(Me.dsMetric, "Metric").Current 'now place the combobox selected values in to the appropriate item. 'Remember this DataRowView is a window directly into the 'BindingContext theDataRowView.Item("MetricSubclass") = Me.cmbMetric_SubClass.SelectedValue
This made sure that the BindingContext knew about the content of the SubClass combobox.
What a learning curve. I was at a brick wall for 30 odd hours and suddenly the dam broke.
Hope some of this is helpful to you as well.
Thanks for the discussion. It really helped give me some direction.
Gary
One quick question, since I am binding the SelectedValue to my main table and the datasource, displaymember and valuemember to a "lookup" table am I really doing Complex binding? (Obviously, I am confused by the difference.)
Thanks for this code. If my idea posted in my other reply (temporarily turning off Constraint checking) does not pan out, I'll DEFINITELY use this.
Thanks again.
Gary "a" <a@a.com> wrote in message news:OKS9H1raEHA.3204@TK2MSFTNGP09.phx.gbl... Yeah . . . . I neglected to see you are not using a CurrencyManager.
That will solve all your problems (and may create new ones.) I have a hard time getting the MS Solution to work in my live Apps. In their solution, you would need to set your Non-Nullable field to something (0 or ""), otherwise you get the error on .Rows.Add(dr)
The following does not work for all controls (checkbox deos not work), but it does work for TextBox and ComboBox:
Public WithEvents cm As CurrencyManager
Private Sub frmTaskEntry_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load Me.cboType.DataBindings.Add(New System.Windows.Forms.Binding("SelectedValue", TaskData.Data.dsTaskData1, "tblTask.TypeRef")) Me.cboType.DataSource = TaskData.Data.dsTaskData1 Me.cboType.DisplayMember = "tblType.Name" Me.cboType.ValueMember = "tblType.ID"
cm = Me.BindingContext(TaskData.Data.dsTaskData1, "tblTask")
cm.AddNew()
' USED TO PROGRAMMATICALLY SET A VALUE IN THE NEW ROW drv = CType(cm.Current, DataRowView) 'Set ProjectRef drv.Item("ProjectRef") = intProjectRef cm.Refresh()
End Sub
Private Sub butSave_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles butSave.Click Try Me.cm.EndCurrentEdit() Me.lblInfo.Text = "Record Successfully Saved" booDirty = False Catch ex As Exception Me.lblInfo.Text = ex.Message End Try End Sub
'--------
The drv = CType(cm.Current, DataRowView) 'Set ProjectRef drv.Item("ProjectRef") = intProjectRef
part needs to be done on columns bound to CheckBoxes, NumericUpDowns, DateTimePickers . . .
Check out the CurrencyManager, which is the result of me.BindingContext when the binding is complex.
Kevin
"Gary Shell" <gshell@fuse.net> wrote in message news:ujIH7fraEHA.1000@TK2MSFTNGP12.phx.gbl... Another quick thought. I wonder if this has anything to do with the fact that my AddNew procedure uses the technique described below. I don' t add the new row to the bindingcontext. I add it directly to the dataset. This does seem to work OK when no exceptions are thrown. But I wonder if when the exception IS thrown if the fact that the BindingContext didn't know I anything about the added row is causing the weirdness I see.
Here's the pertinent text from a file found on Microsoft's site:
However, these auto-generated programs have a major limitation. If the data being accessed has any fields that cannot be null (because the database schema does not allow nulls), then the program generated by the wizard will be unable to add records. When the Add button is pressed, an error message will indicate the first field in the record that is not allowed to be null. (If you don't have the latest service packs applied, you may not see the error message but the program will still refuse to add a record.)
The problem is that the Data Form Wizard uses the BindingContext object to add a row to the bound DataTable. Here is the code that fails in the btnAdd_Click event routine:
Me.BindingContext(objProducts, "Products").AddNew() The solution is to bypass the BindingContext object for new rows. Below is the typical code to add a new row. This code should replace the single line of code above:
Dim dr As DataRow dr = objProducts.Tables("Products").NewRow dr.Item("ProductName") = "" dr.Item("Discontinued") = False ' Set any other fields that cannot null to default values. objProducts.Tables("Products").Rows.Add(dr) "Gary Shell" <gshell@fuse.net> wrote in message news:ubx868qaEHA.1764@TK2MSFTNGP10.phx.gbl... First let me say I'm glad we are still talking. I was afraid I might have pissed you off in our exchange about crossposting.
The GUI is pretty simple. I will distill it further though to make this brief. I have a text field called MetricName bound to txtMetricName. I have a MetricClass field (with a no null constraint) bound to a combo box's SelectedValue property. The list portion of the combo is bound to a table called MetricClasses. There are a few other fields including a couple of booleans.
If I add a new record all fields on the form are blanked, as expected. I then type a MetricName and NOTHING else, then hit my update code, the EndCurrentEdit method throws an exception about the null state of MetricClass, again as expected.
I have a watch variable set to me.dsMetric.ItemArray and just before the EndCurrentEdit I can see the text I typed into the txtMetricName textbox in the correct ItemArray element. (I can also see the contents of the other textboxes and checkboxes as well.) But as soon as the EndCurrentEdit throws the exception, the entire ItemArray is reset. By that I mean, all Items are cleared to a valid state for a new row, i.e. all non-nullable fields are set to "", all nullable fields are set to null and all fields with declared default values have their correct defaults (only my booleans have default declared some true some false). These same values from this ItemArray are also populated back to the bound controls, wiping out whatever I'd typed into the various text boxes.
What happens to your ItemArray after such an exception. What happens to your form controls? I'm desperately trying to figure out a way to preserve their state so I can retry the EndCurrentEdit. That brings up a point, after your exception is thrown, and you click save a second time exactly what does your code do? Does it do another EndCurrrentEdit and if that works you use an UPDATE method on the dataadapter?
Hope this is a bit clearer.
And thanks for sticking with me on this!!!!!!
Gary "a" <a@a.com> wrote in message news:%23IWxKToaEHA.3508@TK2MSFTNGP09.phx.gbl... Try Me.cm.EndCurrentEdit() Me.lblInfo.Text = "Record Successfully Saved" booDirty = FalseCatch ex As Exception Me.lblInfo.Text = ex.MessageEnd Try This works for me. I have some columns that do not allow Nulls. When I click save, if there is an error (a no-null is still DBNull.Value) it shows it to the user. They can then fix it and click save and all is well.Explain your GUI to me a little better. Kevin "Gary Shell" <gshell@fuse.net> wrote in message news:uN6ODfdaEHA.596@TK2MSFTNGP11.phx.gbl...> Yeah, I was a bit obtuse in my initial description. > > You are correct I could add code to check for the null first. But I am > trying to figure out why the EndCurrentEdit clears the dataset's item > collection when an exception occurs. I want to know what to expect when > some OTHER exception occurs, not just the one for this constraint. > > Gary > > > "a" <a@a.com> wrote in message > news:%231%23ozDdaEHA.556@tk2msftngp13.phx.gbl... > > Sorry, I thought you were gettin ghte error on the Update call, not the > > EndCurrentEdit.. Didn't know you had the constraint in the dataset. > > > > Why not perform the check in the GUI before the call to EndCurrentEdit? > > > > Kevin > > > > "Gary Shell" <gshell@fuse.net> wrote in message > > news:%231gacccaEHA.1248@TK2MSFTNGP11.phx.gbl... > > > I am bound to a dataset. There is no relation in the dataset. The > > > EndCurrentEdit does not make a trip to the server. (only an UPDATE > method > > > would do that.) The exception is actually occurring in the > EndCurrentEdit > > > because there is a No Nulls constraint on the column in question. I've > > dug > > > into this a bit further and when the EndCurrentEdit is executed if the > > > exception occurs ALL of the data in the items collection for the new row > > is > > > set to "". I don't understand why that should be. > > > > > > Gary > > > > > > "a" <a@a.com> wrote in message > > > news:%23FF0gWcaEHA.3480@TK2MSFTNGP11.phx.gbl... > > > > Post some code for this. I am wondering a few things: are you > binding > > to > > > > datasets? Do you have the relation in the dataset? That would let > you > > > > catch the violation prior to going to the server > > > > in the call to EndCurrentEdit. > > > > > > > > Kevin > > > > > > > > > > > > > > > > > > > > "Gary Shell" <gshell@fuse.net> wrote in message > > > > news:OlCz$ZbaEHA.2520@TK2MSFTNGP12.phx.gbl... > > > > > I have a simple data form that can edit and add records to a table. > > In > > > my > > > > > "OK Button" code I EndCurrentEdit and then perform a UPDATE method > on > > > the > > > > > data adapter. It works just fine adding and editing data. > > > > > > > > > > One field on the table is a foreign key and I have referential > > integrity > > > > > enforced on the server. When adding a new record I catch the > > exception > > > > > raised by the Update method if the user supplies invalid data and > > > display > > > > an > > > > > error message. I then allow the user to fix the problem and hit the > > OK > > > > > button again. The problem is when I get to the Update method again > > the > > > > > underlying row in the dataset seems to be no longer bound to my > > > controls. > > > > > That is, if I go in and look at the contents of the item collection > > for > > > > the > > > > > row being added the field that triggered the exception is still > null. > > > It > > > > > does not have the value in the control that the user just corrected. > > > > > > > > > > Is there something I need to tell the dataset or more likely the > data > > > > > adapter that we are "retrying" the update? > > > > > > > > > > Sign me somewhat confused, but impressed, by this .Net databinding. > > > > > > > > > > Gary
a - 16 Jul 2004 17:49 GMT You ARE resorting to a CurrencyManager.
It's like this: BindingManagerBase is a BaseClass with 2 implementations, CurrencyManager and PropertyManager. Simple Binding (properties) results in a PropertyManager. Complex Binding (list) results in a CurrencyManager.
me.BindingContext(...) will Either be a PM or a CM, depending on the type of binding.
Me.BindingContext(me.dsMetric, "Metric").AddNew
is the same as
dim cm as CurrencyManager cm = Me.BindingContext(me.dsMetric, "Metric") cm.addnew
Just wanted to provide some more info. It helped me when I understood what was going on here.
As you noticed, to set the Value in code, Get the DataRowView from the CurrencyManager, and set it. You MAY have to call CM.Refresh to display the data in a bound control.
The Validate of the control is what triggers the data to go to the Underlying datasource, so clicking the combobox, (or setting the focus), can work too.
NOTE: The tweak you did in the XSD does not Serialize, as far as I can tell . . . . So, that solution does not work if you are consuming a WebService. (WSDL does not set the NullValue property) I had a multiple-day experience like your own figuring that out.
Kevin Well I fixed it and didn't have to resort to a currency manager at all! My problems most assuredly were due to the stupid work around to the AddNew dilemma. This workaround had me create my own data row and add it directly to the dataset bypassing the forms BindingContext. I wanna shoot the person who came up with that fix. Bypassing the BindingContext like that and then latter trying to use it to do the EndCurrentEdit and such is just plain STUPID! My gut told me that from day one and I ignored my gut. (Grumble, Grumble 32 hours of debugging time latter...) Here is the scoop:
Turning off the Constraint test IS possible, there is a Dataset.EnbleConstraint property that controls that. But when I did turn it off, I got a weird error on the Me.BindingContext(me.dsMetric, "Metric").AddNew statement. That error said "DataBinding could not find a row in the list that is suitable for all bindings".
While doing a Google search on that error I uncovered a discussion about why the AddNew method was throwing an exception on the fields with a No Null constraint and as it turns out, it REALLY has nothing to do with those constraints at all. The problem is with the Boolean fields defined in the same data set. I know, I know that doesn't sound right. Trust me. Read this:http://groups.Google.com/groups?hl=en&lr=&ie=UTF-8&frame=right&thÕc90598e5d95dba &seekm=eVR4w7tVDHA.2024%40TK2MSFTNGP12.phx.gbl#link6 (scroll to message 6 in the thread), (Sorry, I know that will word-wrap, I don't know how to avoid that. If you can't read that: basically he said the problem is in the XSD definition of the data set. He tells how to edit that XSD.) Well, I thought what's another few minutes lost and tried what he suggested. I added the default value info to the XML for each Boolean field in the schema. I then got rid of my Dataset.EnableConstraints=off code and Voila! the AddNew worked just fine. Ooooooooo. We're on to something here... I can jettison the manual add of a new row to the underlying data set. I won't have to bypass the BindingContext. This is starting to sound better already.
So, next I went to my Commit code and change it to a simple Me.BindingContext(Me.dsMetric, "Metric").EndCurrentEdit() sqldaMetric.Update(Me.dsMetric)
Just like your code. Well it worked!!! I tried a new record and filled in my MetricName field and left my MetricClass field blank. The EndCurrentEdit threw a No Nulls Allowed exception as expected and (trumpet flourish) LEFT MY DATASET AND CONTROLS CONTENTS INTACT!!! Now I could go back to my form and all of the contents were still there. All I had to do was select a MetricClass from the dropdown and click my update button again. My data was written to my SQLServer. Whew!!!!!
Again, I was able to do this all using the forms own BindingContext. Since IT created the new row, IT knew exactly how to keep my bound controls in sync. What a surprise. <grin>
Two other items. First, I do have these controls on a tab page so I made sure in the FormLoad event I had a "tabPage.BindingContext = Me.BindingContext" for each tab page. (Unsure, I added one for the actual TabControl itself as well, just for good measure.) This apparently overcomes the problem of each container maintaining its own BindingContext. (A case CAN be made to not do this, I realize. But in most situations one is enough, I think.)
Second, I noticed an anomaly with the ComboBox. If a ComboBox is programmatically populated and the user does not physically select a value, choosing to accept the displayed value, the BindingContext does not know to use this displayed value. (I'm guessing that because the BindingContext gets no event notifying it of the data.) On my form, I actually had a MetricClass and a MetricSubClass combobox. A selection in the Class causes a query of the data base to repopulate the SubClass combobox. If I didn't ever click in the SubClass combobox, the EndEdit threw an No Nulls Exception about the MetricSubClass field. Digging into the watch window I drilled down into the Binding Context and found it had a DataRowView. So I declared my own DataRowView and set it to this one. I could then drill down on that and found the itemarray. Sure enough there was still a null there. So I added this:
'Get the context's actual DataRowView so we can manipulate it theDataRowView = Me.BindingContext(Me.dsMetric, "Metric").Current 'now place the combobox selected values in to the appropriate item. 'Remember this DataRowView is a window directly into the 'BindingContext theDataRowView.Item("MetricSubclass") = Me.cmbMetric_SubClass.SelectedValue
This made sure that the BindingContext knew about the content of the SubClass combobox.
What a learning curve. I was at a brick wall for 30 odd hours and suddenly the dam broke.
Hope some of this is helpful to you as well.
Thanks for the discussion. It really helped give me some direction.
Gary
"Gary Shell" <gshell@fuse.net> wrote in message news:uombCVtaEHA.3996@TK2MSFTNGP12.phx.gbl... One quick question, since I am binding the SelectedValue to my main table and the datasource, displaymember and valuemember to a "lookup" table am I really doing Complex binding? (Obviously, I am confused by the difference.)
Thanks for this code. If my idea posted in my other reply (temporarily turning off Constraint checking) does not pan out, I'll DEFINITELY use this.
Thanks again.
Gary "a" <a@a.com> wrote in message news:OKS9H1raEHA.3204@TK2MSFTNGP09.phx.gbl... Yeah . . . . I neglected to see you are not using a CurrencyManager.
That will solve all your problems (and may create new ones.) I have a hard time getting the MS Solution to work in my live Apps. In their solution, you would need to set your Non-Nullable field to something (0 or ""), otherwise you get the error on .Rows.Add(dr)
The following does not work for all controls (checkbox deos not work), but it does work for TextBox and ComboBox:
Public WithEvents cm As CurrencyManager
Private Sub frmTaskEntry_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load Me.cboType.DataBindings.Add(New System.Windows.Forms.Binding("SelectedValue", TaskData.Data.dsTaskData1, "tblTask.TypeRef")) Me.cboType.DataSource = TaskData.Data.dsTaskData1 Me.cboType.DisplayMember = "tblType.Name" Me.cboType.ValueMember = "tblType.ID"
cm = Me.BindingContext(TaskData.Data.dsTaskData1, "tblTask")
cm.AddNew()
' USED TO PROGRAMMATICALLY SET A VALUE IN THE NEW ROW drv = CType(cm.Current, DataRowView) 'Set ProjectRef drv.Item("ProjectRef") = intProjectRef cm.Refresh()
End Sub
Private Sub butSave_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles butSave.Click Try Me.cm.EndCurrentEdit() Me.lblInfo.Text = "Record Successfully Saved" booDirty = False Catch ex As Exception Me.lblInfo.Text = ex.Message End Try End Sub
'--------
The drv = CType(cm.Current, DataRowView) 'Set ProjectRef drv.Item("ProjectRef") = intProjectRef
part needs to be done on columns bound to CheckBoxes, NumericUpDowns, DateTimePickers . . .
Check out the CurrencyManager, which is the result of me.BindingContext when the binding is complex.
Kevin
"Gary Shell" <gshell@fuse.net> wrote in message news:ujIH7fraEHA.1000@TK2MSFTNGP12.phx.gbl... Another quick thought. I wonder if this has anything to do with the fact that my AddNew procedure uses the technique described below. I don' t add the new row to the bindingcontext. I add it directly to the dataset. This does seem to work OK when no exceptions are thrown. But I wonder if when the exception IS thrown if the fact that the BindingContext didn't know I anything about the added row is causing the weirdness I see.
Here's the pertinent text from a file found on Microsoft's site:
However, these auto-generated programs have a major limitation. If the data being accessed has any fields that cannot be null (because the database schema does not allow nulls), then the program generated by the wizard will be unable to add records. When the Add button is pressed, an error message will indicate the first field in the record that is not allowed to be null. (If you don't have the latest service packs applied, you may not see the error message but the program will still refuse to add a record.)
The problem is that the Data Form Wizard uses the BindingContext object to add a row to the bound DataTable. Here is the code that fails in the btnAdd_Click event routine:
Me.BindingContext(objProducts, "Products").AddNew() The solution is to bypass the BindingContext object for new rows. Below is the typical code to add a new row. This code should replace the single line of code above:
Dim dr As DataRow dr = objProducts.Tables("Products").NewRow dr.Item("ProductName") = "" dr.Item("Discontinued") = False ' Set any other fields that cannot null to default values. objProducts.Tables("Products").Rows.Add(dr) "Gary Shell" <gshell@fuse.net> wrote in message news:ubx868qaEHA.1764@TK2MSFTNGP10.phx.gbl... First let me say I'm glad we are still talking. I was afraid I might have pissed you off in our exchange about crossposting.
The GUI is pretty simple. I will distill it further though to make this brief. I have a text field called MetricName bound to txtMetricName. I have a MetricClass field (with a no null constraint) bound to a combo box's SelectedValue property. The list portion of the combo is bound to a table called MetricClasses. There are a few other fields including a couple of booleans.
If I add a new record all fields on the form are blanked, as expected. I then type a MetricName and NOTHING else, then hit my update code, the EndCurrentEdit method throws an exception about the null state of MetricClass, again as expected.
I have a watch variable set to me.dsMetric.ItemArray and just before the EndCurrentEdit I can see the text I typed into the txtMetricName textbox in the correct ItemArray element. (I can also see the contents of the other textboxes and checkboxes as well.) But as soon as the EndCurrentEdit throws the exception, the entire ItemArray is reset. By that I mean, all Items are cleared to a valid state for a new row, i.e. all non-nullable fields are set to "", all nullable fields are set to null and all fields with declared default values have their correct defaults (only my booleans have default declared some true some false). These same values from this ItemArray are also populated back to the bound controls, wiping out whatever I'd typed into the various text boxes.
What happens to your ItemArray after such an exception. What happens to your form controls? I'm desperately trying to figure out a way to preserve their state so I can retry the EndCurrentEdit. That brings up a point, after your exception is thrown, and you click save a second time exactly what does your code do? Does it do another EndCurrrentEdit and if that works you use an UPDATE method on the dataadapter?
Hope this is a bit clearer.
And thanks for sticking with me on this!!!!!!
Gary "a" <a@a.com> wrote in message news:%23IWxKToaEHA.3508@TK2MSFTNGP09.phx.gbl... Try Me.cm.EndCurrentEdit() Me.lblInfo.Text = "Record Successfully Saved" booDirty = FalseCatch ex As Exception Me.lblInfo.Text = ex.MessageEnd Try This works for me. I have some columns that do not allow Nulls. When I click save, if there is an error (a no-null is still DBNull.Value) it shows it to the user. They can then fix it and click save and all is well.Explain your GUI to me a little better. Kevin "Gary Shell" <gshell@fuse.net> wrote in message news:uN6ODfdaEHA.596@TK2MSFTNGP11.phx.gbl...> Yeah, I was a bit obtuse in my initial description. > > You are correct I could add code to check for the null first. But I am > trying to figure out why the EndCurrentEdit clears the dataset's item > collection when an exception occurs. I want to know what to expect when > some OTHER exception occurs, not just the one for this constraint. > > Gary > > > "a" <a@a.com> wrote in message > news:%231%23ozDdaEHA.556@tk2msftngp13.phx.gbl... > > Sorry, I thought you were gettin ghte error on the Update call, not the > > EndCurrentEdit.. Didn't know you had the constraint in the dataset. > > > > Why not perform the check in the GUI before the call to EndCurrentEdit? > > > > Kevin > > > > "Gary Shell" <gshell@fuse.net> wrote in message > > news:%231gacccaEHA.1248@TK2MSFTNGP11.phx.gbl... > > > I am bound to a dataset. There is no relation in the dataset. The > > > EndCurrentEdit does not make a trip to the server. (only an UPDATE > method > > > would do that.) The exception is actually occurring in the > EndCurrentEdit > > > because there is a No Nulls constraint on the column in question. I've > > dug > > > into this a bit further and when the EndCurrentEdit is executed if the > > > exception occurs ALL of the data in the items collection for the new row > > is > > > set to "". I don't understand why that should be. > > > > > > Gary > > > > > > "a" <a@a.com> wrote in message > > > news:%23FF0gWcaEHA.3480@TK2MSFTNGP11.phx.gbl... > > > > Post some code for this. I am wondering a few things: are you > binding > > to > > > > datasets? Do you have the relation in the dataset? That would let > you > > > > catch the violation prior to going to the server > > > > in the call to EndCurrentEdit. > > > > > > > > Kevin > > > > > > > > > > > > > > > > > > > > "Gary Shell" <gshell@fuse.net> wrote in message > > > > news:OlCz$ZbaEHA.2520@TK2MSFTNGP12.phx.gbl... > > > > > I have a simple data form that can edit and add records to a table. > > In > > > my > > > > > "OK Button" code I EndCurrentEdit and then perform a UPDATE method > on > > > the > > > > > data adapter. It works just fine adding and editing data. > > > > > > > > > > One field on the table is a foreign key and I have referential > > integrity > > > > > enforced on the server. When adding a new record I catch the > > exception > > > > > raised by the Update method if the user supplies invalid data and > > > display > > > > an > > > > > error message. I then allow the user to fix the problem and hit the > > OK > > > > > button again. The problem is when I get to the Update method again > > the > > > > > underlying row in the dataset seems to be no longer bound to my > > > controls. > > > > > That is, if I go in and look at the contents of the item collection > > for > > > > the > > > > > row being added the field that triggered the exception is still > null. > > > It > > > > > does not have the value in the control that the user just corrected. > > > > > > > > > > Is there something I need to tell the dataset or more likely the > data > > > > > adapter that we are "retrying" the update? > > > > > > > > > > Sign me somewhat confused, but impressed, by this .Net databinding. > > > > > > > > > > Gary
Gary Shell - 16 Jul 2004 18:35 GMT You are right I am using a CurrencyManager, I realized I should have said I didn't have to explicitly reference it I was able to use the "default" one.
It took me a while to figure out how to set that Value in code. But boy oh boy, did it open my eyes as to how all this "object stuff" really works. I've only been using VB.Net for a month, but have been a VB user since the very first beta (aka ThunderForm if my memory serves me). It didn't appear that I did need to call the CM.Refresh, my data did display. In fact it was displayed all along. But I just realized that if I set the data in the CM to something different than was displayed at the time, I might need to do this refresh. Thanks, I'll TRY to remember that for future use. <grin>
Since the Validate of the control actually what triggers the data to go to the dataset, is there any downside to the way I did it, or should I change my code to just set focus to each combobox to make sure they send their data to the dataset?
I am not consuming any WebServices in this app, nor will it in the future. So the "tweak" should suffice for now. However, if that tweak can't be used as you indicated what is the CORRECT way to get around the issue of the Boolean fields causing a "No Nulls" exception on other fields when invoking an AddNew method on the BindingContext? Obviously, the widely published method of bypassing the BindingContext and adding your own row to the associated dataset is NOT usable. I now know what havoc it will cause when exceptions are thrown on the subsequent EndCurrentEdit. I wonder how many other poor souls have been bitten by this supposed workaround? I'm betting hundreds of folks!
Again thank you so much for this discourse. It proves it is possible for an old dog to learn new tricks. <big ol' grin>
You ARE resorting to a CurrencyManager.
It's like this: BindingManagerBase is a BaseClass with 2 implementations, CurrencyManager and PropertyManager. Simple Binding (properties) results in a PropertyManager. Complex Binding (list) results in a CurrencyManager.
me.BindingContext(...) will Either be a PM or a CM, depending on the type of binding.
Me.BindingContext(me.dsMetric, "Metric").AddNew
is the same as
dim cm as CurrencyManager cm = Me.BindingContext(me.dsMetric, "Metric") cm.addnew
Just wanted to provide some more info. It helped me when I understood what was going on here.
As you noticed, to set the Value in code, Get the DataRowView from the CurrencyManager, and set it. You MAY have to call CM.Refresh to display the data in a bound control.
The Validate of the control is what triggers the data to go to the Underlying datasource, so clicking the combobox, (or setting the focus), can work too.
NOTE: The tweak you did in the XSD does not Serialize, as far as I can tell . . . . So, that solution does not work if you are consuming a WebService. (WSDL does not set the NullValue property) I had a multiple-day experience like your own figuring that out.
Kevin "Gary Shell" <gshell@fuse.net> wrote in message news:OX%23hgVvaEHA.2016@TK2MSFTNGP09.phx.gbl... Well I fixed it and didn't have to resort to a currency manager at all! My problems most assuredly were due to the stupid work around to the AddNew dilemma. This workaround had me create my own data row and add it directly to the dataset bypassing the forms BindingContext. I wanna shoot the person who came up with that fix. Bypassing the BindingContext like that and then latter trying to use it to do the EndCurrentEdit and such is just plain STUPID! My gut told me that from day one and I ignored my gut. (Grumble, Grumble 32 hours of debugging time latter...) Here is the scoop:
Turning off the Constraint test IS possible, there is a Dataset.EnbleConstraint property that controls that. But when I did turn it off, I got a weird error on the Me.BindingContext(me.dsMetric, "Metric").AddNew statement. That error said "DataBinding could not find a row in the list that is suitable for all bindings".
While doing a Google search on that error I uncovered a discussion about why the AddNew method was throwing an exception on the fields with a No Null constraint and as it turns out, it REALLY has nothing to do with those constraints at all. The problem is with the Boolean fields defined in the same data set. I know, I know that doesn't sound right. Trust me. Read this:http://groups.Google.com/groups?hl=en&lr=&ie=UTF-8&frame=right&thÕc90598e5d95dba &seekm=eVR4w7tVDHA.2024%40TK2MSFTNGP12.phx.gbl#link6 (scroll to message 6 in the thread), (Sorry, I know that will word-wrap, I don't know how to avoid that. If you can't read that: basically he said the problem is in the XSD definition of the data set. He tells how to edit that XSD.) Well, I thought what's another few minutes lost and tried what he suggested. I added the default value info to the XML for each Boolean field in the schema. I then got rid of my Dataset.EnableConstraints=off code and Voila! the AddNew worked just fine. Ooooooooo. We're on to something here... I can jettison the manual add of a new row to the underlying data set. I won't have to bypass the BindingContext. This is starting to sound better already.
So, next I went to my Commit code and change it to a simple Me.BindingContext(Me.dsMetric, "Metric").EndCurrentEdit() sqldaMetric.Update(Me.dsMetric)
Just like your code. Well it worked!!! I tried a new record and filled in my MetricName field and left my MetricClass field blank. The EndCurrentEdit threw a No Nulls Allowed exception as expected and (trumpet flourish) LEFT MY DATASET AND CONTROLS CONTENTS INTACT!!! Now I could go back to my form and all of the contents were still there. All I had to do was select a MetricClass from the dropdown and click my update button again. My data was written to my SQLServer. Whew!!!!!
Again, I was able to do this all using the forms own BindingContext. Since IT created the new row, IT knew exactly how to keep my bound controls in sync. What a surprise. <grin>
Two other items. First, I do have these controls on a tab page so I made sure in the FormLoad event I had a "tabPage.BindingContext = Me.BindingContext" for each tab page. (Unsure, I added one for the actual TabControl itself as well, just for good measure.) This apparently overcomes the problem of each container maintaining its own BindingContext. (A case CAN be made to not do this, I realize. But in most situations one is enough, I think.)
Second, I noticed an anomaly with the ComboBox. If a ComboBox is programmatically populated and the user does not physically select a value, choosing to accept the displayed value, the BindingContext does not know to use this displayed value. (I'm guessing that because the BindingContext gets no event notifying it of the data.) On my form, I actually had a MetricClass and a MetricSubClass combobox. A selection in the Class causes a query of the data base to repopulate the SubClass combobox. If I didn't ever click in the SubClass combobox, the EndEdit threw an No Nulls Exception about the MetricSubClass field. Digging into the watch window I drilled down into the Binding Context and found it had a DataRowView. So I declared my own DataRowView and set it to this one. I could then drill down on that and found the itemarray. Sure enough there was still a null there. So I added this:
'Get the context's actual DataRowView so we can manipulate it theDataRowView = Me.BindingContext(Me.dsMetric, "Metric").Current 'now place the combobox selected values in to the appropriate item. 'Remember this DataRowView is a window directly into the 'BindingContext theDataRowView.Item("MetricSubclass") = Me.cmbMetric_SubClass.SelectedValue
This made sure that the BindingContext knew about the content of the SubClass combobox.
What a learning curve. I was at a brick wall for 30 odd hours and suddenly the dam broke.
Hope some of this is helpful to you as well.
Thanks for the discussion. It really helped give me some direction.
Gary
"Gary Shell" <gshell@fuse.net> wrote in message news:uombCVtaEHA.3996@TK2MSFTNGP12.phx.gbl... One quick question, since I am binding the SelectedValue to my main table and the datasource, displaymember and valuemember to a "lookup" table am I really doing Complex binding? (Obviously, I am confused by the difference.)
Thanks for this code. If my idea posted in my other reply (temporarily turning off Constraint checking) does not pan out, I'll DEFINITELY use this.
Thanks again.
Gary "a" <a@a.com> wrote in message news:OKS9H1raEHA.3204@TK2MSFTNGP09.phx.gbl... Yeah . . . . I neglected to see you are not using a CurrencyManager.
That will solve all your problems (and may create new ones.) I have a hard time getting the MS Solution to work in my live Apps. In their solution, you would need to set your Non-Nullable field to something (0 or ""), otherwise you get the error on .Rows.Add(dr)
The following does not work for all controls (checkbox deos not work), but it does work for TextBox and ComboBox:
Public WithEvents cm As CurrencyManager
Private Sub frmTaskEntry_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load Me.cboType.DataBindings.Add(New System.Windows.Forms.Binding("SelectedValue", TaskData.Data.dsTaskData1, "tblTask.TypeRef")) Me.cboType.DataSource = TaskData.Data.dsTaskData1 Me.cboType.DisplayMember = "tblType.Name" Me.cboType.ValueMember = "tblType.ID"
cm = Me.BindingContext(TaskData.Data.dsTaskData1, "tblTask")
cm.AddNew()
' USED TO PROGRAMMATICALLY SET A VALUE IN THE NEW ROW drv = CType(cm.Current, DataRowView) 'Set ProjectRef drv.Item("ProjectRef") = intProjectRef cm.Refresh()
End Sub
Private Sub butSave_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles butSave.Click Try Me.cm.EndCurrentEdit() Me.lblInfo.Text = "Record Successfully Saved" booDirty = False Catch ex As Exception Me.lblInfo.Text = ex.Message End Try End Sub
'--------
The drv = CType(cm.Current, DataRowView) 'Set ProjectRef drv.Item("ProjectRef") = intProjectRef
part needs to be done on columns bound to CheckBoxes, NumericUpDowns, DateTimePickers . . .
Check out the CurrencyManager, which is the result of me.BindingContext when the binding is complex.
Kevin
"Gary Shell" <gshell@fuse.net> wrote in message news:ujIH7fraEHA.1000@TK2MSFTNGP12.phx.gbl... Another quick thought. I wonder if this has anything to do with the fact that my AddNew procedure uses the technique described below. I don' t add the new row to the bindingcontext. I add it directly to the dataset. This does seem to work OK when no exceptions are thrown. But I wonder if when the exception IS thrown if the fact that the BindingContext didn't know I anything about the added row is causing the weirdness I see.
Here's the pertinent text from a file found on Microsoft's site:
However, these auto-generated programs have a major limitation. If the data being accessed has any fields that cannot be null (because the database schema does not allow nulls), then the program generated by the wizard will be unable to add records. When the Add button is pressed, an error message will indicate the first field in the record that is not allowed to be null. (If you don't have the latest service packs applied, you may not see the error message but the program will still refuse to add a record.)
The problem is that the Data Form Wizard uses the BindingContext object to add a row to the bound DataTable. Here is the code that fails in the btnAdd_Click event routine:
Me.BindingContext(objProducts, "Products").AddNew() The solution is to bypass the BindingContext object for new rows. Below is the typical code to add a new row. This code should replace the single line of code above:
Dim dr As DataRow dr = objProducts.Tables("Products").NewRow dr.Item("ProductName") = "" dr.Item("Discontinued") = False ' Set any other fields that cannot null to default values. objProducts.Tables("Products").Rows.Add(dr) "Gary Shell" <gshell@fuse.net> wrote in message news:ubx868qaEHA.1764@TK2MSFTNGP10.phx.gbl... First let me say I'm glad we are still talking. I was afraid I might have pissed you off in our exchange about crossposting.
The GUI is pretty simple. I will distill it further though to make this brief. I have a text field called MetricName bound to txtMetricName. I have a MetricClass field (with a no null constraint) bound to a combo box's SelectedValue property. The list portion of the combo is bound to a table called MetricClasses. There are a few other fields including a couple of booleans.
If I add a new record all fields on the form are blanked, as expected. I then type a MetricName and NOTHING else, then hit my update code, the EndCurrentEdit method throws an exception about the null state of MetricClass, again as expected.
I have a watch variable set to me.dsMetric.ItemArray and just before the EndCurrentEdit I can see the text I typed into the txtMetricName textbox in the correct ItemArray element. (I can also see the contents of the other textboxes and checkboxes as well.) But as soon as the EndCurrentEdit throws the exception, the entire ItemArray is reset. By that I mean, all Items are cleared to a valid state for a new row, i.e. all non-nullable fields are set to "", all nullable fields are set to null and all fields with declared default values have their correct defaults (only my booleans have default declared some true some false). These same values from this ItemArray are also populated back to the bound controls, wiping out whatever I'd typed into the various text boxes.
What happens to your ItemArray after such an exception. What happens to your form controls? I'm desperately trying to figure out a way to preserve their state so I can retry the EndCurrentEdit. That brings up a point, after your exception is thrown, and you click save a second time exactly what does your code do? Does it do another EndCurrrentEdit and if that works you use an UPDATE method on the dataadapter?
Hope this is a bit clearer.
And thanks for sticking with me on this!!!!!!
Gary "a" <a@a.com> wrote in message news:%23IWxKToaEHA.3508@TK2MSFTNGP09.phx.gbl... Try Me.cm.EndCurrentEdit() Me.lblInfo.Text = "Record Successfully Saved" booDirty = FalseCatch ex As Exception Me.lblInfo.Text = ex.MessageEnd Try This works for me. I have some columns that do not allow Nulls. When I click save, if there is an error (a no-null is still DBNull.Value) it shows it to the user. They can then fix it and click save and all is well.Explain your GUI to me a little better. Kevin "Gary Shell" <gshell@fuse.net> wrote in message news:uN6ODfdaEHA.596@TK2MSFTNGP11.phx.gbl...> Yeah, I was a bit obtuse in my initial description. > > You are correct I could add code to check for the null first. But I am > trying to figure out why the EndCurrentEdit clears the dataset's item > collection when an exception occurs. I want to know what to expect when > some OTHER exception occurs, not just the one for this constraint. > > Gary > > > "a" <a@a.com> wrote in message > news:%231%23ozDdaEHA.556@tk2msftngp13.phx.gbl... > > Sorry, I thought you were gettin ghte error on the Update call, not the > > EndCurrentEdit.. Didn't know you had the constraint in the dataset. > > > > Why not perform the check in the GUI before the call to EndCurrentEdit? > > > > Kevin > > > > "Gary Shell" <gshell@fuse.net> wrote in message > > news:%231gacccaEHA.1248@TK2MSFTNGP11.phx.gbl... > > > I am bound to a dataset. There is no relation in the dataset. The > > > En |
|