为什么Prism DelegateCommands有时会导致线程异常?

时间:2015-11-11 21:42:02

标签: c# wpf multithreading prism delegatecommand

以下示例使用Prism 6.1中的DelegateCommand,但我在5.0中产生了同样的问题。

使用以下视图模型(视图省略,只包含2个按钮):

public class MainWindowViewModel
{
    public DelegateCommand TestCommand { get; set; }
    public DelegateCommand ActionCommand { get; set; }

    public MainWindowViewModel()
    {
        TestCommand = new DelegateCommand(()=> TestCommand.RaiseCanExecuteChanged());

        ActionCommand = new DelegateCommand(() =>
        {
            Task.Run(() =>
            {
                Thread.Sleep(1000);
                TestCommand.RaiseCanExecuteChanged();
            });
        });
    } 
}

如果首先调用ActiveCommand,则会发生此异常:

  

WindowsBase.dll中出现'System.InvalidOperationException'类型的异常,但未在用户代码中处理

     

附加信息:调用线程无法访问此对象,因为另一个线程拥有它。

就我所知,这是标准的“如果你不在UI线程上,你就不允许与Wpf控件交谈”例外。这似乎与方法摘要不一致:

  

在UI线程上引发Prism.Commands.DelegateCommandBase.CanExecuteChanged,这样每个命令调用者都可以重新查询以检查该命令是否可以执行。

此外,我在过去从非UI线程调用此方法时没有问题。

Weirder仍然,如果首先引发TestCommand,那么ActionCommand开始正常工作。我已经检查过,在任何情况下,Task.Run块中的代码都在非UI线程上运行。

不幸的是,我不能在我的真实代码中使用它作为一种解决方法 - 我尝试让UI线程在工作线程执行之前调用RaiseCanExecuteChanged,但它没有帮助。

是否有任何理由让RaiseCanExecuteChanged以这种方式行事?任何修复或解决方法?

1 个答案:

答案 0 :(得分:1)

您在UI线程上创建TestCommand并尝试在单独的线程上访问它。你不能这样做。如果你想要做的只是提升can执行,那么只需等待Task.Run然后调用它。

    public MainWindowViewModel()
    {
        TestCommand = new DelegateCommand(Test, CanTest);

        ActionCommand = new DelegateCommand(async () => 
        {
            await Task.Run(() =>
            {
                Thread.Sleep(1000);
            });

            TestCommand.RaiseCanExecuteChanged();
        });
    }