在BindingList的ItemChanging事件中获取已删除的项目

时间:2014-04-28 10:54:43

标签: c# inotifypropertychanged bindinglist

我在我的应用程序中使用了绑定列表以及ItemChanged事件。

我有什么方法可以知道ItemChanged事件中以前的属性值。目前,我正在添加一个名为“OldValue”的独立属性来实现此目的。

有没有办法知道项目更改事件中已删除的项目。我无法找到任何方法来知道哪个项目已从列表中删除。

5 个答案:

答案 0 :(得分:38)

如果我理解正确,您想获取有关从绑定列表中删除的项目的信息。

我认为最简单的方法是创建自己的绑定列表,该列表来自绑定列表。

在你的内部将覆盖RemoveItem方法,因此在从绑定列表中删除项目之前,您将能够触发包含将要删除的项目的事件。

public class myBindingList<myInt> : BindingList<myInt>
{
        protected override void RemoveItem(int itemIndex)
        {
            //itemIndex = index of item which is going to be removed
            //get item from binding list at itemIndex position
            myInt deletedItem = this.Items[itemIndex];

            if (BeforeRemove != null)
            {
                //raise event containing item which is going to be removed
                BeforeRemove(deletedItem);
            }

            //remove item from list
            base.RemoveItem(itemIndex);
        }

        public delegate void myIntDelegate(myInt deletedItem);
        public event myIntDelegate BeforeRemove;
    }

为了示例,我创建了类型myInt实现INotifyPropertyChanged - 接口只是在添加/删除绑定列表中的元素后刷新dataGridView。

public class myInt : INotifyPropertyChanged
    {
        public myInt(int myIntVal)
        {
            myIntProp = myIntVal;
        }
        private int iMyInt;
        public int myIntProp {
            get
            {
                return iMyInt;
            }
            set
            {
                iMyInt = value;
                if (PropertyChanged != null)
                {
                    PropertyChanged(this, new PropertyChangedEventArgs("myIntProp"));
                }
            } 
        }

        public event PropertyChangedEventHandler PropertyChanged;
    }

我使用int初始化绑定列表(确切地说是myInts),然后我将绑定列表绑定到dataGridView(用于演示目的)并订阅我的BeforeRemove事件。

bindingList = new myBindingList<myInt>();
        bindingList.Add(new myInt(8));
        bindingList.Add(new myInt(9));
        bindingList.Add(new myInt(11));
        bindingList.Add(new myInt(12));

        dataGridView1.DataSource = bindingList;
        bindingList.BeforeRemove += bindingList_BeforeRemove;

如果引发了BeforeRemove事件,我有删除的项目

 void bindingList_BeforeRemove(Form1.myInt deletedItem)
    {
        MessageBox.Show("You've just deleted item with value " + deletedItem.myIntProp.ToString());
    }

下面是整个示例代码(在表单上删除3个按钮和dataGridView) - 按钮1初始化绑定列表,按钮2将项目添加到列表,按钮3从登记列表中删除项目

before delete

after delete

deleted

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace bindinglist
{
public partial class Form1 : Form
{
    myBindingList<myInt> bindingList;

    public Form1()
    {
        InitializeComponent();
    }

    private void button1_Click(object sender, EventArgs e)
    {
        bindingList = new myBindingList<myInt>();
        bindingList.Add(new myInt(8));
        bindingList.Add(new myInt(9));
        bindingList.Add(new myInt(11));
        bindingList.Add(new myInt(12));

        dataGridView1.DataSource = bindingList;
        bindingList.BeforeRemove += bindingList_BeforeRemove;
    }

    void bindingList_BeforeRemove(Form1.myInt deletedItem)
    {
        MessageBox.Show("You've just deleted item with value " + deletedItem.myIntProp.ToString());
    }


    private void button2_Click(object sender, EventArgs e)
    {
        bindingList.Add(new myInt(13));
    }

    private void button3_Click(object sender, EventArgs e)
    {
        bindingList.RemoveAt(dataGridView1.SelectedRows[0].Index);
    }

    public class myInt : INotifyPropertyChanged
    {
        public myInt(int myIntVal)
        {
            myIntProp = myIntVal;
        }
        private int iMyInt;
        public int myIntProp {
            get
            {
                return iMyInt;
            }
            set
            {
                iMyInt = value;
                if (PropertyChanged != null)
                {
                    PropertyChanged(this, new PropertyChangedEventArgs("myIntProp"));
                }
            } 
        }

        public event PropertyChangedEventHandler PropertyChanged;
    }

    public class myBindingList<myInt> : BindingList<myInt>
    {
        protected override void RemoveItem(int itemIndex)
        {
            myInt deletedItem = this.Items[itemIndex];

            if (BeforeRemove != null)
            {
                BeforeRemove(deletedItem);
            }

            base.RemoveItem(itemIndex);
        }

        public delegate void myIntDelegate(myInt deletedItem);
        public event myIntDelegate BeforeRemove;
    }
}
}

