【问题标题】:How to tell if user has modified data using bindingsource?如何判断用户是否使用 bindingsource 修改了数据?
【发布时间】:2019-07-29 02:29:11
【问题描述】:

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

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

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

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

更新:我发现我可以做bindingSource.EndEdit(),它将更改推送到我在列表中的项目。在我的项目中,我可以说如果 Dirty 抛出一个消息框,但如果他们单击“否”来保存信息,则 CancelEdit 不起作用。

【问题讨论】:

    标签: c# winforms data-binding ado.net subsonic


    【解决方案1】:

    如果列表中的对象支持INotifyPropertyChanged 事件,并且您将List<T> 替换为BindingList<T>,则可以订阅BindingList 的ListChanged 事件以了解用户所做的任何更改。

    【讨论】:

    【解决方案2】:

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

    【讨论】:

    • bindingsource 数据源是一个 List
    • 您无法观看收藏。另一种方法是使用一堆 TextChanged 事件来更新您在保存之前检查的私有“已更改”变量
    • 我有这个想法,但它很讨厌
    • 是的!使用 DataSet/DataTable 是不可能的吗?
    • 我是这么认为的,因为 List 包含 List ,其中包含正在编辑/添加的项目的所有属性等
    【解决方案3】:

    我现在做了这个功能。你可以像这样使用:

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

    【讨论】:

    • 除非 BindingSource 的 DataSource 是“EntityObject”,否则没有用处,我会假设它是 Entity Framework 基类的旧版本?另外,你为什么要传入一个从未使用过的“bs”参数?
    • 你是对的@ChristopherKing,我现在修复了 bs 参数,以便在第一行使用.. 谢谢。 EntityObject 不是旧版本,我所有的实体都继承了 EntityObject 类,即使在 .Net 4.5 中也是如此。
    【解决方案4】:

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

    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 并没有帮助我。

    【讨论】:

      【解决方案5】:

      更简单的方法是订阅 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 如果需要该级别的准确性,可以另外使用。

      【讨论】:

        【解决方案6】:

        我设置了一个相当简单的机制,如下:

        1. 绑定我的控件后,我运行一个方法来查找所有绑定的 控制并保存它们的当前值(我做一个 ReadValue() 只是为了 确保我在 Dictionary 中获得了来自 DataSource) 的值 将控件映射到其值(有一个小方法可以获取 我拥有的每种控件的适当值)。
        2. 我还为每个事件添加了一个更改事件处理程序(同样,具体的 事件由控件的类型决定,但它们都指向 同一个处理程序)
        3. 更改处理程序根据字典检查当前值 价值。如果它不同,那么它会相应地采取行动(在我的情况下 将 关闭 按钮切换为 取消 按钮)。如果是一样的 它检查所有其他绑定控件,因此如果没有 不同的是它可以将Cancel切换回Close;这是一个很好的功能 这种方法也可以识别何时撤消更改, 即使是重新输入原始值。
        4. 在离开之前,如果有要保存的更改,我会遍历 再次绑定控件以执行 WriteValue(),以防万一 WinForms 没有 开始宣传一些变化。

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

        【讨论】:

          【解决方案7】:

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

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

          【讨论】:

            【解决方案8】:

            我知道这是一篇旧帖子,但这里有一个带有 IsDirtyFlag 的 Extended 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;
                }
            
                /// <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
            
            }
            

            【讨论】:

              【解决方案9】:

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

              【讨论】:

              • “在 BeginEdit”到底是什么意思? BindingSource 没有任何 BeginEditEndEdit 事件,是吗?我也在尝试在正确的时间获取我的对象的副本,但我很挣扎。 :(
              • @Jon 我知道你的帖子已经过时了——但是看看我的帖子——你可能会觉得它很有帮助。
              【解决方案10】:

              我总是做的是捕捉控件的各个“更改”事件。在下面的示例中,我在此示例中使用了选项卡控件。 Try/Catch 是一个肮脏的解决方案,不必处理各种异常;-)

              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
              

              【讨论】:

                【解决方案11】:

                我不确定在提问时它是否可用,但我使用的是 grid_CurrentCellDirtyStateChanged;事件

                【讨论】:

                  【解决方案12】:

                  首先确定你设置了 DataSourceUpdateMode.OnPropertyChanged

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

                  然后将此代码添加到您的 movenext 点击事件中

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

                  【讨论】:

                    猜你喜欢
                    • 1970-01-01
                    • 1970-01-01
                    • 2013-12-27
                    • 1970-01-01
                    • 1970-01-01
                    • 2016-02-27
                    • 1970-01-01
                    • 1970-01-01
                    • 2011-03-14
                    相关资源
                    最近更新 更多