mvvmlight galasoft c# wpf mvvm mvvm-light

c# - galasoft - Implementando CollectionChanged



nuget mvvmlight (8)

He agregado el CollectionChanged eventhandler(onCollectionChanged) a una de las propiedades de ObservableCollection .

Descubrí que el método onCollectionChanged se invoca solo en caso de agregar elementos o eliminar elementos en la colección, pero no en el caso de que el elemento de colección se edite.

Me gustaría saber cómo enviar la lista / colección de elementos recién agregados, eliminados y editados en una sola colección.

Gracias.


Un ItemsControl escucha a CollectionChanged para administrar la visualización de la colección de elementos que presenta en la pantalla. Un ContentControl escucha PropertyChanged para administrar la visualización del elemento específico que está presentando en la pantalla. Es muy fácil mantener los dos conceptos separados en tu mente una vez que comprendas esto.

El seguimiento de si un elemento se edita no es algo que haga cualquiera de estas interfaces. Los cambios de propiedad no son ediciones, es decir, no representan necesariamente algún tipo de cambio iniciado por el usuario en el estado del objeto. Por ejemplo, un objeto puede tener una propiedad ElapsedTime que se actualiza continuamente mediante un temporizador; la IU necesita ser notificada de estos eventos de cambio de propiedad, pero ciertamente no representan cambios en los datos subyacentes del objeto.

La forma estándar de rastrear si un objeto se edita es primero hacer que ese objeto implemente IEditableObject . Luego puede, internamente a la clase del objeto, decidir qué cambios constituyen una edición (es decir, le requieren llamar a BeginEdit ) y qué cambios no. A continuación, puede implementar una propiedad IsDirty que se establece cuando BeginEdit se llama y se borra cuando se llama a EndEdit o CancelEdit . (Realmente no entiendo por qué esa propiedad no es parte de IEditableObject ; aún no he implementado un objeto editable que no lo requiera).

Por supuesto, no hay necesidad de implementar ese segundo nivel de abstracción si no lo necesita, sin duda puede escuchar el evento PropertyChanged y simplemente asumir que el objeto ha sido editado si se plantea. Realmente depende de tus requisitos.


INotifyCollectionChanged no es uno igual con INotiftyPropertyChanged . Cambiar las propiedades de los objetos subyacentes de ninguna manera sugiere que la colección haya cambiado.

Una forma de lograr este comportamiento es crear una colección personalizada que interrogue al objeto una vez agregado y registrarse para el evento INotifyPropertyChanged.PropertyChanged ; entonces necesitaría cancelar el registro apropiadamente cuando se elimine un artículo.

Una advertencia con este enfoque es cuando sus objetos están anidados en N niveles profundos. Para resolver esto, necesitará esencialmente interrogar cada propiedad usando reflexión para determinar si es quizás otra colección que implemente INotifyCollectionChanged u otro contenedor que deba atravesarse.

Aquí hay un ejemplo rudimentario no probado ...

public class ObservableCollectionExt<T> : ObservableCollection<T> { public override event System.Collections.Specialized.NotifyCollectionChangedEventHandler CollectionChanged; protected override void SetItem(int index, T item) { base.SetItem(index, item); if(item is INotifyPropertyChanged) (item as INotifyPropertyChanged).PropertyChanged += new PropertyChangedEventHandler(OnPropertyChanged); } protected override void ClearItems() { for (int i = 0; i < this.Items.Count; i++) DeRegisterINotifyPropertyChanged(this.IndexOf(this.Items[i])); base.ClearItems(); } protected override void InsertItem(int index, T item) { base.InsertItem(index, item); RegisterINotifyPropertyChanged(item); } protected override void RemoveItem(int index) { base.RemoveItem(index); DeRegisterINotifyPropertyChanged(index); } private void RegisterINotifyPropertyChanged(T item) { if (item is INotifyPropertyChanged) (item as INotifyPropertyChanged).PropertyChanged += new PropertyChangedEventHandler(OnPropertyChanged); } private void DeRegisterINotifyPropertyChanged(int index) { if (this.Items[index] is INotifyPropertyChanged) (this.Items[index] as INotifyPropertyChanged).PropertyChanged -= OnPropertyChanged; } private void OnPropertyChanged(object sender, PropertyChangedEventArgs e) { T item = (T)sender; OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset, item)); } }


Creo que INotifyPropertyChanged el ObservableCollection con elementos que implementan INotifyPropertyChanged provocará que el evento CollectionChanged se INotifyPropertyChanged cuando un elemento genere su notificación PropertyChanged .

