如何判断用户是否使用bindingsource修改了数据?

我有一个DataGridView绑定到绑定到List的绑定源。 用户单击转到带有文本框等的表单的行。文本框是数据绑定的,如下所示:

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

我希望能够检测到用户在单击关闭按钮时是否修改了控件中的任何数据,因此我可以提示他们说“您有未保存的工作。是否要保存?”

如何在绑定源上检测到这一点?

更新:我已经知道我可以使用bindingSource.EndEdit()将更改推送到列表中的项目。 在我的项目中,我可以说,如果Dirty抛出一个Messagebox但是如果他们点击“No”来保存信息,则CancelEdit不起作用。

如果List中的对象支持INotifyPropertyChanged事件并且您通过BindingList替换List ,则可以订阅BindingList的ListChanged事件以获知用户所做的任何更改。

如果你绑定了一个DataSet,那么你很幸运:它有一个HasChanges属性。 您可以通过在数据集上调用GetChanges来获取实际更改。 这将返回一个新数据集,其中包含所有已更改行的副本

我现在做了这个function。 您可以使用:

 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. 绑定我的控件之后,我运行一个方法,找到所有绑定的控件并保存它们的当前值(我只是为了确保我从DataSource得到了值),在一个将控件映射到它的Dictionary中value(有一种小方法可以为我拥有的每种控件获得适当的值)。
  2. 我还为每一个添加了一个更改事件处理程序(同样,特定事件由控件类型决定,但它们都指向同一个处理程序)
  3. change-handler根据Dictionary值检查当前值。 如果它不同则会相应地起作用(在我的情况下,它会切换“ 取消”按钮的“ 关闭 ”按钮)。 如果它是相同的,它会检查所有其他绑定控件,这样如果没有什么不同,它可以将Cancel切换回Close ; 这个方法的一个很好的特性是,它还可以识别何时撤消更改,即使重新输入原始值也是如此。
  4. 在离开之前,如果要保存更改,我再次循环绑定控件以执行WriteValue() ,以防万一WinForms没有绕过传播某些更改。

如果有人有兴趣,我可以分享来源。

如果您的bindingsource使用数据表,您可以这样做:

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

在尝试不同的事情后,我最终得到了这段代码:

 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; } 

它对我来说很好,虽然我在Windows窗体的实例字段中保存了大量的状态信息。 然而,与CurrentChangedCurrentItemChanged并没有帮助我。

一种更简单的方法是订阅BindingSource的ListChanged事件,并根据事件类型设置IsDirty标志。

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

并在事件方法中设置IsDirty = true …

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

这里需要注意的是,它无法检测修改后的值何时仍与原始值相同。 如果需要该级别的准确性,可以另外使用Memberwise.Clone

从我更新的问题,我发现我必须使用Memberwise.Clone在BeginEdit存储对象的当前版本,然后在CancelEdit中我将其恢复到当前版本。

我一直在做的是捕获控件的各个“已更改”事件。 在下面的示例中,我在此示例中使用了tabcontrol。 Try / Catch是一个肮脏的解决方案,无需处理各种exception;-)

 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 

当问到这个问题时我不确定它是否可用但是我使用了grid_CurrentCellDirtyStateChanged; 事件

我知道这是一个老post,但这里是一个带有IsDirtyFlag的扩展BindingSource – 你可以根据自己的意愿调整它 – 我在几年前从网上的某个地方提取了这个代码 – 做了一些非常小的改动,我认为它原来是VB – 我转换为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; } ///  /// Returns the First Instance of the specified User Control ///  /// System.Windows.Forms.Control 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 }