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

时间:2010-02-05 09:24:18

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

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

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

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

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

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

12 个答案:

答案 0 :(得分:7)

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

答案 1 :(得分:2)

更简单的方法是订阅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

答案 2 :(得分:1)

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

答案 3 :(得分:1)

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

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混在一起并没有帮助我。

答案 4 :(得分:1)

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

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

答案 5 :(得分:1)

我建立了一个相当简单的机制,如下所示:

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

答案 6 :(得分:1)

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

    public bool HasChanges()
    {
        bool Result = false;

        myBindingSource.EndEdit();
        Result = ((DataTable)myBindingSource.DataSource).GetChanges(DataRowState.Modified) != null;


        return Result;
    }

答案 7 :(得分:0)

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

答案 8 :(得分:0)

我一直在做的是捕获控件的各个“已更改”事件。在下面的示例中,我在此示例中使用了tabcontrol。 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

答案 9 :(得分:0)

我不确定问题被问到时是否可用但是我使用了grid_CurrentCellDirtyStateChanged;事件

答案 10 :(得分:0)

我知道这是一个老帖子,但这里是一个带有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;
    }

    /// <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

}

答案 11 :(得分:0)

首先确保您设置了DataSourceUpdateMode.OnPropertyChanged

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

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

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