致评论

&#34;问题的另一部分是=&gt;有没有办法知道列表中更改的项目的旧值?在ListChangedEvent中不共享任何内容&#34;

要查看项目的旧值,您可以覆盖SetItem方法

protected override void SetItem(int index, myInt item)
        {
            //here we still have old value at index
            myInt oldMyInt = this.Items[index];
            //new value
            myInt newMyInt = item;

            if (myIntOldNew != null)
            {
                //raise event
                myIntOldNew(oldMyInt, newMyInt);
            }

            //update item at index position
            base.SetItem(index, item);
        }

当指定索引处的对象发生更改时会触发,如此

bindingList[dataGridView1.SelectedRows[0].Index] = new myInt(new Random().Next());

如果您尝试直接修改项目的属性,那么棘手的部分是

bindingList[dataGridView1.SelectedRows[0].Index].myIntProp = new Random().Next();

SetItem 赢了,必须更换整个对象。

所以我们需要另一名代表和处理此事件的事件

public delegate void myIntDelegateChanged(myInt oldItem, myInt newItem);
public event myIntDelegateChanged myIntOldNew;

然后我们可以订阅这个

bindingList.myIntOldNew += bindingList_myIntOldNew;

并处理它

void bindingList_myIntOldNew(Form1.myInt oldItem, Form1.myInt newItem)
{
    MessageBox.Show("You've just CHANGED item with value " + oldItem.myIntProp.ToString() + " to " + newItem.myIntProp.ToString());
}

before event raised changed

更新了代码(需要4个按钮,第4个修改所选项目)

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace bindinglist
{
public partial class Form1 : Form
{
    myBindingList<myInt> bindingList;

    public Form1()
    {
        InitializeComponent();
    }

    private void button1_Click(object sender, EventArgs e)
    {
        bindingList = new myBindingList<myInt>();
        bindingList.Add(new myInt(8));
        bindingList.Add(new myInt(9));
        bindingList.Add(new myInt(11));
        bindingList.Add(new myInt(12));

        dataGridView1.DataSource = bindingList;
        bindingList.BeforeRemove += bindingList_BeforeRemove;
        bindingList.myIntOldNew += bindingList_myIntOldNew;
    }

    void bindingList_myIntOldNew(Form1.myInt oldItem, Form1.myInt newItem)
    {
        MessageBox.Show("You've just CHANGED item with value " + oldItem.myIntProp.ToString() + " to " + newItem.myIntProp.ToString());
    }



    void bindingList_BeforeRemove(Form1.myInt deletedItem)
    {
        MessageBox.Show("You've just deleted item with value " + deletedItem.myIntProp.ToString());
    }


    private void button2_Click(object sender, EventArgs e)
    {
        bindingList.Add(new myInt(13));
    }

    private void button3_Click(object sender, EventArgs e)
    {
        bindingList.RemoveAt(dataGridView1.SelectedRows[0].Index);
    }

    public class myInt : INotifyPropertyChanged
    {
        public myInt(int myIntVal)
        {
            myIntProp = myIntVal;
        }
        private int iMyInt;
        public int myIntProp {
            get
            {
                return iMyInt;
            }
            set
            {
                iMyInt = value;
                if (PropertyChanged != null)
                {
                    PropertyChanged(this, new PropertyChangedEventArgs("myIntProp"));
                }
            } 
        }
        public event PropertyChangedEventHandler PropertyChanged;
    }

    public class myBindingList<myInt> : BindingList<myInt>
    {
        protected override void SetItem(int index, myInt item)
        {
            myInt oldMyInt = this.Items[index];
            myInt newMyInt = item;

            if (myIntOldNew != null)
            {
                myIntOldNew(oldMyInt, newMyInt);
            }

            base.SetItem(index, item);
        }

        protected override void RemoveItem(int itemIndex)
        {
            myInt deletedItem = this.Items[itemIndex];

            if (BeforeRemove != null)
            {
                BeforeRemove(deletedItem);
            }

            base.RemoveItem(itemIndex);
        }

        public delegate void myIntDelegateChanged(myInt oldItem, myInt newItem);
        public event myIntDelegateChanged myIntOldNew;

        public delegate void myIntDelegate(myInt deletedItem);
        public event myIntDelegate BeforeRemove;


    }

    private void button4_Click(object sender, EventArgs e)
    {
        bindingList[dataGridView1.SelectedRows[0].Index] = new myInt(new Random().Next());
    }
}
}

