Bezdepbonusu7Программирование: статьи и ответы

Вопрос

Как узнать, изменил ли пользователь данные с помощью источника привязки?

Автор вопроса: JonОтветов: 12

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

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

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

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

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

data-bindingwinformssubsonic

Ответы

Ответ 1 · рейтинг 8 · Oliver

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

Ответ 2 · рейтинг 2 · Matt Jacobsen

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

Ответ 3 · рейтинг 2 · Tiago Gouvêa

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

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

Ответ 4 · рейтинг 2 · Matthias Meid

Попробовав разные вещи, я получил этот фрагмент кода:

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. Однако возня с CurrentChanged и CurrentItemChanged мне не помогла.

Ответ 5 · рейтинг 2 · prabhats.net

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

Ответ 6 · рейтинг 1 · Michael

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

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

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

Ответ 7 · рейтинг 1 · GuidoG

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

    public bool HasChanges()
    {
        bool Result = false;

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


        return Result;
    }

Ответ 8 · рейтинг 1 · Ken

Я знаю, что это старый пост, но вот расширенный 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

}

Ответ 9 · Jon

Из моего обновленного вопроса я обнаружил, что мне нужно сохранить текущую версию объекта в BeginEdit с помощью Memberwise.Clone, а затем в CancelEdit я восстановил ее до текущей.

Ответ 10 · Jeroen Bom

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

Ответ 11 · Kirsten Greed

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

Ответ 12 · Wasim Foad

сначала убедитесь, что вы установили 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();
                }

Перевод вопроса и ответов сообщества Stack Overflow, лицензия CC BY-SA. Оригинал на Stack Overflow.

Похожие материалы