ObservableCollection не замечает, когда элемент в нем изменяется (даже с INotifyPropertyChanged) - PullRequest
152 голосов
/ 15 сентября 2009

Кто-нибудь знает, почему этот код не работает:

public class CollectionViewModel : ViewModelBase {  
    public ObservableCollection<EntityViewModel> ContentList
    {
        get { return _contentList; }
        set 
        { 
            _contentList = value; 
            RaisePropertyChanged("ContentList"); 
            //I want to be notified here when something changes..?
            //debugger doesn't stop here when IsRowChecked is toggled
        }
     }
}

public class EntityViewModel : ViewModelBase
{

    private bool _isRowChecked;

    public bool IsRowChecked
    {
        get { return _isRowChecked; }
        set { _isRowChecked = value; RaisePropertyChanged("IsRowChecked"); }
    }
}

ViewModelBase содержит все для RaisePropertyChanged и т. Д. И работает для всего остального, кроме этой проблемы.

Ответы [ 18 ]

1 голос
/ 18 сентября 2014

Я пробую это решение, но оно работает только для меня, как RaisePropertyChange ("SourceGroupeGridView") при изменении коллекции, которая запускается для каждого добавления или изменения элемента.

Проблема в:

public void EntityViewModelPropertyChanged(object sender, PropertyChangedEventArgs e)
{
     NotifyCollectionChangedEventArgs args = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset);
    OnCollectionChanged(args);
}

NotifyCollectionChangedAction. Сбросьте это действие, чтобы выполнить полное повторное связывание всех элементов в groupedgrid, эквивалентно в RaisePropertyChanged. При его использовании все группы gridview обновляются.

ЕСЛИ вам нужно только обновить в пользовательском интерфейсе группу нового элемента, вы не используете действие «Сбросить», вам нужно будет смоделировать действие «Добавить» в itemproperty примерно так:

void item_PropertyChanged(object sender, PropertyChangedEventArgs e)
{         
    var index = this.IndexOf((T)sender);

    this.RemoveAt(index);
    this.Insert(index, (T)sender);

    var a = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, sender);
    OnCollectionChanged(a);
}

Извините за мой английский, и спасибо за базовый код :), Я надеюсь, что это поможет кому-то ^ _ ^

Enjoi !!

1 голос
/ 15 марта 2014

Простое решение для стандартной наблюдаемой коллекции, которое я использовал:

НЕ ДОБАВЛЯЙТЕ К ВАШЕМУ ИМУЩЕСТВУ ИЛИ ИЗМЕНЯЙТЕ его внутренние предметы ПРЯМО, вместо этого создайте некоторую временную коллекцию как это

ObservableCollection<EntityViewModel> tmpList= new ObservableCollection<EntityViewModel>();

и добавлять элементы или вносить изменения в tmpList,

tmpList.Add(new EntityViewModel(){IsRowChecked=false}); //Example
tmpList[0].IsRowChecked= true; //Example
...

, затем передайте его фактическому имуществу по назначению.

ContentList=tmpList;

это изменит целое свойство, что приведет к уведомлению INotifyPropertyChanged, как вам нужно.

1 голос
/ 13 января 2017

Вместо ObservableCollection или TrulyObservableCollection рассмотрите возможность использования BindingList и вызова метода ResetBindings.

Например:

private BindingList<TfsFile> _tfsFiles;

public BindingList<TfsFile> TfsFiles
{
    get { return _tfsFiles; }
    set
    {
        _tfsFiles = value;
        NotifyPropertyChanged();
    }
}

При наличии события, например щелчка, ваш код будет выглядеть следующим образом:

foreach (var file in TfsFiles)
{
    SelectedFile = file;
    file.Name = "Different Text";
    TfsFiles.ResetBindings();
}

Моя модель выглядела так:

namespace Models
{
    public class TfsFile 
    {
        public string ImagePath { get; set; }

        public string FullPath { get; set; }

        public string Name { get; set; }

        public string Text { get; set; }

    }
}
1 голос
/ 07 августа 2014

Вот метод расширения для вышеуказанного решения ...

