WPF-Task.Run(()=>;window.ShowDialog)失败

WPF-Task.Run(()=>;window.ShowDialog)失败,wpf,modal-dialog,thread-safety,Wpf,Modal Dialog,Thread Safety,我必须执行繁忙指示和进度报告。限制是,我必须使用提供的控件库,它为进度报告提供了一个窗口 以下代码工作正常,但不会阻止UI,这在某些情况下是必需的 private async void StartLongRunningTask2Sync() { var wndHandle = Application.Current.Windows.OfType<Window>().SingleOrDefault(x => x.IsActive); if (wndHandle == null)

我必须执行繁忙指示和进度报告。限制是,我必须使用提供的控件库,它为进度报告提供了一个窗口

以下代码工作正常,但不会阻止UI,这在某些情况下是必需的

private async void StartLongRunningTask2Sync() {
var wndHandle = Application.Current.Windows.OfType<Window>().SingleOrDefault(x => x.IsActive);
if (wndHandle == null)
{
    return;
}

IntPtr windowHandle = new WindowInteropHelper(wndHandle).Handle;
var progressWindow = new ProgressBarCustomized(windowHandle)
{
    Value = 0, CanCancel = true, CanRetry = false, Thumbnail = null, IsIndeterminate = true
};
progressWindow.Show();

await Task.Run(() => this.longRunningTaskComponent.DoLongRunningTask(this.taskIterations, this.iterationSleepTime));
progressWindow.Close();
private async nongrunningtask2sync(){
var wndHandle=Application.Current.Windows.OfType().SingleOrDefault(x=>x.IsActive);
if(wndHandle==null)
{
返回;
}
IntPtr windowHandle=新的WindowInteropHelper(wndHandle).Handle;
var progressWindow=新ProgressBarCustomized(windowHandle)
{
值=0,CanCancel=true,CanRetry=false,Thumbnail=null,IsIndeterminate=true
};
progressWindow.Show();
wait Task.Run(()=>this.longRunningTaskComponent.DoLongRunningTask(this.taskIterations,this.iterationSleepTime));
progressWindow.Close();
}

以下代码将阻止UI,直到对话框打开为止,但长时间运行的任务在对话框再次关闭之前不会执行:

private async void StartLongRunningTask2Sync() {
var wndHandle = Application.Current.Windows.OfType<Window>().SingleOrDefault(x => x.IsActive);
if (wndHandle == null)
{
    return;
}

IntPtr windowHandle = new WindowInteropHelper(wndHandle).Handle;
var progressWindow = new ProgressBarCustomized(windowHandle)
{
    Value = 0, CanCancel = true, CanRetry = false, Thumbnail = null, IsIndeterminate = true
};
progressWindow.ShowDialog();

await Task.Run(() => this.longRunningTaskComponent.DoLongRunningTask(this.taskIterations, this.iterationSleepTime));
progressWindow.Close();
private async nongrunningtask2sync(){
var wndHandle=Application.Current.Windows.OfType().SingleOrDefault(x=>x.IsActive);
if(wndHandle==null)
{
返回;
}
IntPtr windowHandle=新的WindowInteropHelper(wndHandle).Handle;
var progressWindow=新ProgressBarCustomized(windowHandle)
{
值=0,CanCancel=true,CanRetry=false,Thumbnail=null,IsIndeterminate=true
};
progressWindow.ShowDialog();
wait Task.Run(()=>this.longRunningTaskComponent.DoLongRunningTask(this.taskIterations,this.iterationSleepTime));
progressWindow.Close();
}

所以我尝试了这种方法:

private async void StartLongRunningTask2Sync() {
var wndHandle = Application.Current.Windows.OfType<Window>().SingleOrDefault(x => x.IsActive);
if (wndHandle == null)
{
    return;
}

IntPtr windowHandle = new WindowInteropHelper(wndHandle).Handle;
var progressWindow = new ProgressBarCustomized(windowHandle)
{
    Value = 0, CanCancel = true, CanRetry = false, Thumbnail = null, IsIndeterminate = true
};
Task.Run(() => progressWindow.ShowDialog());

await Task.Run(() => this.longRunningTaskComponent.DoLongRunningTask(this.taskIterations, this.iterationSleepTime));
progressWindow.Close();
private async nongrunningtask2sync(){
var wndHandle=Application.Current.Windows.OfType().SingleOrDefault(x=>x.IsActive);
if(wndHandle==null)
{
返回;
}
IntPtr windowHandle=新的WindowInteropHelper(wndHandle).Handle;
var progressWindow=新ProgressBarCustomized(windowHandle)
{
值=0,CanCancel=true,CanRetry=false,Thumbnail=null,IsIndeterminate=true
};
Task.Run(()=>progressWindow.ShowDialog());
wait Task.Run(()=>this.longRunningTaskComponent.DoLongRunningTask(this.taskIterations,this.iterationSleepTime));
progressWindow.Close();
}

执行此操作时,会出现以下错误: 调用线程无法访问此对象,因为其他线程拥有它

在调查自定义进度窗口后,我发现调用“base.ShowDialog()”会引发此错误

有没有办法做我喜欢做的事情,或者我必须用完全不同的方法来做? 致意

更新: 是的,我已经搜索了这个错误,是的,我已经用Dispatcher.Invoke()等尝试了几种方法

因此,“真正”的问题是: 如何在长时间运行的任务正在运行时显示阻塞窗口,并在长时间运行的任务完成后关闭它,并最终通知窗口操作的进度。解决方案应该(最好)使用MVVM模式,而不是依赖(太多)代码隐藏

调用线程无法访问此对象,因为其他线程拥有它

这是一个非常常见的错误,如果你在网上搜索,你会发现一个非常简单的解释

不能在非UI线程上操作UI对象

解决办法很简单。不要试图在非UI线程上打开对话框
窗口


如果你能澄清你的实际问题是什么(通过编辑你的问题,而不是通过评论),那么我可以进一步提供帮助。

我想我在这里找到了一个几乎可行的解决方案:

我唯一要做的就是在显示对话框时关闭主窗口

所以“真正”的问题是:如何在长时间运行的任务运行时显示阻塞窗口,并在长时间运行的任务完成后关闭它,并最终通知窗口操作的进度

你已经得到了大部分作品;你只需要把它们放在一起

如何显示阻挡窗口

所有UI都应该在一个GUI线程上运行。这不是严格必要的,但它是一个非常简单的工具,适用于绝大多数应用程序。“阻塞窗口”在UI世界中称为“模式对话框”,您可以通过调用
ShowDialog
来显示它

// Start the long-running operation
var task = LongRunningOperationAsync();

// Show the dialog
progressWindow.ShowDialog();

// Retrieve results / propagate exceptions
var results = await task;
在长时间运行的任务完成后关闭它

为此,您需要连接任务的完成以关闭窗口。使用
async
/
wait
,这非常简单:

async Task DoOperationAsync(ProgressWindow progressWindow)
{
  try
  {
    await LongRunningOperationAsync();
  }
  finally
  {
    progressWindow.Close();
  }
}

// Start the long-running operation
var task = DoOperationAsync(progressWindow);

// Show the dialog
progressWindow.ShowDialog();

// Retrieve results / propagate exceptions
var results = await task;
通知窗口操作的进度

假设您的操作使用标准的
IProgress
界面来报告进度:

async Task DoOperationAsync(Window progressWindow, IProgress<int> progress)
{
  try
  {
    await LongRunningOperationAsync(progress);
  }
  finally
  {
    progressWindow.Close();
  }
}

var progressWindowVM = ...;
var progress = new Progress<int>(value =>
{
  progressWindowVM.Progress = value;
});

var task = DoOperationAsync(progressWindow, progress);
progressWindow.ShowDialog();
var results = await task;
解决方案应该(最好)使用MVVM模式,而不是依赖(太多)代码隐藏

MVVM在一个窗口内运行良好。一旦您开始尝试数据绑定窗口级别的操作和属性,很多操作和属性就会崩溃。这并不是因为MVVM是一种糟糕的模式,而是因为很多MVVM框架不能很好地处理这一问题


上面的示例代码仅使用数据绑定向进度对话框报告进度。如果您的MVVM框架可以数据绑定模式窗口的显示/隐藏,那么您可以使用my来驱动它。另外,有些框架有一种更优雅的(MVVM)方式来处理
窗口。已关闭
,但细节取决于您的框架。

您好,Stephen,非常感谢您提供此逐步教程。很好!:-)祝您一切顺利。是否正在从非GUI线程安全模式调用
progressWindow.Close()
?我认为windows的大多数标准实现都禁止这样做。@КББКС:我发布的代码将从UI线程调用
progressWindow.Close
async Task DoOperationAsync(Window progressWindow, CancellationToken token, IProgress<int> progress)
{
  try
  {
    await LongRunningOperationAsync(token, progress);
  }
  catch (OperationCanceledException) { }
  finally
  {
    progressWindow.Close();
  }
}

var progressWindowVM = ...;
var progress = new Progress<int>(value =>
{
  progressWindowVM.Progress = value;
});

var cts = new CancellationTokenSource();
progressWindow.Closed += (_, __) => cts.Cancel();

var task = DoOperationAsync(progressWindow, cts.Token, progress);
progressWindow.ShowDialog();
var results = await task;