我在我的应用程序中使用了Binding List和ItemChanged事件。
有没有办法在ItemChanged事件中知道属性的先前值。目前,我正在添加一个名为“OldValue”的单独属性来实现此操作。
是否有任何方法可以在项更改事件中知道已删除的项。我无法找到任何方法来知道从列表中删除了哪个项。
我在我的应用程序中使用了Binding List和ItemChanged事件。
有没有办法在ItemChanged事件中知道属性的先前值。目前,我正在添加一个名为“OldValue”的单独属性来实现此操作。
是否有任何方法可以在项更改事件中知道已删除的项。我无法找到任何方法来知道从列表中删除了哪个项。
如果我理解正确,您想获取有关从绑定列表中删除的项目的信息。
我认为最简单的方法是创建自己的绑定列表,它派生自绑定列表。
在其中,您将覆盖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;
}
我用整数(准确地说是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从绑定列表中删除项目
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;
}
}
}
回复评论的答案
“问题的另一部分是=>有没有办法知道在列表中被更改的项目的旧值?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个按钮,第四个修改所选项目)
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());
}
}
}
这个问题的另一种解决方法是用 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
}
}
}
BindingListWithCollectionChanged(ObservableCollection oc) : base(oc)
{
oc.CollectionChanged += (s, e) => CollectionChanged?.Invoke(s, e);
}
public event EventHandler CollectionChanged;
}`。
- Ruben Bartelinkpublic 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; } }
}
BindingList
与DataGridView
的特定情况下,您可以使用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;
}
实际上,删除操作发生在事件触发之前。因此,您无法获取要删除的项。 您肯定需要一些额外的逻辑来处理这个问题。 但是,您可以继承自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;
}
protected override void RemoveItem(int index)
{
Removing?.Invoke(this, new ListChangedEventArgs(ListChangedType.ItemDeleted, index));
base.RemoveItem(index);
}
public event EventHandler Removing;
}`
- Ruben Bartelink