使用 MVVM 显示来自工作任务的同步对话框给出 System.InvalidOperationException

Showing an synchronous dialog from a worker task with MVVM gives System.InvalidOperationException

我正在尝试以同步方式显示来自工作任务的对话框,以便进程可以根据用户通过对话框的反馈中止或继续进程。

public Task StartBackgroundTask()
{
    var measurementResult = DoQuickPreMeasurement();

    if(measurementSuccessful == false)
    {
        var task = dialogService.ShowMeasurementFailed("Measurement failed because of reason XYZ, continue?!");
        var dialogResult = task.Result;
        if (dialogResult == MessageDialogResult.Negative)
            return Task.CompletedTask; // this ends the background run
    }

    Task.Run(()=>{ LongRunningTask();});
}

StartBackgroundTask() 从 UI 线程调用。当我 运行 这样做时,出现以下异常,我无法解决该异常:

    Exception thrown: 'System.InvalidOperationException' in WindowsBase.dll ("The calling thread cannot access this object because a different thread owns it.")

我正在使用 MvvmLight 并尝试使用各种不同的方式调用 UI 调度程序(也使用 DispatcherHelper),但没有成功。

这是DialogService中的简化代码:

public class DialogService
{
    private IDialogCoordinator dialogCoordinator;
    private ViewModelBase parentViewModel;
    private BaseMetroDialog pleaseCloseLidDialog;
    private BaseMetroDialog openLidPrintingAbortedDialog;
    private BaseMetroDialog shutDownDialog;

    public DialogService(IDialogCoordinator dialogCoordinator, ViewModelBase parentViewModel)
    {
        this.dialogCoordinator = dialogCoordinator;
        this.parentViewModel = parentViewModel;
    }

    public Task<MessageDialogResult> ShowMeasurementFailed(string message)
    {
        var metroWindow = (MetroWindow)(Application.Current.MainWindow);
        return metroWindow.Invoke(() =>
        {
            var mySettings = new MetroDialogSettings()
            {
                AffirmativeButtonText = "Continue",
                NegativeButtonText = "Abort"
            };

            return ((MetroWindow)(Application.Current.MainWindow)).ShowMessageAsync(
                "Measurement has finished",
                "Result: " + message + ".\n\nContinue?",
                MessageDialogStyle.AffirmativeAndNegative,
                mySettings);
            //return dialogCoordinator.ShowMessageAsync(parentViewModel,
            //    "Measurement has finished",
            //    "Result: " + message + ".\n\nContinue?",
            //    MessageDialogStyle.AffirmativeAndNegative,
            //    mySettings);
        });
    }
}       

为了完整性:IDialogCoordinatorparentViewModel 我正在使用 StructureMap 注入 MahApps 实现 (DialogCoordinator) 以及用于主要 window 的 VM,这有效非常适合服务中的其他对话框(我在上面的示例中省略了):

    this.For<IDialogCoordinator>().Use<DialogCoordinator>().Singleton();

    this.ForConcreteType<DialogService>()
        .Configure
        .Ctor<IDialogCoordinator>("dialogCoordinator").Is(c => c.GetInstance<DialogCoordinator>())
        .Ctor<ViewModelBase>("parentViewModel").Is(c => c.GetInstance<MainWindowDialogParentViewModel>())
        .Singleton();

我希望有人能发现我的错误,或者至少给我一些调试方法。

似乎调用

var metroWindow = (MetroWindow)(Application.Current.MainWindow);

是个冒犯性的电话。我现在修改了 ShowMeasurementFailed 方法如下,现在它可以工作了:

public Task<MessageDialogResult> ShowMeasurementFailed(string message)
{
    var dispatcher = DispatcherHelper.UIDispatcher; // get UI dispatcher from MvvmLight
    return dispatcher.Invoke(() =>
    {
        var mySettings = new MetroDialogSettings()
        {
            AffirmativeButtonText = "Continue",
            NegativeButtonText = "Abort"
        };

        return dialogCoordinator.ShowMessageAsync(parentViewModel,
            "Measurement has finished",
            "Result: " + message + ".\n\nContinue?",
            MessageDialogStyle.AffirmativeAndNegative,
            mySettings);
    });
}

请注意,与我之前的尝试相反,调度程序的使用现在有效。也许我在测试期间在代码中的某处仍然遇到了上面 (MetroWindow)(Application.Current.MainWindow) 的违规调用。