CommandManager.InvalidateRequerySuggested不触发RequerySuggested。

4

我正在尝试测试一个使用CommandManager.RequerySuggested的类,并注意到从我的测试中调用CommandManager.InvalidateRequerySuggested不会触发RequerySuggested。这是有原因的吗?如何解决这个问题?CommandManager是否需要一些初始化?

复现问题的代码:

[Test]
public void InvalidateRequerySuggested_TriggersRequerySuggested()
{
    bool triggered = false;
    CommandManager.RequerySuggested += (s, a) => triggered = true;

    CommandManager.InvalidateRequerySuggested();
    Thread.Sleep(1000); // Just to be sure

    Assert.True(triggered); // Never true
}

尝试将测试转换为异步测试,我相信你可能需要将控制权返回给“同步上下文”,否则如果你有1000个RequerySuggested订阅者和1000个InvalidateRequerySuggested,那么你会有很多工作要做。 - Aron
3个回答

3
如MSDN所述,在这里的注释中,CommandManager.RequerySuggested只持有弱事件引用。在您的单元测试中,lambda表达式可能被垃圾回收。请尝试以下操作:
bool triggered;
EventHandler handler = (s, e) => triggered = true;
CommandManager.RequerySuggested += handler;
CommandManager.InvalidateRequerySuggested();
GC.KeepAlive(handler);
Assert.IsTrue(triggered);

更新

经过进一步调查,我相信我已经找到了问题的根源。

CommandManager.InvalidateRequestSuggested() 使用当前分派程序异步引发事件。

这里是一个解决方案:

bool triggered;
EventHandler handler = (s, e) => triggered = true;
CommandManager.RequerySuggested += handler;
CommandManager.InvalidateRequerySuggested();

// Ensure the invalidate is processed
Dispatcher.CurrentDispatcher.Invoke(DispatcherPriority.Background, new Action(() => { }));

GC.KeepAlive(handler);
Assert.IsTrue(triggered);

测试仍然失败。只要handler != nullKeepAlive就是不必要的 - 对吗? - larsmoa
KeepAlive 确保垃圾回收器不会回收 handler。我上面粘贴的内容最终肯定会成为一个问题(尤其是在内存紧张的系统上)。不过你的测试失败也可能是其他原因造成的。稍后我会仔细查看 :) - Lukazoid
那么 EventHandler handler 不会对 (s, e) => triggered = true 保持强引用? - larsmoa
1
它确实可以,但垃圾回收器能够收集不再使用或引用的局部变量。因此,在这种情况下,在CommandManager.RequerySuggested += handler;行之后,handler不再使用,因此也可以进行垃圾回收,因此lambda表达式也可以被回收。 - Lukazoid
1
点击这里查看另一种解释。如果是在调试编译中,没有进行优化,那么这不是一个问题。 - Lukazoid
显示剩余2条评论

1
另一个可能导致这种行为的原因是:我发现我们需要使用与调用InvalidateRequerySuggested相同的Dispatcher订阅RequerySuggested事件。

我在非UI线程上创建了一些对象来订阅此事件,但该事件未被触发。需更改。

CommandManager.RequerySuggested += HandleRequerySuggestedSuggested;

Application.Current.Dispatcher.Invoke((Action)(() => 
    CommandManager.RequerySuggested += HandleRequerySuggestedSuggested));

解决了我的问题。


0

只需保持对事件处理程序的强引用即可


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接