RelayCommand перестает работать через некоторое время

Я сталкиваюсь с некоторыми проблемами с помощью GalaSoft RelayCommand.

У меня есть свойство NextCommand, которое работает, но только несколько раз.

Затем он перестает работать полностью.

Вы можете попробовать это с помощью образца проекта:

http://s000.tinyupload.com/?file_id=65828891881629261404

Поведение выглядит следующим образом:

  • NextCommand:
    • выводит все элементы до активного индекса
    • если осталось менее 50 элементов, толкает 1 новый элемент
    • отмечает новый элемент как активный
  • BackCommand:
    • перемещает активный индекс назад на 1 позицию.

Шаги для репликации:

  • Ключ '+' (OemPlus) привязан к NextCommand
  • Ключ '-' (OemMinus) привязан к BackCommand
  • Удерживайте клавишу "+", пока список не перестанет расти (ограничение 50 единиц)
  • Удерживайте клавишу "-", пока первый элемент в списке не будет активным.
  • Повторите

Количество повторений, необходимых (для репликации ошибки), несовместимо.

Иногда я получаю его после 4 повторений; в других случаях до 9.

enter image description here

// Items Collection
public class ItemCollection : ViewModelBase
{
    // List of Items
    private readonly ObservableCollection<Item> _items = new ObservableCollection<Item>();
    public ObservableCollection<Item> Items
    {
        get { return _items; }
    }

    // Constructor
    public ItemCollection()
    {
        BackCommand = new RelayCommand(
                () =>
                {
                    // Go to previous page
                    var index = Items.IndexOf(ActiveItem);
                    if (index > 0)
                    {
                        ActiveItem = Items[index - 1];
                    }
                },
                () => ActiveItem != null && Items.IndexOf(ActiveItem) > 0);
    }

    // Back command
    public RelayCommand BackCommand { get; set; }

    // Next command
    public RelayCommand NextCommand { get; set; }

    // The currently-active item
    private Item _activeItem;
    public Item ActiveItem
    {
        get { return _activeItem; }
        set
        {
            Set(() => ActiveItem, ref _activeItem, value);
        }
    }
}

// Item
public class Item : ViewModelBase
{
    public string Title { get; set; }
}

Когда я вошел в код RelayCommand, флаг выполнения isAlive выполнялся как false. Но я не могу понять, как это может произойти.

Ответ 1

Два слова: Сборщик мусора

В вашем примере проекта, в котором вы должны разместить соответствующие биты, чтобы сделать свой вопрос надежным, вы устанавливаете DataContext в своем окне следующим образом:

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();

        var logic  = new LogicObject();
        DataContext = logic.Collection;
    }
}

Поскольку ничто иное не сохраняет ссылку на созданный здесь LogicObject, он будет собран при следующей возможности.

Команда перестает функционировать, потому что в LogicObject вы устанавливаете NextCommand для ItemCollection для использования закрытых членов скопированного времени LogicObject:

public class LogicObject
{
    public LogicObject()
    {
        Collection = new ItemCollection();
        Collection.NextCommand = new RelayCommand(AddItem, CanAddItem);
        AddItem();
    }

    private bool CanAddItem()
    {
        // snip...
    }

    private void AddItem()
    {
        // snip...
    }
}

Как только LogicObject будет собрано, команда перестанет работать, потому что у нее больше нет ссылок на допустимые методы (AddItem и CanAddItem). Вот почему поле isAlive на обеих слабых ссылках RelayCommand на методы ложно.

Вы можете исправить это, просто навешив на LogicObject или переместив методы AddItem и CanAddItem в коллекцию.


Чтобы получить в духе GIF для этого вопроса, вот тот, который показывает, что кнопка перестает работать, как только происходит сборка Gen 0.

Desktop capture showing button failing when GC occurs

Ответ 2

почему вы просто не используете методы из ICollectionView? там у вас есть:

  • MoveCurrentTo
  • MoveCurrentToFirst
  • MoveCurrentToLast
  • MoveCurrentToNext
  • MoveCurrentToPrevious
  • и другие приятные вещи.

что-то вроде этого

 private ICollectionView MyView {get;set;}


 this.MyView = CollectionViewSource.GetDefaultView(this._items);


 if (!this.MyView.IsCurrentBeforeFirst)
 {
     this.MyView.MoveCurrentToPrevious();
 }