繁体   English   中英

为什么ButtonBase在测试`ICommand.CanExecute`之前不检查其可见性?

[英]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 还有更好的主意吗?

我将给出四个可能的答案,并对如何以这种方式实现它进行猜测:

  1. 在处置所有内容之前,将要拆除的窗口的DataContext设置为null 该按钮在对象上没有引用,因此永远不会引发异常。

  2. 将对一次性对象的调用包装在try / catch中,该对象过滤ObjectDisposedException并返回false。

  3. IsDisposed属性添加到一次性对象,并事先检查。 如果您在非UI或终结器线程上执行任何操作,似乎这里可能存在争用情况。

  4. 如果正在等待终结器调用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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM