Как определить, изменил ли пользователь данные с помощью bindingsource? - PullRequest
8 голосов
/ 05 февраля 2010

У меня есть DataGridView, связанный с источником привязки, который связан с List<T>. Пользователь щелкает строку, которая переходит в форму с текстовыми полями и т. Д. Текстовые поля связаны с данными следующим образом:

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

Я хочу иметь возможность определять, изменил ли пользователь какие-либо данные в элементах управления, когда они нажимают кнопку закрытия, поэтому я могу предложить им сказать «У вас есть несохраненная работа. 1005 *

Как мне обнаружить это в источнике привязки?

ОБНОВЛЕНИЕ: Я выяснил, что я могу сделать bindingSource.EndEdit(), который вносит изменения в мой элемент в списке. В моем элементе я могу сказать, что Dirty выбрасывает окно сообщений, но если они нажимают «Нет» для сохранения информации, CancelEdit не работает.

Ответы [ 11 ]

7 голосов
/ 28 апреля 2011

Если ваш объект в Списке поддерживает событие INotifyPropertyChanged и вы заменили List<T> на BindingList<T>, вы можете подписаться на ListChanged Событие BindingList для получения информации о любых изменениях, внесенных пользователем.

2 голосов
/ 17 июля 2015

Более простой подход состоит в том, чтобы подписаться на событие ListChanged BindingSource и установить флаг 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 может использоваться дополнительно, если требуется такой уровень точности.

1 голос
/ 10 апреля 2015

Если ваш источник привязки использует таблицу данных, вы можете сделать это:

    public bool HasChanges()
    {
        bool Result = false;

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


        return Result;
    }
1 голос
/ 16 января 2014

Я настроил довольно простой механизм, а именно:

  1. После привязки моих элементов управления я запускаю метод, который находит все связанные контролирует и сохраняет их текущие значения (я делаю ReadValue () только для убедитесь, что у меня есть значения из источника данных ) в словаре который отображает элемент управления на его значение (есть небольшой метод, который получает соответствующее значение для каждого вида контроля, который у меня есть).
  2. Я также добавляю обработчик события изменения для каждого (опять же, определенный событие определяется типом управления, но все они указывают на тот же обработчик)
  3. Обработчик изменений проверяет текущее значение по словарю значение. Если он отличается, то он действует соответственно (в моем случае это переключает кнопку Закрыть для кнопки Отмена ). Если это то же самое он проверяет все остальные связанные элементы управления, так что если ничего не иначе он может переключаться Отмена обратно на Закрыть ; это хорошая особенность этот метод, который он также распознает, когда изменение было отменено, даже если это путем повторного ввода исходного значения.
  4. Перед отъездом, если есть изменения, которые нужно сохранить, я перебираю привязать элементы управления снова, чтобы сделать WriteValue () , на всякий случай WinForms не приступить к распространению некоторых изменений.

Я могу поделиться источником, если кому-то интересно.

1 голос
/ 21 ноября 2012

Я сделал эту функцию сейчас. Вы можете использовать как:

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 голос
/ 28 апреля 2011

Попробовав что-то другое, я получил следующий код:

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 Form. Тем не менее, трюки с CurrentChanged и CurrentItemChanged мне не помогли.

1 голос
/ 05 февраля 2010

Если вы привязаны к DataSet, вам повезло: у него есть свойство HasChanges . Вы можете получить фактические изменения, вызвав GetChanges для набора данных. Это возвращает новый набор данных, содержащий копию всех измененных строк

0 голосов
/ 10 августа 2016

Я знаю, что это старый пост, но вот Extended BindingSource с IsDirtyFlag - вы можете адаптировать его так, как вам хочется - я вытащил этот код из другого поста где-то в сети несколько лет назад - внес некоторые очень незначительные изменения, я думаю изначально был в 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

}
0 голосов
/ 13 сентября 2014

Я не уверен, что он был доступен, когда был задан вопрос, но я использую grid_CurrentCellDirtyStateChanged; событие

0 голосов
/ 13 июня 2013

Что я всегда делаю, так это фиксирую отдельные «измененные» события элементов управления. В приведенном ниже примере я использовал 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
...