6

我真的为这个挠头。我有一个打开对话框的主窗口。对话框关闭后,对话框中绑定的命令的 CanExecute 方法仍在执行。这在我的应用程序中造成了一些严重的问题。

例子:

MainWindow 有一个带有点击处理程序的按钮。这是点击事件处理程序:

    private void Button_Click(object sender, RoutedEventArgs e)
    {
        DialogWindow window = new DialogWindow();
        window.ShowDialog();
    }

在对话框中,我将项目控件绑定到对话框窗口中的静态资源,列表中的每个项目都有一个命令:

<Window.Resources>

    <Collections:ArrayList x:Key="itemsSource">
        <local:ItemViewModel Description="A"></local:ItemViewModel>
        <local:ItemViewModel Description="B"></local:ItemViewModel>
        <local:ItemViewModel Description="C"></local:ItemViewModel>
    </Collections:ArrayList>

    <DataTemplate DataType="{x:Type local:ItemViewModel}">
            <Button Grid.Column="1" Command="{Binding Path=CommandClickMe}" Content="{Binding Path=Description}" Style="{StaticResource {x:Static ToolBar.ButtonStyleKey}}">
            </Button>
    </DataTemplate>

</Window.Resources>

<Grid>
    <ToolBar ItemsSource="{StaticResource itemsSource}"></ToolBar>
</Grid>

这是视图模型:

public class ItemViewModel
{
    private RelayWpfCommand<object> _commandClickMe;

    public RelayWpfCommand<object> CommandClickMe
    {
        get
        {
            if (_commandClickMe == null)
                _commandClickMe = new RelayWpfCommand<object>(obj => System.Console.Out.WriteLine("Hei mom"), obj => CanClickMe());

            return _commandClickMe;
        }
    }

    private bool CanClickMe()
    {
        return true;
    }

    public string Description { get; set; }

这是 DelegateCommand 的实现:

public class RelayWpfCommand<T> : ICommand
{
    public event EventHandler CanExecuteChanged
    {
        add { CommandManager.RequerySuggested += value; }
        remove { CommandManager.RequerySuggested -= value; }
    }

    private readonly Predicate<T> _canExecute;
    private readonly Action<T> _execute;

    public RelayWpfCommand(Action<T> execute, Predicate<T> canExecute)
    {
        _execute = execute;
        _canExecute = canExecute;
    }

    /// <summary>
    /// Forces a notification that the CanExecute state has changed
    /// </summary>
    public void RaiseCanExecuteChanged()
    {
        CommandManager.InvalidateRequerySuggested();
    }

    public bool CanExecute(T parameter)
    {
        return _canExecute(parameter);
    }

    public void Execute(T parameter)
    {
        _execute(parameter);
    }

    bool ICommand.CanExecute(object parameter)
    {
        if (!IsParameterValidType(parameter))
            return false;

        return CanExecute((T)parameter);
    }

    void ICommand.Execute(object parameter)
    {
        if (!IsParameterValidType(parameter))
            throw new ArgumentException(string.Format("Parameter must be of type {0}", typeof(T)));

        Execute((T)parameter);
    }

    private static bool IsParameterValidType(object parameter)
    {
        if (parameter != null && !typeof(T).IsAssignableFrom(parameter.GetType()))
            return false;

        return true;
    }
}

现在,如果我关闭对话框窗口并在视图模型上的 CanExecute(我正在使用带有弱事件订阅的 Prism DelegateCommand)方法中设置断点,我注意到尽管对话框已关闭,但它仍会触发。为什么对话框中的按钮和 ViewModel 上的命令之间的绑定仍然存在?

我正在通过关闭窗口并稍后在视图模型的“CanClickMe”方法中设置断点来检查它是否正在执行。它会执行一段时间,然后突然停止(可能是由于 GC)。这种不确定的行为会导致问题,因为在实际应用程序中,视图模型可能已经被处理掉了。

4

4 回答 4

1

您可以使用 Wea​​kEvent 模式来缓解此问题。请参考以下 Stackoverflow 问题:Josh Smith 的 RelayCommand 实现是否有缺陷?

于 2011-12-05T17:44:53.123 回答
0

您可以尝试以下操作,而不是将您的命令作为属性:

public ICommand CommandClickMe
{
   get
   {
       return new RelayWpfCommand<object>((obj)=>System.Console.Out.WriteLine("Hei mom"), obj => CanClickMe());
   }
}
于 2011-11-25T10:11:12.447 回答
0

我在不同的项目中多次看到这个问题,我不确定这个令人毛骨悚然的错误是否也潜伏在你的应用程序中,但值得检查。

WPF 3.5(包括 SP1)中有一个已知的内存泄漏问题,基本上,如果您绑定到不是 aDependencyProperty或未实现INotifyPropertyChanged. 这正是您的代码的内容。

只需实施INotifyPropertyChangedItemViewModel看看它是如何进行的。希望这可以帮助。

于 2011-01-09T21:46:57.127 回答
0

您可以在窗口关闭时清除窗口的 CommandBindings 集合。

于 2011-06-22T11:49:44.080 回答