我正在使用计算器。我的检视包含16个Button
和1个TextBox
:
TextBox
的Text
属性绑定到ViewModel
中的UserInput
属性:
第一个
我有3个命令:一个用于向TextBox
添加文本,另一个用于显示结果,最后一个用于清除TextBox
。AC和**"="**按钮(请参见屏幕截图)绑定到ClearTextCommand
和ShowResultCommand
。
第一次
程序运行的很好,但是有两个命令有一个共同的问题。我不知道是不是我的无知,但是我在谷歌上没有找到任何东西。我以为我的CanExecute
方法只有在UserInput
改变的时候才能工作(属性已更改),但我在命令的CanExecute
方法上设置了一个断点,并看到它们在调试中始终有效,比如一旦UserInput
被修改,ShowResultCommand
和ClearTextCommand
的CanExecute
方法就永远有效,它们在循环中被一次又一次地调用。我想知道它们是否应该这样工作?它们不应该在UserInput
被修改时被调用吗?这不会在我的程序中引起任何错误,但我觉得我的命令有问题。
所以基本上问题是:
CanExecute
是应该在我的应用运行时循环工作,还是应该在与方法相关的属性更改时工作?如果应该循环工作,则一切正常,但如果不应该循环工作,则我的命令有什么问题?
1条答案
按热度按时间snz8szmq1#
我以为我的
CanExecute
方法只有在UserInput变更(属性变更)时才能运作。是和否。您的命令将
CanExecuteChanged
事件委托给CommandManager
的RequerySuggested
事件。这是一种常见的方法。CommandManager
是一种WPF框架类型,负责:提供与命令相关的实用工具方法,这些方法为类所有者和命令注册
CommandBinding
和InputBinding
对象,添加和移除命令事件处理程序,并提供用于查询命令状态的服务。RequerySuggested
事件仅在少数未详细记录的情况下引发:CommandManager
在确定命令目标何时发生变化时仅关注 * 某些条件 *,例如键盘焦点的变化。正如您所看到的,这是一个非常复杂的问题,在某些情况下,
CommandManager
根本无法知道:在
CommandManager
无法充分确定导致命令无法执行的条件更改的情况下,可以调用InvalidateRequerySuggested
来强制CommandManager
引发RequerySuggested
事件。总而言之,是的,当用户输入发生变化以及其他与输入相关的事件(例如,在
TextBox
中或绑定属性发生变化)时,会引发RequerySuggested
事件,但并非在所有情况下都是如此。从另一个Angular 来看,CommandManager
仅在 * 常规触发器 * 上确定何时需要引发事件,因此通常它会使 * 所有 * 可执行状态无效。但它并不是针对特定的情况,就像你想观察一个不同的属性的变化,而是像一个水壶。这当然会对性能产生影响,尽管在大多数应用程序中可以忽略不计。我将断点放在命令的
CanExecute
方法上,并看到它们在调试中始终有效是的,现在您已经知道确切的原因了。在调试模式中,当遇到断点时,调试器或IDE将进入前台,这意味着键盘焦点将发生变化。当您切换回正在调试的应用程序时,键盘焦点将再次发生变化......一次又一次......一次又一次。由于
CommandManager
会在键盘焦点上引发RequerySuggested
事件,你将不断地触发CanExecute
并命中断点。同样的情况也可能发生在窗口激活上。不同的命令方法
还有另一种非常常见的方法来通知can execute已更改。在本示例中,您依赖
CommandManager
来最好地处理所有命令。但是,您也可以自己负责,并通过另一个公共方法显式地使can execute无效。这里你假设你知道什么时候更新你自己的命令,而不是问"亲爱的命令管理器,请告诉我什么时候更新",你说"现在更新这些特定的命令"。为此你必须为属性中的每个 * 受影响的 * 命令调用
RaiseCanExecuteChanged
。与
RequerySuggested
事件相比,此机制具有一些优点。关于干燥和可重用性的提示
到目前为止,你所有的命令都包含重复的逻辑,违反了Don't repeat yourself principle,简称DRY。这使得维护变得更加困难。相反,你至少应该提取一个通用的、可重用的命令类型,如上所示,以改进你的代码。你甚至不需要自己实现一个,已经有很多框架、库和NuGet包可用,例如Microsoft.Toolkit.Mvvm。请在此处查看其documentation。
以下是明文命令的示例:
为了简单起见,我使用了一个没有额外参数的
RelayCommand
实现,因为您目前没有使用它。它与上面的命令相同,只是每个方法都没有参数。正如注解中所要求的,这将是没有参数的
RelayCommand
实现。