public static TrulyObservableCollection<T> ToTrulyObservableCollection<T>(this List<T> list)
     where T : INotifyPropertyChanged
{
    var newList = new TrulyObservableCollection<T>();

    if (list != null)
    {
        list.ForEach(o => newList.Add(o));
    }

    return newList;
}  
0 голосов
/ 17 апреля 2019

Для запуска OnChange в списке наблюдаемых коллекций

  1. Получить индекс выбранного элемента
  2. Удалить предмет из списка родителей
  3. Добавить элемент с тем же индексом в родительском

Пример:

int index = NotificationDetails.IndexOf(notificationDetails);
NotificationDetails.Remove(notificationDetails);
NotificationDetails.Insert(index, notificationDetails);
0 голосов
/ 01 апреля 2018

Простое решение в 2 строки кода. Просто используйте конструктор копирования. Нет необходимости писать TrulyObservableCollection и т. Д.

Пример:

        speakers.list[0].Status = "offline";
        speakers.list[0] = new Speaker(speakers.list[0]);

Еще один метод без конструктора копирования. Вы можете использовать сериализацию.

        speakers.list[0].Status = "offline";
        //speakers.list[0] = new Speaker(speakers.list[0]);
        var tmp  = JsonConvert.SerializeObject(speakers.list[0]);
        var tmp2 = JsonConvert.DeserializeObject<Speaker>(tmp);
        speakers.list[0] = tmp2;
0 голосов
/ 08 апреля 2019

Вы также можете использовать этот метод расширения, чтобы легко зарегистрировать обработчик для изменения свойства элемента в соответствующих коллекциях. Этот метод автоматически добавляется ко всем коллекциям, реализующим INotifyCollectionChanged, которые содержат элементы, реализующие INotifyPropertyChanged:

public static class ObservableCollectionEx
{
    public static void SetOnCollectionItemPropertyChanged<T>(this T _this, PropertyChangedEventHandler handler)
        where T : INotifyCollectionChanged, ICollection<INotifyPropertyChanged> 
    {
        _this.CollectionChanged += (sender,e)=> {
            if (e.NewItems != null)
            {
                foreach (Object item in e.NewItems)
                {
                    ((INotifyPropertyChanged)item).PropertyChanged += handler;
                }
            }
            if (e.OldItems != null)
            {
                foreach (Object item in e.OldItems)
                {
                    ((INotifyPropertyChanged)item).PropertyChanged -= handler;
                }
            }
        };
    }
}

Как использовать:

public class Test
{
    public static void MyExtensionTest()
    {
        ObservableCollection<INotifyPropertyChanged> c = new ObservableCollection<INotifyPropertyChanged>();
        c.SetOnCollectionItemPropertyChanged((item, e) =>
        {
             //whatever you want to do on item change
        });
    }
}
0 голосов
/ 16 апреля 2015

Вот мой вариант реализации. Он проверяет и выдает ошибку, если объекты в списке не реализуют INotifyPropertyChanged, поэтому не могут забыть эту проблему при разработке. Снаружи вы используете событие ListItemChanged, чтобы определить, изменился ли список или сам элемент списка.

public class SpecialObservableCollection<T> : ObservableCollection<T>
{
    public SpecialObservableCollection()
    {
        this.CollectionChanged += OnCollectionChanged;
    }

    void OnCollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
    {
        AddOrRemoveListToPropertyChanged(e.NewItems,true); 
        AddOrRemoveListToPropertyChanged(e.OldItems,false); 
    }

    private void AddOrRemoveListToPropertyChanged(IList list, Boolean add)
    {
        if (list == null) { return; }
        foreach (object item in list)
        {
            INotifyPropertyChanged o = item as INotifyPropertyChanged;
            if (o != null)
            {
                if (add)  { o.PropertyChanged += ListItemPropertyChanged; }
                if (!add) { o.PropertyChanged -= ListItemPropertyChanged; }
            }
            else
            {
                throw new Exception("INotifyPropertyChanged is required");
            }
        }
    }

    void ListItemPropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        OnListItemChanged(this, e);
    }

    public delegate void ListItemChangedEventHandler(object sender, PropertyChangedEventArgs e);

    public event ListItemChangedEventHandler ListItemChanged;

    private void OnListItemChanged(Object sender, PropertyChangedEventArgs e)
    {
        if (ListItemChanged != null) { this.ListItemChanged(this, e); }
    }


}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...