C# 为什么ButtonBase没有';在测试“ICommand.CanExecute”之前,请不要检查其可见性?

C# 为什么ButtonBase没有';在测试“ICommand.CanExecute”之前,请不要检查其可见性?,c#,wpf,mvvm,weak-references,C#,Wpf,Mvvm,Weak References,我遇到了一个问题,它让我心烦意乱。 让我们从按钮库中查看这些方法: private void HookCommand(ICommand command) { CanExecuteChangedEventManager.AddHandler(command, OnCanExecuteChanged); UpdateCanExecute(); } private void OnCanExecuteChanged(object sen

我遇到了一个问题,它让我心烦意乱。
让我们从
按钮库
中查看这些方法:

    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
。它订阅
命令管理器。通过弱事件管理器重新查询建议的
,并更新按钮状态(启用/禁用)

OnCanExecuteChanged
只是一个事件处理程序,当您使用与
RoutedCommand
不同的命令时,
UpdateCanExecute
最终调用您的
ICommand.CanExecute
。当您使用任何MVVM框架时,情况就是这样

现在,问题来了。
我的一个数据模板应用于
ContentControl
以显示一些数据:

<ContentControl Grid.Row="0" Content="{Binding}" ContentTemplate="{StaticResource TemplateState}"/>

此模板包含在另一个
ContentControl
中相当复杂的可视化树中,该树位于
ElementHost
中(这是WinForms MDI应用程序中的WPF组件)。 此模板中有两个按钮,其
命令
属性绑定到
RelayCommand
s

当我关闭包含使用此数据模板渲染的视觉效果的MDI子级时,按钮尝试更新其状态并调用
OnCanExecuteChanged
。这是一个大问题,因为
CanExecute
调用一些已经处理过的一次性对象

我知道: 1) 此时窗口(WinForms窗体)关闭,因为在处理
窗体之后调用
CanExecute
。closed
事件; 2) 没有内存泄漏-如果我模拟
CanExecute
,内存探查器会显示,包含命令的视图模型由GC收集,不再存在

这个问题。
如果按钮不可见,检查
CanExecute
的目的是什么? 有没有办法阻止这种行为

附言。 我看到的唯一解决方法是在视图模型中的某个地方保留一个标志,它将显示一次性已被处置,并从
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" />
    
    
    

    如果它没有查询按钮隐藏的状态,那么一旦禁用,它将永远不会显示。

    为什么有些Microsoft开发人员会以他们的方式开发东西呢。。。不管什么原因,他们都是那样做的。在你尝试使用
    disposableObject
    之前,先使用旧的
    if(disposableObject!=null)
    怎么样?@Sheridan:可能有这样的行为的原因,我做错了什么
    disposableObject
    不是空的,它只是被释放了,检查一些标志是我建议的解决方法。“它订阅CommandManager.RequerySuggested”我想这就是问题所在,因为您的所有命令都与此事件挂钩,当调用InvalidateRequestSuggested()时,它们都将被重新计算,这是在你关闭窗口时发生的。@Dtex:没错。但是当按钮不可见时,我想不出检查某个影响视觉状态的东西的理由。我的建议基本上与您的解决方法相同,如果处理了视图模型,则返回false。