Display save indicator after textbox modified

FreeriderUK

Well-known member
Joined
Jun 27, 2006
Messages
100
Location
London
Programming Experience
10+
Hi all - I think this is the right place for this...

I would like to add some kind of indicator to show data has changed in MyDataset, so the user knows they need to click the save button to update the database.

I'm using a number of bound textboxes on a Windows Form and have tried putting this code in the CustomerNameTextBox.Leave event:

If MyCustomersDataSet.HasChanges = True Then
Me.Text="Data Changed"
End If

but the HasChanges thing doesn't seem to be working - it doesn't detect the change.

I must be missing something here. I thought that when you change the text in a databound textbox, it was changing the data in the dataset.:confused:

Or, is there another way of doing this?
 
I would add an event handler to the RowChanged or XXXRowChanged (if you are using typed datasets and tables) event of the table you are binding to.

In C# it would look like this:

VB.NET:
    realDS.Reports.RowChanged += new DataRowChangeEventHandler(Reports_RowChanged);
      

    void Reports_RowChanged(object sender, DataRowChangeEventArgs e)
    {
      changesMadeLabel.Visible = true;
    }

In VB, I have no idea; the way VB adds event handlers is much more awkward and backward. I think you have to do something like:

VB.NET:
  Private WithEvents [B]xyzTempDataTableRef [/B]as MyXXXDataTable = Me.MyTypedDataSet.WhatImBindingToDataTable

  Private Sub WhateverXXX(sender as Object, e as MyTypedDataSet.WhatImBindingToDataTableRowChangeEvent) Handles xyzTempDataTableRef.WhatImBindingToDataTableRowChanged

I've never got on with VB's event subscription for nested objects (the datatable is nested inside the data set), and the way it works compared to C# is just ugly.. Perhaps another forum user who is a bit more used to attaching event handlers to nested objects will be able to provide a more accurate translation
 
Thanks for the suggestion cjard.

After searching for hours for more info I decided to try something else:

In the TextBoxKeyPress event, I set a flag (KeyWasPressed = True)
In the TextBoxTextChanged event, I put this code:

If KeyWasPressed = True Then
'A key was pressed
MsgBox("Key pressed AND Text changed")
KeyWasPressed = False
Else
'No key was pressed
MsgBox("No key pressed")
End If


I need to test some more, but it seems to work...:)
 
If the user pastes data into the text box they need not press a key, but the row will be modified.. Seriously, the RowChangedEvent of the underlying data table is how you would handle this. Then, no matter how the data comes to be modified, your indicator lights up..


The crazy thing is, youre shying away from writing an event handler, but you are happy to go into the IDE and attach an event handler to the TextBox' TextChanged event.. The IDE writes for you:

VB.NET:
Private WithEvents MyTextBox as New TextBox

Private Sub MyTextBox_TextChanged(...) Handles MyTextBox.TextChanged

Why would you avoid writing virtually identical code yourself?
 
Why would you avoid writing virtually identical code yourself?
Cos I don't understand it!:eek:
When I hear stuff ike
the way VB adds event handlers is much more awkward and backward.
puts me off trying!

I will have to try though, cos my method doesn't work properly. The delete key isn't caught either.
 
I was watching Lost, season 2 last night, and this point came up.. Jack's dad said to him "Sometimes, even if there's a 99% chance the patient wont make it.. they really wanna focus more on that 1% of hope.."

Sorry for putting you off trying! Lemme guide you through it:

You have a dataset on your form, called Me.MyCustomersDataSet
Write this under the line "Public Class FormXXX": Private WithEvents whateverDataTableXYZ as MyCustomersDataSet.WhateverDataTable = MyCustomersDataSet.Whatever
Write this as a new sub:
VB.NET:
 Public Sub Whatever_rowChanged() Handles whateverDataTableXYZ.RowChanged

The IDE throws an error, something like:
"yada cant handle RowChanged(sender as Object, yadaYada as YadaYadaEventYadaArgs) yada yada because the signatures are different.."
Copy that bit between the brackets (sender as Object, yada ....), and paste it into your code:

VB.NET:
 Public Sub Whatever_rowChanged[B](sender as Object, yada ...) [/B]Handles whateverDataTableXYZ.RowChanged
  'put your handling code ehre
  End Sub

Now the IDE stops complaining, and you can write your handler code. We jump through this copying-errror-message hoop because I dont know a better way to get the IDE to help out - it KNOWS what the signature should be, but there isnt a snippet or code enhancement I know of to get the IDE to create the handler for you. I wish there was

Sorry its so long and stupid, but that's what VB is like.. If anyone knows a better way of putting an Event Handler in to a variable you declared yourself, let me know (i've asked before, and noone answered. In C# it's like a few keypresses and Intellisense does it all for you.. beautiful. Too bad this isnt C#)
 
