Можно ли откатить изменения коллекции при событии изменения коллекции?

У меня есть 2 представления списка... и добавлять/удалять кнопки между ними.

В событии изменения коллекции коллекции списка-представления в модели представления могу ли я отменить изменения для определенного условия?


person Relativity    schedule 25.12.2010    source источник


Ответы (3)


Вы можете обработать событие CollectionChanged ObservableCollection для резервного копирования (через или что-то еще) старые значения (см. NotifyCollectionChangedEventArgs.OldItems свойство) и возвращать их при необходимости, например, когда пользователь нажимает «Отменить» и т. д.

Обновление В отношении комментариев ниже:

Если вы хотите откатить коллекцию из обработчика событий CollectionChanged, создайте флаг, в котором вы избегаете обработчика рекурсивного вызова (не тестировалось с многопоточным приложением), вот простой пример, вы можете легко настроить его на подходит для вашего V/VM.

private void Window_Loaded(object sender, RoutedEventArgs e)
{
  var x = new ObservableCollection<string>();
  x.CollectionChanged += 
    new NotifyCollectionChangedEventHandler(x_CollectionChanged);
  x.Add("asdf");
  x.Remove("asdf");
}

bool rollingBack = false;
void x_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
  if (rollingBack) return;

  if (e.Action == NotifyCollectionChangedAction.Remove)
  {
    if (e.OldItems.Contains("asdf"))
    {
      var oc = (ObservableCollection<string>)sender;
      rollingBack = true;
      oc.Add("asdf");
      rollingBack = false;
    }
  }
}
person Shimmy Weitzhandler    schedule 25.12.2010
comment
Один вопрос => если я назначу e.OldItems своей коллекции, не вызовет ли это снова событие изменения коллекции? - person Relativity; 25.12.2010
comment
Вы не можете назначить e.OldItems, это 1) свойство только для чтения, 2) оно возвращает ReadOnlyList. - person Shimmy Weitzhandler; 25.12.2010
comment
Я имею в виду... Моя наблюдаемая коллекция = e.oldItems.. Могу ли я изменить ReadOnlyList на наблюдаемую коллекцию? - person Relativity; 25.12.2010
comment
Нет, но вы можете просто прочитать/заменить старые элементы в наблюдаемой коллекции, не забывайте, что аргумент sender, переданный обработчику CollectionChanged, является самим ObservableCollection. - person Shimmy Weitzhandler; 25.12.2010
comment
например var x = (ObservableCollection<object>)sender; (ваш ObservableCollection и его тип), затем foreach (var item in e.OldItems) x.Add(item); - person Shimmy Weitzhandler; 25.12.2010
comment
Так что это не вызовет событие изменения коллекции снова, верно? - person Relativity; 25.12.2010
comment
Это, конечно, будет, и это обновит ваш пользовательский интерфейс, но вы в любом случае не собираетесь выполнять откат из обработчика CollectionChanged, этот обработчик следует использовать для извлечения и резервного копирования измененных данных, поэтому, когда пользователь нажимает «Отменить», вы имейте это прямо там. Если вы хотите сделать откат из обработчика, просто создайте флаг, который обработчик должен экранировать, когда он один, просмотрите мой обновленный ответ. - person Shimmy Weitzhandler; 25.12.2010
comment
Как вы заставили это работать? Когда я пытаюсь это сделать, я получаю InvalidOperationException Cannot change ObservableCollection во время события CollectionChanged. - Я пытаюсь разрешить пользователю отменить удаление строки в сетке данных. - person Tod; 24.03.2012
comment
@ Тод, можешь отладить его и посмотреть, где именно находится источник твоего исключения? - person Shimmy Weitzhandler; 24.03.2012
comment
Прямо на .Add, где я пытаюсь вернуть элемент в коллекцию. - person Tod; 25.03.2012
comment
@ Тод, я давно не занимался этой проблемой, я не могу вспомнить, что происходит. Надеюсь, что кто-то из вышеперечисленных людей сможет помочь. Жаль это не я :( - person Shimmy Weitzhandler; 25.03.2012
comment
В любом случае, спасибо, я решил пойти другим путем. - person Tod; 26.03.2012

Учитывая, что вы получаете отправителя события как объект (т.е. первый параметр события) и список объектов, которые были изменены, да, вы можете это сделать. Хотя я бы этого не советовал. Если вы столкнулись с таким условием, предоставьте метод ViewModel, который предоставляется с EventArgs, и позвольте ему сделать свою работу. Представление не место для логики.

Еще лучше: проверьте условие в самой ViewModel (т.е. в командах, отвечающих за добавление/удаление)! Модель представления отвечает за состояние информации, поэтому держите там свою логику. Представление предназначено только для отображения данных.

person Femaref    schedule 25.12.2010
comment
Один вопрос => если я назначу e.OldItems своей коллекции, не вызовет ли это снова событие изменения коллекции? - person Relativity; 25.12.2010

Ответ Шимми не сработал для меня в приложении Магазина Windows, вы все равно столкнетесь с проблемами повторного входа и получите InvalidOperationException сообщение «Невозможно изменить ObservableCollection во время события CollectionChanged».

Мне пришлось использовать диспетчер пользовательского интерфейса и отключить/включить обработчик событий, чтобы избежать этих проблем.

Имейте в виду: это взлом, и разработчики фреймворка сделали все возможное, чтобы помешать вам это сделать. Так что, если вы хотите проигнорировать их предупреждение, будьте осторожны, чтобы не выстрелить себе в ногу.

Items.CollectionChanged += ItemsChanged;

private async void ItemsChanged(object sender, NotifyCollectionChangedEventArgs e)
{
    if(condition)
    {
        //rollback
        await CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(
            CoreDispatcherPriority.Normal, () => {

                //disable/enable event handler
                Items.CollectionChanged -= ItemsChanged;

                Items.Remove(e.NewItems[0]);

                Items.CollectionChanged += ItemsChanged;
            })).AsTask();
    }
}

Это позволит избежать исключения, избежать рекурсивного вызова обработчика и правильно обновить пользовательский интерфейс.

person dcastro    schedule 11.05.2015
comment
и принятый ответ также не работает в настольном приложении wpf. Посмотрите также мой сообщение. - person ; 01.03.2017