奇怪的问题,按钮不会重新启用,除非点击鼠标。

23
我的应用程序使用WPF中的MVVM模式编写,所有按钮都使用命令绑定来执行模型中的代码。所有命令都具有CanExecute代码,以确定所绑定的按钮的启用状态。逻辑完美,但在所有情况下,除非我单击GUI中的其他位置,否则GUI将保持禁用状态。
例如,我有一个名为Discard Candy的按钮。当我单击此按钮时,它会在线程池线程中启动一个进程,该进程将一个名为Running的bool属性设置为true。由于Discard Candy命令的CanExecute方法看起来像这样:
public bool CanExecute(object parameter)
{
  return !Running;
}

一旦进程开始,按钮就会被禁用。问题在于,当进程完成时,Running被设置为false,但GUI没有更新,即“Discard Candy”未被重新启用。

然而,如果我在GUI的任何地方点击,比如窗口或标题栏,那么“Discard Candy”按钮突然就变成可用状态了。所以逻辑是正确的,但是有些什么事情发生了,我不理解。请有人能解释一下这种行为吗?

编辑 - 到目前为止,看起来CommandManager.InvalidateRequerySuggested对人们没有帮助。我打算试一试,但目前有点担心它。我确实遵循了建议的链接,在这样做的过程中决定更多地了解MVVM light toolkit。它听起来非常不错,有没有人在这里使用过它,并能够确认它没有展现出我迄今为止看到的问题?虽然我计划在我的应用程序的下一个主要版本中尝试MVVM light toolkit,但我不想重做我目前已经放置的所有指令,这就是为什么我可能会先从CommandManager.InvalidateRequerySuggested开始,这样我们就可以得到另一个有关其有用性的数据点。

编辑#2 - 非常有趣,MVVM light toolkit实际上依赖于CommandManager.InvalidateRequerySuggested以支持UI的能力来禁用/重新启用命令。作者说:

"严格来说,在WPF中,如果您的命令绑定到被CommandManager监视的控件,则不需要自己触发CanExecuteChanged事件。您可以让CommandManager处理这种情况。也就是说,外部事件也可能会更改UI的状态。假设UI应该在早上9点到下午5点之间启用,然后在晚上禁用。用户没有触发UI,因此代码应该(礼貌地)请求CommandManager重新查询指令的状态。这是通过在CommandManager上调用InvalidateRequerySuggested方法实现的。而正如你猜到的那样,RelayCommand类的RaiseCanExecuteChanged方法也正是这样做的。"


你能展示一下将Running设置为false的代码吗?它是在线程完成后的回调函数中吗? - John Weldon
当您设置Running时,是否为命令引发CanExecuteChanged? - itowlson
@John:当指定的事件被设置时,工作流完成后运行被设置为false。@itowlson:我本来以为一切都会“神奇地工作”。学习这些东西时,我在CanExecute中设置了一个断点,它总是被触发...但我想这只是因为当我按F5时,GUI重新绘制,然后再次调用CanExecute。我太傻了! - Dave
2
我也遇到了同样的问题,执行CommandManager.InvalidRequerySuggested也没有帮助。我很好奇你是如何解决这个问题的。我们最终手动禁用了它,因为这个问题。 - Kilhoffer
2
我曾经看到 InvalidateRequerySuggested 没有起作用的唯一情况是它在非 Dispatcher 线程中执行。除此之外,它都能正常工作。 - Anderson Imes
显示剩余2条评论
3个回答

22

除非有理由进行更新,否则WPF不会更新绑定命令的控件。单击GUI会导致WPF刷新,因此更新将起作用。

您可以通过调用CommandManager.InvalidateRequerySuggested手动刷新任何绑定命令的控件。


1
谢谢,卡梅伦,我会试一下的。将其放入一个合理的间隔计时器中是否很糟糕? - Dave
1
可能吧。你可以在处理完成后直接调用它吗? - Cameron MacFarland
7
我看到了Dave出现了同样的行为,执行CommandManager.InvalidateRequerySuggested并没有帮助。我希望这里有人能够提供一些关于为什么这种情况有时会发生的见解。 - Kilhoffer
非常小心CommandManager.InvalidateRequerySuggested。请看我几个月前发布的类似问题:https://dev59.com/Z3I-5IYBdhLWcg3wpaB1 - Rob
@unforgiven3:我来试一试。你应该将它提交为答案。在查找时,我还没有遇到过你的帖子,但这是一个非常奇怪的主题。 - Dave
我本来希望能找到一个更好的方法来解决这个问题,但最后我只是在主线程中的一个慢节拍定时器中调用了CommandManager.InvalidateRequerySuggested,这对我解决了问题。再次感谢你的建议! - Dave

5
我的问题似乎与命令绑定有关 - 我像往常一样使用了RelayCommand,但是按钮的渲染在我点击窗口之前就不正确。从CommandBinding 中删除CanExecute 代码并改用IsEnabled 属性解决了我的问题,而且没有任何麻烦 - 只是花了很长时间才尝试了这个方法,因为还有很多其他可能引起问题的地方。

这对我很有效,比其他“解决方案”简单得多,你是真正的MVP! - Paul Murphy

0
有时,将焦点设置在父控件上会使CommandManager触发CanExecute。在将Running设置为false后,请尝试以下操作:
...
Running = false;
parentControl.Focusable = true;
parentControl.Focus();

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