使用 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);
});
}
}
为了完整性:IDialogCoordinator
和 parentViewModel
我正在使用 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)
的违规调用。
我正在尝试以同步方式显示来自工作任务的对话框,以便进程可以根据用户通过对话框的反馈中止或继续进程。
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);
});
}
}
为了完整性:IDialogCoordinator
和 parentViewModel
我正在使用 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)
的违规调用。