How do I know if a user has modified data using bindingsource?

I have a DataGridView associated with a binding source associated with a List<T> . The user clicks on a line that refers to a form with text fields, etc. Text fields are bound like this:

 if (txtID.DataBindings.Count == 0) txtID.DataBindings.Add("Text", bindingSource, "Title"); 

I want to be able to determine if the user has changed any data in the controls when they click the close button, so I can offer them to say: "You have no saved work. Do you want to save?"

How to detect this in the binding source?

UPDATE: I developed what I can do with bindingSource.EndEdit() , which pushes changes to my item in the list. In my paragraph, I can say if Dirty quit Messagebox, but if they click "No" to save the information, CancelEdit does not work.

+8
c # data-binding subsonic
source share
12 answers

From my updated question, I found that I needed to save the current version of the object in BeginEdit using Memberwise.Clone, and then in CancelEdit I restored it to the current one.

0
source share

If your object in the list supports INotifyPropertyChanged , and you replace List<T> with a BindingList<T> you can subscribe to the ListChanged event to get information about any changes made by the user.

+7
source share

A simpler approach is to subscribe to the BindingSource ListChanged event and set the IsDirty flag based on the type of event.

 categoryBindingSource.ListChanged += new System.ComponentModel.ListChangedEventHandler(categoryBindingSource_ListChanged); 

and set IsDirty = true in the event method ...

 void customerAccountBindingSource_ListChanged(object sender, system.ComponentModel.ListChangedEventArgs e) { if (e.ListChangedType == System.ComponentModel.ListChangedType.ItemChanged) _isDirty = true; } 

A word of caution here: he will not be able to determine when the changed value is still the same as the original value. Memberwise.Clone can be used optionally if this level of accuracy is required.

+2
source share

If you're tied to a DataSet, you're in luck: it has a HasChanges property. You can get the actual changes by calling GetChanges on the dataset. This returns a new dataset containing a copy of all changed rows

+1
source share

After I tried another thing, I ended up with this piece of code:

 private MyClass currentItem = null; private bool itemDirty = false; // can be used for "do you want to save?" private void bindingSource_CurrentChanged(object sender, EventArgs e) { var handler = new PropertyChangedEventHandler((s, e2) => itemDirty = true); var crnt = currentItem as INotifyPropertyChanged; if(crnt != null) crnt.PropertyChanged -= handler; currentItem = (MyClass)bindingSource.Current; crnt = currentItem as INotifyPropertyChanged; if(crnt != null) crnt.PropertyChanged += handler; itemDirty = false; } 

It works great for me, although I keep a lot of status information in the fields of the Windows Form instance. However, the rotation using CurrentChanged and CurrentItemChanged did not help me.

+1
source share

I made this function now. You can use as:

 if (changedOrNew(myBindingSource)){ // Do something! } public bool changedOrNew(BindingSource bs){ EntityObject obj = (EntityObject)bs.Current; if (obj==null) return false; return (obj.EntityState == EntityState.Detached || obj.EntityState == EntityState.Added || obj.EntityState == EntityState.Modified); } 
+1
source share

I installed a fairly simple mechanism as follows:

  • After binding my controls, I run a method that finds all related controls and stores their current values ​​(I do ReadValue () just to make sure that I have values ​​from the DataSource) in a Dictionary that maps the control to its value (there is a small method which gets the corresponding value for each kind of control that I have).
  • I also add a change handler for each of them (again, a specific event is determined by the type of control, but they all point to the same handler)
  • The change processor checks the current value at the Dictionary cost. If it is different, then it acts accordingly (in my case, it switches the Close button for the Cancel button). If it is the same, it checks all other related controls, so if nothing else, it can switch Cancel back to Close ; it is a good feature of this method, which also recognizes when a change has been undone, even if it is by re-entering the original value.
  • Before leaving, if there are changes that need to be saved, I go through the linked control again to do WriteValue (), just in case WinForms could not get around to spread some changes.

I can share the source if anyone is interested.

+1
source share

If your binding source uses a data type, you can do this:

  public bool HasChanges() { bool Result = false; myBindingSource.EndEdit(); Result = ((DataTable)myBindingSource.DataSource).GetChanges(DataRowState.Modified) != null; return Result; } 
+1
source share

What I always do is capture individual β€œchanged” events of the controls. In the example below, I used tabcontrol in this example. Try / Catch is a dirty solution for not dealing with all kinds of exceptions; -)

 Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load ' ' some code ' BindingNavigatorSaveItem.Enabled = False For Each tabctl As Control In Me.TabControl1.Controls For Each ctl As Control In tabctl.Controls Try If ctl.GetType Is GetType(TextBox) Then AddHandler DirectCast(ctl, TextBox).TextChanged, AddressOf GenDataChanged ElseIf ctl.GetType Is GetType(NumericUpDown) Then AddHandler DirectCast(ctl, NumericUpDown).ValueChanged, AddressOf GenDataChanged ElseIf ctl.GetType Is GetType(ComboBox) Then AddHandler DirectCast(ctl, ComboBox).SelectedValueChanged, AddressOf GenDataChanged ElseIf ctl.GetType Is GetType(CheckBox) Then AddHandler DirectCast(ctl, CheckBox).CheckStateChanged, AddressOf GenDataChanged End If Catch ex As Exception End Try Next Next End Sub Private Sub GenDataChanged(sender As System.Object, e As System.EventArgs) BindingNavigatorSaveItem.Enabled = True End Sub 
