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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2206140/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-07 00:24:42  来源:igfitidea点击:

How to tell if user has modified data using bindingsource?

c#winformsdata-bindingado.netsubsonic

提问by Jon

I have a DataGridView bound to a bindingsource which is bound to a List<T>. The user clicks a row that goes to a form with textboxes, etc. The textboxes are databound like so:

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

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

I want to be able to detect if the user has modified any data in the controls when they click the close button, so I can prompt them to say "You have un-saved work. Do you want to Save?"

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

How do I detect this on the binding source?

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

UPDATE:I have worked out that I can do bindingSource.EndEdit()which pushes the changes to my item in the list. In my item, I can then say if Dirty throw a Messagebox but if they click "No" to saving the information, the CancelEdit does not work.

更新:我已经确定我可以执行bindingSource.EndEdit()将更改推送到我在列表中的项目。在我的项目中,我可以说 Dirty 是否抛出 Messagebox 但如果他们单击“否”以保存信息,则 CancelEdit 不起作用。

采纳答案by Jon

From my updated question I found I had to store a current version of the object at BeginEdit using Memberwise.Clone and then in CancelEdit I restored that to the current.

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

回答by Matt Jacobsen

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

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

回答by Matthias Meid

After trying different 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 fine for me, although I save lots of state information in the Windows Form's instance fields. However, twiddling with CurrentChangedand CurrentItemChangeddid not help me.

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

回答by Oliver

If your object within the List support the INotifyPropertyChangedevent and you replace the List<T>by a BindingList<T>you can subscribe to the ListChangedevent of the BindingList to get informed about any changes made by the user.

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

回答by Tiago Gouvêa

I made this function now. You can use like:

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

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

回答by Jeroen Bom

What I always do is to capture the individual "changed" events of the controls. In the below example I used a tabcontrol in this example. The Try/Catch is a dirty solution for not having to deal with all kinds of exceptions ;-)

我总是做的是捕获控件的单个“更改”事件。在下面的例子中,我在这个例子中使用了一个 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

回答by Michael

I set up a fairly simple mechanism, as follows:

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

  1. After binding my controls, I run a method that finds all the bound controls and saves their current values (I do a ReadValue()just to be sure I've got the values from the DataSource) in a Dictionarythat maps a control to its value (there's a small method that gets the appropriate value for each kind of control that I have).
  2. I also add a change-event handler for each one (again, the specific event is determined by the type of control, but they all point to the same handler)
  3. The change-handler checks the current value against the Dictionaryvalue. If it's different then it acts accordingly (in my case it switches the Closebutton for the Cancelbutton). If it's the same it checks all the other bound controls, so that if nothing is different it can switch Cancelback to Close; it's a nice feature of this method that it also recognizes when a change has been undone, even if it's by re-entering the original value.
  4. Before leaving, if there are changes to be saved I loop through the bound controls again to do WriteValue(), just in case WinFormsdidn't get around to propagating some change.
  1. 绑定我的控件后,我运行一个方法来查找所有绑定的控件并保存它们的当前值(我执行ReadValue()只是为了确保我已经从DataSource获得了值)在一个 将控件映射到其的字典中值(有一个小方法可以为我拥有的每种控件获取适当的值)。
  2. 我还为每一个添加了一个更改事件处理程序(同样,特定事件由控件类型决定,但它们都指向同一个处理程序)
  3. 更改处理程序根据Dictionary值检查当前值。如果它不同,那么它会相应地采取行动(在我的情况下,它会切换“取消”按钮的“关闭”按钮)。如果相同,它会检查所有其他绑定控件,以便如果没有任何不同,它可以将Cancel切换回Close;这种方法的一个很好的功能是它还能识别更改何时被撤消,即使是通过重新输入原始值。
  4. 在离开之前,如果有要保存的更改,我会再次遍历绑定控件以执行WriteValue(),以防万一WinForms无法传播某些更改。

I can share the source if anyone is interested.

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

回答by Kirsten Greed

I aren't sure if it was available when the question was asked but I use the grid_CurrentCellDirtyStateChanged; event

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

回答by GuidoG

If your bindingsource uses a datatable you can do this :

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

    public bool HasChanges()
    {
        bool Result = false;

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


        return Result;
    }

回答by prabhats.net

A simpler approach would be to subscribe to the BindingSource's ListChanged event and set an IsDirty flag based on the event type.

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

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

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

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

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

A word of caution here, it would not be able to detect when the modified value is still same as the original value. Memberwise.Clonecan be used additionally if that level of accuracy is required.

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