简体   繁体   English

一段时间后,RelayCommand停止工作

[英]RelayCommand stops working after a while

I am facing some problems using GalaSoft's RelayCommand. 我使用GalaSoft的RelayCommand遇到了一些问题。

I have a NextCommand property that works, but only several times. 我有一个可以工作的NextCommand属性,但只有几次。

Afterwards, it stops working completely. 之后,它完全停止工作。

You can try this out with the sample project: 您可以使用示例项目尝试此操作:

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

The behaviour is as follows: 行为如下:

  1. NextCommand : NextCommand
    1. pops all items until the active index 弹出所有项目直到活动索引
    2. if there are less than 50 items left, pushes 1 new item 如果剩下少于50个项目,则推送1个新项目
    3. marks new item as active 将新项目标记为活动
  2. BackCommand : BackCommand
    1. moves the active index back by 1 position 将活动索引向后移动1个位置

Steps to replicate: 复制步骤:

  1. the '+' (OemPlus) key has been bound to NextCommand '+'(OemPlus)键已绑定到NextCommand
  2. the '-' (OemMinus) key has been bound to BackCommand ' - '(OemMinus)键已绑定到BackCommand
  3. Hold the '+' key until the list stops growing (50 items limit) 按住“+”键直到列表停止增长(限制50个项目)
  4. Hold the '-' key until the first item in the list is the active 按住“ - ”键,直到列表中的第一项处于活动状态
  5. Repeat 重复

The number of repetitions needed (to replicate the bug) is inconsistent. 所需的重复次数(复制错误)是不一致的。

Sometimes I get it after 4 repetitions; 有时我会在重复4次后得到它; other times up till 9. 其他时间到9。

在此输入图像描述

// 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; }
}

When I stepped into the RelayCommand's code, the execute action's isAlive flag was false. 当我进入RelayCommand的代码时,执行操作的isAlive标志为false。 But I can't seem to figure out how that might happen. 但我似乎无法弄清楚这可能会如何发生。

Two words: Garbage Collector 两个字: 垃圾收集器

In your example project--which you should post the relevant bits of to make your question future-proof--you set the DataContext on your window like this: 在您的示例项目中 - 您应该发布相关的部分以使您的问题面向未来 - 您在窗口上设置DataContext ,如下所示:

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

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

Because nothing else retains a reference to the LogicObject created here, it will be collected at the next opportunity. 因为没有别的东西保留对这里创建的LogicObject的引用,所以它将在下一次机会时收集。

The command stops functioning because in LogicObject , you set the NextCommand of the ItemCollection to use private members of the soon-to-be-collected LogicObject : 该命令停止运行,因为在LogicObject ,您将ItemCollectionNextCommand设置为使用即将收集的LogicObject私有成员:

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

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

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

Once LogicObject is collected, the command can no longer work because it no longer has references to valid methods ( AddItem and CanAddItem ). 收集LogicObject ,该命令将无法再运行,因为它不再引用有效方法( AddItemCanAddItem )。 This is why the isAlive field on both of the RelayCommand 's weak references to the methods is false. 这就是为什么RelayCommand对这些方法的弱引用的isAlive字段都是错误的。

You can fix this by just hanging on to the LogicObject , or by moving the AddItem and CanAddItem methods into the collection. 您可以通过挂起LogicObject或将AddItemCanAddItem方法移动到集合中来解决此问题。


To get in the spirit of GIFs for this question, here's one that shows the button stop working as soon as a Gen 0 collection occurs. 为了获得这个问题的GIF精神,这里有一个显示按钮在Gen 0收集发生时立即停止工作的问题。

GC发生时桌面捕获显示按钮失败

why dont you simply use the methods from ICollectionView? 为什么不简单地使用ICollectionView中的方法? there you have: 你有:

  • MoveCurrentTo MoveCurrentTo
  • MoveCurrentToFirst MoveCurrentToFirst
  • MoveCurrentToLast MoveCurrentToLast
  • MoveCurrentToNext MoveCurrentToNext
  • MoveCurrentToPrevious MoveCurrentToPrevious
  • and other nice stuff 和其他好东西

something like this 这样的事情

 private ICollectionView MyView {get;set;}


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


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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM