在第二个 UI 线程上使用命令时出现 InvalidOperationException

InvalidOperationException when using a command on a second UI thread

我们正在向我们的 catel 4.4.0 MVVM 应用程序添加第二个 UI 线程。

我已经在第二个 UI 线程上创建了我的视图和视图模型,并且正在使用 DispatcherTimer 定期更新视图模型。那部分似乎工作正常。但是,当我在视图模型的构造函数中创建 Catel.MVVM.Command 时,我 运行 遇到了问题。当命令的 CanExecute 触发时,我在第二个 UI 线程上得到一个 System.InvalidOperationException。

命令没什么特别的。

private bool ImageClickCanExecute()
{
    return true;
}

public void ImageClickAction()
{
    if(ImageClickTarget != null)
        Process.Start(ImageClickTarget.ToString());
}

观点也不是。

<Grid>
    <Button Command="{Binding ImageClickCommand}">
        <Image Name="ImageSource" Source="{Binding ImageSource, FallbackValue={x:Null}}"/>
    </Button>
</Grid>

堆栈跟踪如下。

0:010> !PrintException /d 00000282cb8effd0
Exception object: 00000282cb8effd0
Exception type:   System.Reflection.TargetInvocationException
Message:          Exception has been thrown by the target of an invocation.
InnerException:   System.InvalidOperationException, Use !PrintException 00000282cb8edee8 to see more.
StackTrace (generated):
    SP               IP               Function
    000000B0459FD510 0000000000000000 mscorlib_ni!System.RuntimeMethodHandle.InvokeMethod(System.Object, System.Object[], System.Signature, Boolean)+0x1
    000000B0459FD510 00007FFFCC5E1C20 mscorlib_ni!System.Reflection.RuntimeMethodInfo.UnsafeInvokeInternal(System.Object, System.Object[], System.Object[])+0x80
    000000B0459FD580 00007FFFCC5BE789 mscorlib_ni!System.Delegate.DynamicInvokeImpl(System.Object[])+0x99
    000000B0459FD5D0 00007FFF70FC83AE UNKNOWN!Catel.EventHandlerExtensions.SplitInvoke(System.Delegate, System.Object[])+0xfe
    000000B0459FD630 00007FFF7164E085 UNKNOWN!Catel.EventHandlerExtensions.SafeInvoke(System.EventHandler, System.Object, System.EventArgs)+0x55
    000000B0459FD680 00007FFF71674998 UNKNOWN!Catel.MVVM.Command`2+<<RaiseCanExecuteChanged>b__32_0>d[[System.__Canon, mscorlib],[System.__Canon, mscorlib]].MoveNext()+0x38
    000000B0459FD7B0 00007FFFCD2E1224 mscorlib_ni!System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(System.Threading.Tasks.Task)+0xd3f9c4
    000000B0459FD7F0 00007FFFCC5A17AD mscorlib_ni!System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(System.Threading.Tasks.Task)+0x3d
    000000B0459FD820 00007FFF71674515 UNKNOWN!Catel.MVVM.Command`2+<>c__DisplayClass35_0+<<AutoDispatchIfRequiredAsync>b__0>d[[System.__Canon, mscorlib],[System.__Canon, mscorlib]].MoveNext()+0xf5
    000000B0459FD9B0 00007FFFCCFCDA88 mscorlib_ni!System.Runtime.CompilerServices.AsyncMethodBuilderCore+<>c.<ThrowAsync>b__6_0(System.Object)+0x38
    000000B0459FD9E0 00007FFFA8119C0E WindowsBase_ni!System.Windows.Threading.ExceptionWrapper.InternalRealCall(System.Delegate, System.Object, Int32)+0xae
    000000B0459FDA50 00007FFFA8119AC6 WindowsBase_ni!System.Windows.Threading.ExceptionWrapper.TryCatchWhen(System.Object, System.Delegate, System.Object, Int32, System.Delegate)+0x36
    000000B0459FDAA0 00007FFFA811CA2B WindowsBase_ni!System.Windows.Threading.DispatcherOperation.InvokeImpl()+0x10b
    000000B0459FDB20 00007FFFCC5CA79E mscorlib_ni!System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean)+0x15e
    000000B0459FDBF0 00007FFFCC5CA637 mscorlib_ni!System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean)+0x17
    000000B0459FDC20 00007FFFCC5CA5F2 mscorlib_ni!System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object)+0x52
    000000B0459FDC70 00007FFFA8333810 WindowsBase_ni!MS.Internal.CulturePreservingExecutionContext.Run(MS.Internal.CulturePreservingExecutionContext, System.Threading.ContextCallback, System.Object)+0x80
    000000B0459FDCD0 00007FFFA811C784 WindowsBase_ni!System.Windows.Threading.DispatcherOperation.Invoke()+0x64
    000000B0459FDD30 00007FFFA8117C24 WindowsBase_ni!System.Windows.Threading.Dispatcher.ProcessQueue()+0x1a4
    000000B0459FDDB0 00007FFFA8118061 WindowsBase_ni!System.Windows.Threading.Dispatcher.WndProcHook(IntPtr, Int32, IntPtr, IntPtr, Boolean ByRef)+0x71
    000000B0459FDE30 00007FFFA8119E53 WindowsBase_ni!MS.Win32.HwndWrapper.WndProc(IntPtr, Int32, IntPtr, IntPtr, Boolean ByRef)+0xc3
    000000B0459FDEC0 00007FFFA8119D82 WindowsBase_ni!MS.Win32.HwndSubclass.DispatcherCallbackOperation(System.Object)+0x82
    000000B0459FDF10 00007FFFA8119BC9 WindowsBase_ni!System.Windows.Threading.ExceptionWrapper.InternalRealCall(System.Delegate, System.Object, Int32)+0x69
    000000B0459FDF80 00007FFFA8119AC6 WindowsBase_ni!System.Windows.Threading.ExceptionWrapper.TryCatchWhen(System.Object, System.Delegate, System.Object, Int32, System.Delegate)+0x36
    000000B0459FDFD0 00007FFFA8117583 WindowsBase_ni!System.Windows.Threading.Dispatcher.LegacyInvokeImpl(System.Windows.Threading.DispatcherPriority, System.TimeSpan, System.Delegate, System.Object, Int32)+0x173
    000000B0459FE070 00007FFFA81194FF WindowsBase_ni!MS.Win32.HwndSubclass.SubclassWndProc(IntPtr, Int32, IntPtr, IntPtr)+0x11f
    000000B0459FE390 0000000000000000 WindowsBase_ni!MS.Win32.UnsafeNativeMethods.DispatchMessage(System.Windows.Interop.MSG ByRef)+0x1
    000000B0459FE450 00007FFFA812D8FC WindowsBase_ni!System.Windows.Threading.Dispatcher.PushFrameImpl(System.Windows.Threading.DispatcherFrame)+0xec
    000000B0459FE4E0 00007FFFA26A98B3 PresentationFramework_ni!System.Windows.Application.RunDispatcher(System.Object)+0x73
    000000B0459FE520 00007FFFA26A969D PresentationFramework_ni!System.Windows.Application.RunInternal(System.Windows.Window)+0x8d
    000000B0459FE580 00007FFF70FD70F7 UNKNOWN!xxx.Wpf.App.Main()+0x67
    000000B0459FF0C0 0000000000000000 mscorlib_ni!System.AppDomain._nExecuteAssembly(System.Reflection.RuntimeAssembly, System.String[])+0x1
    000000B0459FF0C0 00007FFFCCD57EBF mscorlib_ni!System.AppDomain.ExecuteAssembly(System.String, System.Security.Policy.Evidence, System.String[])+0x7f
    000000B0459FF110 00007FFF7053434E UNKNOWN!Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()+0x3e
    000000B0459FF150 00007FFFCC5CA79E mscorlib_ni!System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean)+0x15e
    000000B0459FF220 00007FFFCC5CA637 mscorlib_ni!System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean)+0x17
    000000B0459FF250 00007FFFCC5CA5F2 mscorlib_ni!System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object)+0x52
    000000B0459FF2A0 00007FFFCC51BEF2 mscorlib_ni!System.Threading.ThreadHelper.ThreadStart()+0x52

StackTraceString: <none>
HResult: 80131604
0:010> !PrintException /d 00000282cb8edee8
Exception object: 00000282cb8edee8
Exception type:   System.InvalidOperationException
Message:          The calling thread cannot access this object because a different thread owns it.
InnerException:   <none>
StackTrace (generated):
    SP               IP               Function
    000000B0459FCD70 00007FFFA8446977 WindowsBase_ni!System.Windows.Threading.Dispatcher.VerifyAccess()+0x32faf7
    000000B0459FCDA0 00007FFFA8112806 WindowsBase_ni!System.Windows.DependencyObject.GetValue(System.Windows.DependencyProperty)+0x26
    000000B0459FCE00 00007FFFA27EBB5B PresentationFramework_ni!System.Windows.Controls.Primitives.ButtonBase.get_Command()+0x2b
    000000B0459FCE30 00007FFFA27EBB00 PresentationFramework_ni!System.Windows.Controls.Primitives.ButtonBase.UpdateCanExecute()+0x10

StackTraceString: <none>
HResult: 80131509

这是因为,默认情况下,命令会使用 DispatcherService 自动发送更改通知。由于您是 运行 边缘情况(2 ui 线程),我建议您关闭自动调度并自行处理。

由于这是一个静态保护成员,您需要从 CommandBase 派生您自己的命令 class 并将 AutomaticallyDispatchEvents 设置为 false

ps。我建议使用 IProcessService 而不是直接使用 Process.Start,它允许您在需要时模拟或替换它。