我在我的应用程序中使用了绑定列表以及ItemChanged事件。
我有什么方法可以知道ItemChanged事件中以前的属性值。目前,我正在添加一个名为“OldValue”的独立属性来实现此目的。
有没有办法知道项目更改事件中已删除的项目。我无法找到任何方法来知道哪个项目已从列表中删除。
答案 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从登记列表中删除项目
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());
}
更新了代码(需要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)
在特定情况下,您将此BindingList
与DataGridView
一起使用,则可以使用数据网格中的UserDeletingRow
事件,其中:
private void myGrid_UserDeletingRow(object sender, DataGridViewRowCancelEventArgs e)
{
ItemType DeletedItem = (ItemType)e.Row.DataBoundItem;
//if you want to cancel deletion
e.Cancel = true;
}