0
source share

I'm not sure if it was available when the question was asked, but I use grid_CurrentCellDirtyStateChanged; event

0
source share

I know this is an old post, but here is an extended BindingSource with IsDirtyFlag - you can adapt it the way you would like - I pulled this code from another publication somewhere on the network a few years ago - made some very minor changes, I think That it was originally in VB - I converted to C # ..

 using System.ComponentModel.Design; using System.Windows.Forms; using System.ComponentModel; using System.Data; using System; public class BindingSourceExIsDirty : System.Windows.Forms.BindingSource, INotifyPropertyChanged { #region "DECLARATIONS AND PROPERTIES" private string _displayMember; private DataTable _dataTable; private DataSet _dataSet; private BindingSource _parentBindingSource; private System.Windows.Forms.Form _form; private System.Windows.Forms.Control _usercontrol; private bool _isCurrentDirtyFlag = false; public bool IsCurrentDirty { get { return _isCurrentDirtyFlag; } set { if (_isCurrentDirtyFlag != value) { _isCurrentDirtyFlag = value; this.OnPropertyChanged(value.ToString()); //call the event when flag is set if (value == true) { OnCurrentIsDirty(new EventArgs()); } } } } private string _objectSource; public string ObjectSource { get { return _objectSource; } set { _objectSource = value; this.OnPropertyChanged(value); } } private bool _autoSaveFlag; public bool AutoSave { get { return _autoSaveFlag; } set { _autoSaveFlag = value; this.OnPropertyChanged(value.ToString()); } } #endregion #region "EVENTS" //Current Is Dirty Event public event CurrentIsDirtyEventHandler CurrentIsDirty; // Delegate declaration. public delegate void CurrentIsDirtyEventHandler(object sender, EventArgs e); protected virtual void OnCurrentIsDirty(EventArgs e) { if (CurrentIsDirty != null) { CurrentIsDirty(this, e); } } //PropertyChanged Event // public event PropertyChangedEventHandler INotifyPropertyChanged.PropertyChanged; public event PropertyChangedEventHandler PropertyChanged; protected virtual void OnPropertyChanged(string info) { PropertyChangedEventHandler handler = PropertyChanged; if (handler != null) { handler(this, new PropertyChangedEventArgs(info)); } } #endregion #region "METHODS" private void _BindingComplete(System.Object sender, System.Windows.Forms.BindingCompleteEventArgs e) { if (e.BindingCompleteContext == BindingCompleteContext.DataSourceUpdate) { if (e.BindingCompleteState == BindingCompleteState.Success & !e.Binding.Control.BindingContext.IsReadOnly) { //Make sure the data source value is refreshed (fixes problem mousing off control) e.Binding.ReadValue(); //if not focused then not a user edit. if (!e.Binding.Control.Focused) return; //check for the lookup type of combobox that changes position instead of value if (e.Binding.Control as ComboBox != null) { //if the combo box has the same data member table as the binding source, ignore it if (((ComboBox)e.Binding.Control).DataSource != null) { if (((ComboBox)e.Binding.Control).DataSource as BindingSource != null) { if (((BindingSource)((ComboBox)e.Binding.Control).DataSource).DataMember == (this.DataMember)) { return; } } } } IsCurrentDirty = true; //set the dirty flag because data was changed } } } private void _DataSourceChanged(System.Object sender, System.EventArgs e) { _parentBindingSource = null; if (this.DataSource == null) { _dataSet = null; } else { //get a reference to the dataset BindingSource bsTest = this; Type dsType = bsTest.DataSource.GetType(); //try to cast the data source as a binding source while ((bsTest.DataSource as BindingSource != null)) { //set the parent binding source reference if (_parentBindingSource == null) _parentBindingSource = bsTest; //if cast was successful, walk up the chain until dataset is reached bsTest = (BindingSource)bsTest.DataSource; } //since it is no longer a binding source, it must be a dataset or something else if (bsTest.DataSource as DataSet == null) { //Cast as dataset did not work if (dsType.IsClass == false) { throw new ApplicationException("Invalid Binding Source "); } else { _dataSet = null; } } else { _dataSet = (DataSet)bsTest.DataSource; } //is there a data member - find the datatable if (!string.IsNullOrEmpty(this.DataMember)) { _DataMemberChanged(sender, e); } //CType(value.GetService(GetType(IDesignerHost)), IDesignerHost) if (_form == null) GetFormInstance(); if (_usercontrol == null) GetUserControlInstance(); } } private void _DataMemberChanged(System.Object sender, System.EventArgs e) { if (string.IsNullOrEmpty(this.DataMember) | _dataSet == null) { _dataTable = null; } else { //check to see if the Data Member is the name of a table in the dataset if (_dataSet.Tables(this.DataMember) == null) { //it must be a relationship instead of a table System.Data.DataRelation rel = _dataSet.Relations(this.DataMember); if ((rel != null)) { _dataTable = rel.ChildTable; } else { throw new ApplicationException("Invalid Data Member"); } } else { _dataTable = _dataSet.Tables(this.DataMember); } } } public override System.ComponentModel.ISite Site { get { return base.Site; } set { //runs at design time to initiate ContainerControl base.Site = value; if (value == null) return; // Requests an IDesignerHost service using Component.Site.GetService() IDesignerHost service = (IDesignerHost)value.GetService(typeof(IDesignerHost)); if (service == null) return; if ((service.RootComponent as Form != null)) { _form = (Form)service.RootComponent; } else if ((service.RootComponent as UserControl != null)) { _usercontrol = (UserControl)service.RootComponent; } } } public System.Windows.Forms.Form GetFormInstance() { if (_form == null & this.CurrencyManager.Bindings.Count > 0) { _form = this.CurrencyManager.Bindings[0].Control.FindForm(); } return _form; } /// <summary> /// Returns the First Instance of the specified User Control /// </summary> /// <returns>System.Windows.Forms.Control</returns> public System.Windows.Forms.Control GetUserControlInstance() { if (_usercontrol == null & this.CurrencyManager.Bindings.Count > 0) { System.Windows.Forms.Control[] _uControls = null; _uControls = this.CurrencyManager.Bindings[0].Control.FindForm().Controls.Find(this.Site.Name.ToString(), true); _usercontrol = _uControls[0]; } return _usercontrol; } public BindingSourceExIsDirty() { DataMemberChanged += _DataMemberChanged; DataSourceChanged += _DataSourceChanged; BindingComplete += _BindingComplete; } // PositionChanged private override void _PositionChanged(object sender, EventArgs e) { if (IsCurrentDirty) { // IsAutoSavingEvent if (AutoSave | MessageBox.Show(_msg, "Confirm Save", MessageBoxButtons.YesNo) == DialogResult.Yes) { try { //cast table as ITableUpdate to get the Update method // CType(_dataTable, ITableUpdate).Update() } catch (Exception ex) { MessageBox.Show(ex, "Position Changed Error"); // - needs to raise an event } } else { this.CancelEdit(); _dataTable.RejectChanges(); } IsCurrentDirty = false; } base(e); } #endregion } 
0
source share

First make sure you set DataSourceUpdateMode.OnPropertyChanged

 txrFirstName.DataBindings.Add("Text", bindingSource1, "FirstName", false,DataSourceUpdateMode.OnPropertyChanged); 

then add this code to the movenext click event

  if (((DataRowView)bindingSource1.Current).IsNew) { MessageBox.Show("Current Row IsNew"); } if (((DataRowView)bindingSource1.CurrencyManager.Current).Row.HasVersion(DataRowVersion.Proposed)) { MessageBox.Show("Current Row Modified"); DialogResult dialogResult = MessageBox.Show("Current Row Modified", "Some Title", MessageBoxButtons.YesNo); if (dialogResult == DialogResult.Yes) { //do something ((DataRowView)bindingSource1.CurrencyManager.Current).Row.AcceptChanges(); } else if (dialogResult == DialogResult.No) { //do something else ((DataRowView)bindingSource1.CurrencyManager.Current).Row.RejectChanges(); } } else { bindingSource1.MoveNext(); } 
0
source share

All Articles