BindingList<T> , creo que debes usar BindingList<T> para que los cambios de elementos individuales se propaguen de esta forma desde el primer momento.

De lo contrario, deberá suscribirse manualmente a las notificaciones de cambio de cada elemento y plantear el evento CollectionChanged . Tenga en cuenta que si está creando su propio ObservableCollection<T> derivado, deberá suscribirse en la creación de instancias y en Add() e Insert() , y darse de baja en Remove() , RemoveAt() y Clear() . De lo contrario, puede suscribirse al evento CollectionChanged y usar los elementos agregados y eliminados de los argumentos del evento para suscribirse / cancelar suscripción.


en BindingList , BindingList es una práctica estándar. en WPF y Silverlight, generalmente está trabada trabajando con ObservableCollection y necesita escuchar PropertyChanged en cada elemento


Usa el siguiente código:

-mi modelo:

public class IceCream: INotifyPropertyChanged { private int liczba; public int Liczba { get { return liczba; } set { liczba = value; Zmiana("Liczba"); } } public IceCream(){} //in the same class implement the below-it will be responsible for track a changes public event PropertyChangedEventHandler PropertyChanged; private void Zmiana(string propertyName) { if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); } } }

Y en mi clase, PersonList implementa el método responsable de activar el aumento del valor de un botón después de hacer clic en AppBarControl

async private void Add_Click(object sender, RoutedEventArgs e) { List<IceCream> items = new List<IceCream>(); foreach (IceCream item in IceCreamList.SelectedItems) { int i=Flavors.IndexOf(item); Flavors[i].Liczba =item.Liczba+ 1; //Flavors.Remove(item); //item.Liczba += 1; // items.Add(item); // Flavors.Add(item); } MessageDialog d = new MessageDialog("Zwiększono liczbę o jeden"); d.Content = "Zwiększono liczbę o jeden"; await d.ShowAsync(); IceCreamList.SelectedIndex = -1; } }

Espero que sea útil que alguien tenga en cuenta que:

private ObservableCollection<IceCream> Flavors;

-


La solución más fácil que encontré para esta limitación es eliminar el elemento usando RemoveAt(index) luego agregar el ítem modificado en el mismo índice usando InsertAt(index) y así el ObservableCollection notificará a View.


Debe agregar un detector PropertyChanged a cada elemento (que debe implementar INotifyPropertyChanged ) para recibir notificaciones sobre la edición de objetos en una lista observable.

public ObservableCollection<Item> Names { get; set; } public List<Item> ModifiedItems { get; set; } public ViewModel() { this.ModifiedItems = new List<Item>(); this.Names = new ObservableCollection<Item>(); this.Names.CollectionChanged += this.OnCollectionChanged; } void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) { if (e.NewItems != null) { foreach(Item newItem in e.NewItems) { ModifiedItems.Add(newItem); //Add listener for each item on PropertyChanged event newItem.PropertyChanged += this.OnItemPropertyChanged; } } if (e.OldItems != null) { foreach(Item oldItem in e.OldItems) { ModifiedItems.Add(oldItem); oldItem.PropertyChanged -= this.OnItemPropertyChanged; } } } void OnItemPropertyChanged(object sender, PropertyChangedEventArgs e) { Item item = sender as Item; if(item != null) ModifiedItems.Add(item); }

Tal vez tenga que verificar si algún elemento ya se encuentra en la Lista de elementos modificados (con el método de la lista Contiene (objeto obj)) y solo agrega un nuevo elemento si el resultado de ese método es falso .

El Item clase debe implementar INotifyPropertyChanged . Mira este ejemplo para saber cómo. Como dijo Robert Rossney, también puedes hacer eso con IEditableObject , si tienes ese requisito.


¡Mi edición de '' esta respuesta '' es rechazada! Así que puse mi edición aquí:

void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) { if (e.NewItems != null) { foreach(Item newItem in e.NewItems) { ModifiedItems.Add(newItem); //Add listener for each item on PropertyChanged event if (e.Action == NotifyCollectionChangedAction.Add) newItem.PropertyChanged += this.ListTagInfo_PropertyChanged; else if (e.Action == NotifyCollectionChangedAction.Remove) newItem.PropertyChanged -= this.ListTagInfo_PropertyChanged; } } // MSDN: OldItems:Gets the list of items affected by a Replace, Remove, or Move action. //if (e.OldItems != null) <--- removed }