除非单击鼠标,否则按钮不会重新启用的奇怪问题

Posted

技术标签:

【中文标题】除非单击鼠标,否则按钮不会重新启用的奇怪问题【英文标题】:Weird problem where Button does not get re-enabled unless the mouse is clicked 【发布时间】:2011-01-20 20:34:40 【问题描述】:

我的应用程序是使用 WPF 中的 MVVM 模式编写的,并且我的所有按钮都使用命令绑定来执行我的模型中的代码。所有命令在 CanExecute 中都有代码来确定绑定的 Button 的 Enabled 状态。该逻辑运行良好,但在所有情况下,除非我单击 GUI 中的其他位置,否则 GUI 始终处于禁用状态。

例如,我有一个名为 Discard Candy 的按钮。当我单击此按钮时,它会在线程池线程中启动一个进程,该进程将名为 Running 的布尔属性设置为 true。由于 Discard Candy 命令的 CanExecute 方法看起来像这样

public bool CanExecute(object parameter)

  return !Running;

一旦进程开始,该按钮将被禁用。问题是,当该过程完成时,Running 被设置为 false,但 GUI 不会更新,即 Discard Candy 不会重新启用。

但是,如果我单击 GUI 中的任意位置,例如窗口或标题栏,“丢弃糖果”按钮会突然启用。所以逻辑有效,但发生了一些我不明白的事情。有人可以向我解释一下这种行为吗?

编辑 -- 到目前为止,听起来 CommandManager.InvalidateRequerySuggested 并没有帮助到人们。我打算试一试,但目前我有点警惕。我确实遵循了推荐的链接,因此决定阅读更多关于 MVVM 轻量级工具包的信息。听起来非常不错——这里有没有人使用过它并且能够确认它没有出现我迄今为止看到的问题?虽然我计划在下一个主要版本中尝试 MVVM 轻量级工具包。在我的应用程序中,我不想重做我目前拥有的所有命令,这就是为什么我可能会从 CommandManager.InvalidateRequerySuggested 开始,这样我们就可以在这里获得另一个关于它有用性的数据点。

EDIT #2 -- 非常有趣,MVVM 轻量级工具包实际上依赖 CommandManager.InvalidateRequerySuggested 以支持 UI 禁用/重新启用命令的能力。作者说:

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

【问题讨论】:

你能显示将 Running 设置为 false 的代码吗?是不是在线程完成后的回调中? 设置 Running 时是否为命令提升 CanExecuteChanged? @John:在工作流完成后,Running 被设置为 false,这在指定事件被设置时是已知的。 @itowlson:我的印象是一切都会“神奇地起作用”。当我在学习这些东西时,我在 CanExecute 中放置了一个断点,它总是被击中......但我想这只是因为当我按下 F5 时,GUI 重新绘制,然后导致 CanExecute 再次被调用。傻我! 我遇到了同样的行为,执行 CommandManager.InvalidRequerySuggested 并没有帮助。我很好奇你是怎么解决这个问题的。由于这个问题,我们最终手动禁用。 我见过的唯一一次 InvalidateRequerySuggested 不起作用是它是从 Dispatcher 以外的另一个线程执行的。除此之外,它工作正常。 【参考方案1】:

除非有理由,否则 WPF 不会更新命令绑定控件。单击 GUI 会导致 WPF 刷新,以便更新工作。

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

【讨论】:

谢谢,Cameron,我会试一试。将其放入具有合理间隔的 Timer 中是不是很糟糕? 可能。处理完之后可以调用吗? 我看到了 Dave 的相同行为,执行 CommandManager.InvalidateRequerySuggested 并没有帮助。我希望这里有人可以就为什么有时会发生这种情况提供一些见解。 非常警惕 CommandManager.InvalidateRequerySuggested。看看我几个月前发布的一个类似问题:***.com/questions/1751966/… @unforgiven3:我会试一试。您应该将其作为答案提交。我在看的时候没有看到你的帖子,但这是一个奇怪的话题。【参考方案2】:

有时,将焦点设置在父控件上会使 CommandManager 触发 CanExecute。将 Running 设置为 false 后尝试以下操作:

...
Running = false;
parentControl.Focusable = true;
parentControl.Focus();

【讨论】:

【参考方案3】:

我的问题似乎与命令绑定有关 - 我经常使用 RelayCommand,但在单击窗口之前按钮的呈现不正确。

CommandBinding 中删除CanExecute 代码并改用IsEnabled 属性解决了我的问题而没有头疼 - 直到我尝试了这个可能是问题的许多其他事情之前,我花了很长时间。

【讨论】:

这对我有用,而且比任何其他“解决方案”都简单得多,你是真正的 MVP!

以上是关于除非单击鼠标,否则按钮不会重新启用的奇怪问题的主要内容,如果未能解决你的问题,请参考以下文章

在 PyQt 窗口中无延迟地绘制 matplotlib 矩形

单击按钮后keylistener不起作用

除非鼠标移动,否则 Java 物理循环(按间隔重绘)不稳定

u盘通用串行总线控制器被停用后怎么重新启用

除非主窗口未聚焦,否则不会更新 QLabel

除非主窗口未聚焦,否则不会更新 QLabel