You know, after writing all that, I had a bit of an idea:


Write this under the line "Public Class FormXXX":
Private WithEvents whateverDataTableXYZ as MyCustomersDataSet.WhateverDataTable = MyCustomersDataSet.Whatever

Now go to the combo box on the top LEFT of the code, that says "(General)"

Choose the whateverDataTableXYZ entry

Now go to the combo box to the RIGHT of that, and scroll through the events till you find the row changing one and pick it..


The IDE writes the Sub for you. Cant believe noone ever told me that!
Still not quite as flexible as C#, but better than I was doing it before!
 
I managed to add the code as follows:

Private WithEvents CustomersDataTable As MyCustomersDataSet.CustomersDataTable = MyCustomersDataSet.Customers

Private Sub CustomersDataTable_RowChanging(ByVal sender As Object, ByVal e As System.Data.DataRowChangeEventArgs) Handles CustomersDataTable.RowChanging
MsgBox("Changed!")
End Sub


(I have a table called "Customers" and a dataset called "MyCustomersDataset")

After adding the code, I'm having a problem displaying the form. (I have a "splash" screen which Shows the main form) but I get this message:
An error occurred creating the form. See Exception.InnerException for details. The error is: Object reference not set to an instance of an object.
 
Do me a favour.. On the debug menu, you see Exceptions. Choose it and put a tick in the "CLR" exceptions "Thrown" column.. Now as soon as an exception occurs the debugger will break to the point where it is caused..

I really get the feeling it is an attempt to reference something that doesnt exist yet, which might mean we have to add our event handler slightly differently..
 
I thought it might - it's because MyCustomersDataSet doesnt exist yet. Nuisance.


I actually started creating a project to answer your question, then found myself wondering about what it was you were asking

Define at which point you nee dthe user to save.

This is usually just before you:

Fill the datatable with something else
Close the form used to edit the data


At this time, you should call:
theForm.Validate() 'causes currently editing controls to send their edit to the bind source
xxxBindingSource.EndEdit() 'causes the bindsource to send the current edit to the datatable

And at this time, then, dataset.HasChanges or datatable.HasChanges will work to report the changes


I tried lighting up an indicator as to whether the current row was modified, and ran into difficulties of over complication. A control bound to a binding list sends edits when it is validated, typically on Leave. The binding source sends edits into the datatable upon EndEdit. Typically this means the datatable only gets to find out about a change very late on (when youre navigating to the next record), and it doesnt make sense to validate and endedit on every keypress, because that can make the UI slow and annoying if you have validation. As such you would have to atach several handlers to various events on the table and the bindingsource.. and it rapidly gets to be not worth it.

Just identify the one or two points in the code where you clear or refill the dataset, or close the form, and ensure you have:

VB.NET:
Validate()
EndEdit()
If HasChanges AndAlso MessageBox.Show("Lose changes?", ... Buttons.YesNo) = DIalogResult.Yes Then
  Clear() / Fill() / Close()
 
Define at which point you nee dthe user to save.
I wasn't thinking about when the user *needs* to save, I just thought it would be clever to indicate changes had been made - like in a word processor.

At this time, you should call:
theForm.Validate() 'causes currently editing controls to send their edit to the bind source
xxxBindingSource.EndEdit() 'causes the bindsource to send the current edit to the datatable

And at this time, then, dataset.HasChanges or datatable.HasChanges will work to report the changes
That's very useful to know - No wonder HasChanges wasn't working for me.
I'm using this code when user clicks the Save button:
VB.NET:
Me.Validate()
Me.CustomersBindingSource.EndEdit
Me.CustomersTableAdapter.Update(MyCustomersDataSet.Customers)

I'll experiment a bit more with HasChanges.
 
I wasn't thinking about when the user *needs* to save, I just thought it would be clever to indicate changes had been made - like in a word processor.

OK, then you need to think a bit more about when the indicator must appear. It isnt enough to simply assert that changing a textbox means a save must be performed - if the change fails validation, the data wont be updated, and no save need be performed.

End of the day, dont spend hours on this trivial little bit of functionality - it'll cost the company you work for X hundred dollars in wages for soemthign that will never add X hundred dollars of value to the product. Writing a method like this:

VB.NET:
Private Function ShouldSave() as Boolean
  Me.Validate()
  xxxBindSource.EndEdit()
  If xxxDataSet.HasChanges Then
    Return MessageBox.Show("Save changes?", ... Buttons.YesNo) == DialogResult.Yes
  Else
    Return False
  End If
End Function

Now, if the data has changed, if the user says yes, save, then a true is returned, if they say no, or no changes exist, then they shouldnt save.


And jsut use it like:

VB.NET:
Sub Form_closing(...) Handles Form.Closing
  If SHouldSave Then xxxTableAdapter.Update()
End Sub
 
Back
Top