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