我遇到了一个问题,这让我大吃一惊
我们来看看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
只是一个事件处理程序,当您使用与UpdateCanExecute
不同的内容时,ICommand.CanExecute
最终会调用您的RoutedCommand
。当您使用任何MVVM框架时就是这种情况。
现在,问题。
我的一个数据模板应用于ContentControl
以显示一些数据:
<ContentControl Grid.Row="0" Content="{Binding}" ContentTemplate="{StaticResource TemplateState}"/>
此模板包含在另一个ContentControl
内的相当复杂的可视树中,该ElementHost
托管在Command
中(这是WinForms MDI应用程序中的WPF组件)。
此模板中有几个按钮,其RelayCommand
属性绑定到OnCanExecuteChanged
。
当我关闭包含使用此数据模板呈现的视觉效果的MDI子项时,按钮会尝试更新其状态并调用CanExecute
。这是一个很大的问题,因为CanExecute
调用了一些已经被处置的一次性对象。
我知道,那:
1)此时窗口(WinForms表单)已关闭,因为在处理Form.Closed
事件后调用了CanExecute
;
2)没有内存泄漏 - 如果我模拟CanExecute
,内存分析器显示,我的视图模型包含命令,由GC收集并且不再存在。
问题。
如果按钮不可见,检查false
的目的是什么?
有没有选择来阻止这种行为?
P.S。
我看到的唯一解决方法是在我的视图模型中的某处保留一个标志,该标志将显示,丢弃了一次性用品,并从CanExecute
返回{{1}}。
有更好的想法吗?
答案 0 :(得分:0)
我会给出四个可能的答案,并猜测为什么它的实现方式如下:
在丢弃所有内容之前,将窗口的DataContext
设置为null
。该按钮没有对象的引用,因此异常永远不会被抛出。
在try / catch中调用一次性对象,过滤ObjectDisposedException
并返回false。
将IsDisposed
属性添加到一次性对象中,并事先进行检查。如果您在非UI或终结器线程上做任何事情,似乎可能存在竞争条件。
如果您正在等待终结器呼叫WeakReference
,请将Dispose
保留到一次性对象,或在致电null
后将引用设置为Dispose()
并在调用之前检查它是否为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" />
如果没有查询隐藏按钮的状态,一旦禁用它就永远不会显示。