Yan*_*nik 3 wpf modal-dialog thread-safety
我必须实施忙碌指示和进度报告.约束是,我必须使用提供的控制库,它提供了一个进度报告窗口.
以下代码工作正常,但不会阻止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();
Run Code Online (Sandbox Code Playgroud)
}
阻止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();
Run Code Online (Sandbox Code Playgroud)
}
所以我尝试了这种方法:
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();
Run Code Online (Sandbox Code Playgroud)
}
执行此操作时,我收到以下错误:调用线程无法访问此对象,因为另一个线程拥有它.
在调查我发现的自定义进度窗口后,调用"base.ShowDialog()"会抛出此错误.
有没有办法做我喜欢或做的事情我必须用完全不同的方法做到这一点?最好的祝福
更新:是的,我已经搜索了这个错误,是的,我已经使用Dispatcher.Invoke()等尝试了几种方法......
所以"真正的"问题:如何在长时间运行的任务运行时显示阻塞窗口,并在长时间运行的任务完成后关闭它,并最终通知窗口有关操作进度的信息.解决方案应该(最好)使用MVVM模式,而不是依赖(太多)代码.
Ste*_*ary 13
所以"真正的"问题:如何在长时间运行的任务运行时显示阻塞窗口,并在长时间运行的任务完成后关闭它,并最终通知窗口有关操作进度的信息.
你已经获得了大部分作品; 你只需要把它们放在一起.
如何显示阻止窗口
所有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;
Run Code Online (Sandbox Code Playgroud)
在长时间运行的任务完成后关闭它
为此,您需要连接完成任务以关闭窗口.使用async/ 这非常简单await:
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;
Run Code Online (Sandbox Code Playgroud)
告知窗口有关行动的进展情况
假设您的操作使用标准IProgress<T>接口来报告进度:
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;
Run Code Online (Sandbox Code Playgroud)
另一个需要考虑的常见用例是,如果用户自己关闭进度对话框,则取消操作.同样,如果您的操作已经使用标准,这是直截了当的CancellationToken:
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;
Run Code Online (Sandbox Code Playgroud)
解决方案应该(最好)使用MVVM模式,而不是依赖(太多)代码.
MVVM在单个窗口中运行良好.一旦你开始尝试数据绑定窗口级别的操作和属性,它就会崩溃.这不是因为MVVM是一个糟糕的模式,而是因为很多MVVM框架都不能很好地处理这个问题.
上面的示例代码仅使用数据绑定将进度报告给进度对话框.如果您的MVVM框架可以数据绑定模式窗口的显示/隐藏,那么您可以使用我的NotifyTaskCompletion类型来驱动它.此外,一些框架具有更优雅(MVVM)的方式来处理Window.Closed,但细节取决于您的框架.
| 归档时间: |
|
| 查看次数: |
5762 次 |
| 最近记录: |