答案 1 :(得分:6)

解决此问题的另一种方法是使用BindingList包装ObservableCollection。这段代码适合我 -

    public void X()
    {
        ObservableCollection<object> oc = new ObservableCollection<object>();
        BindingList<object> bl = new BindingList<object>(oc);
        oc.CollectionChanged += oc_CollectionChanged;
        bl.Add(new object());
        bl.RemoveAt(0);
    }

    void oc_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
    {
        if (e.Action == NotifyCollectionChangedAction.Remove)
        {
            foreach (object o in e.OldItems)
            {
                //o was deleted
            }
        }
    }

答案 2 :(得分:3)

这是一个非常古老的8年问题,微软并不想解决这个问题(我猜这是因为回归风险的原因)。这是它的连接链接:ListChangedType.ItemDeleted is useless because ListChangedEventArgs.NewIndex is already gone

提出了各种解决方法。 If-Zen(2013/12/28)的最后一篇看起来相当不错,我在这里引用一个稍微修改过的版本:

public class MyBindingList<T> : BindingList<T>
{
    public MyBindingList()
    {
    }

    public MyBindingList(IList<T> list)
        : base(list)
    {
    }

    // TODO: add other constructors

    protected override void RemoveItem(int index)
    {
        // NOTE: we could check if index is valid here before sending the event, this is arguable...
        OnListChanged(new ListChangedEventArgsWithRemovedItem<T>(this[index], index));

        // remove item without any duplicate event
        bool b = RaiseListChangedEvents;
        RaiseListChangedEvents = false;
        try
        {
            base.RemoveItem(index);
        }
        finally
        {
            RaiseListChangedEvents = b;
        }
    }
}

public class ListChangedEventArgsWithRemovedItem : ListChangedEventArgs
{
    public ListChangedEventArgsWithRemovedItem(object item, int index)
        : base(ListChangedType.ItemDeleted, index, index)
    {
        if (item == null)
            throw new ArgumentNullException("item");

        Item = item;
    }

    public virtual object Item { get; protected set; }
}

public class ListChangedEventArgsWithRemovedItem<T> : ListChangedEventArgsWithRemovedItem
{
    public ListChangedEventArgsWithRemovedItem(T item, int index)
        : base(item, index)
    {
    }

    public override object Item { get { return (T)base.Item; } protected set { base.Item = value; } }
}

答案 3 :(得分:1)

实际上,删除发生在事件触发之前。因此,您无法访问要删除的项目。 你肯定需要一些额外的逻辑 但是,您可以从BindingList继承,并覆盖RemoveItem:

    public class RemoveAndBind<T> : BindingList<T>
    {
         protected override void RemoveItem(int index)
         {
            if (FireBeforeRemove != null)
             FireBeforeRemove(this,new ListChangedEventArgs(ListChangedType.ItemDeleted, index));
            base.RemoveItem(index);
         }

        public event EventHandler<ListChangedEventArgs> FireBeforeRemove;
    }

复制BindingList构造函数。不要让它可以取消以避免误解。你也可以在这里找到一些帮助: http://connect.microsoft.com/VisualStudio/feedback/details/148506/listchangedtype-itemdeleted-is-useless-because-listchangedeventargs-newindex-is-already-gone

希望这有帮助。

答案 4 :(得分:0)

在特定情况下,您将此BindingListDataGridView一起使用,则可以使用数据网格中的UserDeletingRow事件,其中:

private void myGrid_UserDeletingRow(object sender, DataGridViewRowCancelEventArgs e)
{
    ItemType DeletedItem = (ItemType)e.Row.DataBoundItem;

    //if you want to cancel deletion
    e.Cancel = true;
}