c# inotifypropertychanged bindinglist

c# - Obtener artículo eliminado en el evento ItemChanging de BindingList



inotifypropertychanged (5)

Estoy utilizando la lista de enlaces en mi aplicación junto con el evento ItemChanged.

¿Hay alguna manera de que pueda saber los valores anteriores de las propiedades en el evento ItemChanged. Actualmente estoy agregando una propiedad separada llamada ''OldValue'' para lograr esto.

¿Hay alguna manera de saber sobre el elemento eliminado en el evento de cambio de elemento. No puedo encontrar ninguna forma de saber qué elemento se ha eliminado de la lista.


En el caso específico que está utilizando esta lista de BindingList con un DataGridView , puede usar el evento UserDeletingRow de la cuadrícula de datos, donde:

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


En realidad, la eliminación se produce antes de que se dispare el evento. Por lo tanto, no puede llegar al elemento que se está eliminando. Definitivamente necesitas algo de lógica adicional para eso. Sin embargo, puedes heredar de BindingList y anular 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; }

Replicar los constructores BindingList. No lo haga cancelable para evitar conceptos erróneos. También puede encontrar ayuda aquí: http://connect.microsoft.com/VisualStudio/feedback/details/148506/listchangedtype-itemdeleted-is-useless-because-listchangedeventargs-newindex-is-already-gone

Espero que esto ayude.


Este es un problema muy antiguo de 8 años que Microsoft no quiere arreglar (supongo que por razones de riesgo de regresión). Aquí está el enlace de conexión: ListChangedType.ItemDeleted es inútil porque ListChangedEventArgs.NewIndex ya se ha ido

Hay varias soluciones propuestas. El último de If-Zen (2013/12/28) parece bastante decente, lo cito aquí con una versión ligeramente modificada:

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


Si entiendo correctamente, desea obtener información sobre el elemento que se eliminó de la lista de enlace.

Creo que la forma más fácil de hacerlo es crear su propia lista de enlaces que se deriva de la lista de enlaces.

En el interior, tendrá la invalidación del método RemoveItem, por lo que ANTES de eliminar un elemento de la lista de enlaces, podrá disparar un evento que contenga un elemento que se eliminará.

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

Por ejemplo, creé el tipo myInt implementando INotifyPropertyChanged: la interfaz es solo para actualizar dataGridView después de agregar / eliminar elementos de la lista de enlaces.

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

Estoy inicializando la lista de enlaces con ints (myInts para ser precisos), luego estoy enlazando la lista con dataGridView (para fines de presentación) y suscribiéndome a mi evento 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;

Si se generó el evento BeforeRemove tengo un elemento que se eliminó

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

A continuación se muestra un código de ejemplo completo (soltar 3 botones y dataGridView en el formulario): el botón 1 inicializa la lista de enlaces, el botón 2 agrega el elemento a la lista, el botón 3 elimina el elemento de la lista de ofertas

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

ASNWER AL COMENTARIO

"La otra parte de la pregunta es => ¿Hay alguna manera de saber el valor antiguo del elemento que se modificó en la lista? En ListChangedEvent no comparte nada"

Para ver el valor antiguo del elemento, puede anular el método 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); }

Se dispara cuando se cambia el objeto en el índice especificado, como este

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

La parte difícil es, si intenta modificar la propiedad del artículo directamente

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

SetItem no se disparará , tiene que ser un objeto entero reemplazado.

Así que necesitaremos otro delegado y evento para manejar esto

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

Entonces podemos suscribirnos a este

bindingList.myIntOldNew += bindingList_myIntOldNew;

y manejarlo

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

Código actualizado (se requieren 4 botones, la 4ª modifica el elemento seleccionado)

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


Un enfoque alternativo a este problema es envolver un ObservableCollection con un BindingList. Este código funciona para mí.

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