[英]Why ButtonBase doesn't check its Visibility before testing `ICommand.CanExecute`?
我遇到了一个问题,这让我大吃一惊。
让我们从ButtonBase
看这些方法:
private void HookCommand(ICommand command)
{
CanExecuteChangedEventManager.AddHandler(command, OnCanExecuteChanged);
UpdateCanExecute();
}
private void OnCanExecuteChanged(object sender, EventArgs e)
{
UpdateCanExecute();
}
private void UpdateCanExecute()
{
if (Command != null)
{
CanExecute = MS.Internal.Commands.CommandHelpers.CanExecuteCommandSource(this);
}
else
{
CanExecute = true;
}
}
将新命令分配给按钮时,将调用HookCommand
。 它通过弱事件管理器订阅CommandManager.RequerySuggested
并更新按钮状态(启用/禁用)。
OnCanExecuteChanged
只是一个事件处理程序,当您使用与RoutedCommand
不同的方法时, UpdateCanExecute
最终将调用ICommand.CanExecute
。 当您使用任何MVVM框架时,就是这种情况。
现在,问题了。
我的数据模板之一应用于ContentControl
来显示一些数据:
<ContentControl Grid.Row="0" Content="{Binding}" ContentTemplate="{StaticResource TemplateState}"/>
该模板包含在另一个ContentControl
内的相当复杂的可视树中,该ContentControl
托管在ElementHost
(这是WinForms MDI应用程序中的WPF组件)。 该模板内有几个按钮,其Command
属性绑定到RelayCommand
。
当我关闭使用此数据模板渲染的包含视觉效果的MDI子项时,按钮会尝试更新其状态并调用OnCanExecuteChanged
。 这是一个很大的问题,因为CanExecute
调用了一些已经被丢弃的一次性对象。
我知道,认为:1)的窗口(WinForms的形式)此时关闭,因为CanExecute
后称为Form.Closed
事件被处理; 2)没有内存泄漏-如果我模拟CanExecute
,内存事件探查器显示,包含命令的视图模型是由GC收集的,不再存在。
问题。
如果按钮不可见,检查CanExecute
的目的是什么? 有什么选择可以防止这种行为?
PS我看到的唯一解决方法是在视图模型中的某个位置保留一个标志,该标志将显示已处理了一次性物品,并从CanExecute
返回false
。 还有更好的主意吗?
我将给出四个可能的答案,并对如何以这种方式实现它进行猜测:
在处置所有内容之前,将要拆除的窗口的DataContext
设置为null
。 该按钮在对象上没有引用,因此永远不会引发异常。
将对一次性对象的调用包装在try / catch中,该对象过滤ObjectDisposedException
并返回false。
将IsDisposed
属性添加到一次性对象,并事先检查。 如果您在非UI或终结器线程上执行任何操作,似乎这里可能存在争用情况。
如果正在等待终结器调用Dispose
,则将WeakReference
保持为可抛弃对象,或者在调用Dispose()
之后将引用设置为null
,并在调用它之前检查其是否为null
。
至于为什么即使在不可见的情况下也要查询命令,请考虑命令的结果可以控制可见性。 想象以下情况:
<!-- This would probably have to be done in some more complicated way, like
passing IsEnabled to a converter with CanExecute as the parameter, or
by just binding to IsEnabled. -->
<Button Visibility="{Binding RelativeSource={x:Static RelativeSource.Self}, Path=CanExecute}"
Command="{Binding TheCommand" Content="Do it" />
如果不查询按钮的状态是隐藏的,则一旦禁用它就永远不会显示。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.