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

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

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

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

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

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

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

 public class myBindingList : BindingList { 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(); 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从项目列表中删除项目

在删除之前

删除后

删除

 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 bindingList; public Form1() { InitializeComponent(); } private void button1_Click(object sender, EventArgs e) { bindingList = new myBindingList(); 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 : BindingList { 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; } } } 

向评论致敬

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

要查看项的旧值,可以覆盖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()); } 

之前事件提出变

更新的代码(需要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 bindingList; public Form1() { InitializeComponent(); } private void button1_Click(object sender, EventArgs e) { bindingList = new myBindingList(); 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 : BindingList { 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()); } } } 

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

  public void X() { ObservableCollection oc = new ObservableCollection(); BindingList bl = new BindingList(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 } } } 

这是一个非常古老的8年问题,微软不想修复(我猜这是因为回归风险的原因)。 这是它的连接链接: ListChangedType.ItemDeleted没用,因为ListChangedEventArgs.NewIndex已经消失了

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

 public class MyBindingList : BindingList { public MyBindingList() { } public MyBindingList(IList 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(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 : ListChangedEventArgsWithRemovedItem { public ListChangedEventArgsWithRemovedItem(T item, int index) : base(item, index) { } public override object Item { get { return (T)base.Item; } protected set { base.Item = value; } } } 

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

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

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

希望这可以帮助。

在具体情况下,您将此BindingListDataGridView一起使用,您可以使用来自datagrid的UserDeletingRow事件,其中:

 private void myGrid_UserDeletingRow(object sender, DataGridViewRowCancelEventArgs e) { ItemType DeletedItem = (ItemType)e.Row.DataBoundItem; //if you want to cancel deletion e.Cancel = true; }