【发布时间】:2012-09-12 18:25:38
【问题描述】:
我正在尝试构建一个进度/取消表单以在我的 WinForms 应用程序中使用,该应用程序运行任何可等待的“操作”,同时为用户提供一些进度信息和取消操作的机会。
因为表单是使用ShowDialog() 显示的,所以它是一个模态表单,可以很好地禁用下面的表单 - 所以我不需要乱用禁用其他表单上的所有控件。
他们实现它的方式,我完全希望你撕成碎片:-),是在Form.Load 事件处理程序期间等待操作的结果,然后在操作完成后关闭表单完成(无论是因为它运行完成、被取消还是引发异常)。
public partial class ProgressForm<T> : Form
{
private CancellationTokenSource _cancellationTokenSource = new CancellationTokenSource();
private Progress<string> _progress = new Progress<string>();
private Func<IProgress<string>, CancellationToken, Task<T>> _operation = null;
private Exception _exception = null;
private T _result = default(T);
public static T Execute(Func<IProgress<string>, CancellationToken, Task<T>> operation)
{
using (var progressForm = new ProgressForm<T>())
{
progressForm._operation = operation;
progressForm.ShowDialog();
if (progressForm._exception != null)
throw progressForm._exception;
else
return progressForm._result;
}
}
public ProgressForm()
{
InitializeComponent();
this._progress.ProgressChanged += ((o, i) => this.ProgressLabel.Text = i.ToString());
}
private async void ProgressForm_Load(object sender, EventArgs e)
{
try
{
this._result = await this._operation(this._progress, this._cancellationTokenSource.Token);
}
catch (Exception ex) // Includes OperationCancelledException
{
this._exception = ex;
}
this.Close();
}
private void CancelXButton_Click(object sender, EventArgs e)
{
if (this._cancellationTokenSource != null)
this._cancellationTokenSource.Cancel();
}
}
这是这样称呼的:
int numberOfWidgets = ProgressForm<int>.Execute(CountWidgets);
...CountWidgets() 是一个可等待的东西(在这种情况下,一个函数返回 Task<int>,带有适当的 IProgress 和 CancellationToken 参数)。
到目前为止,它运行良好,但我想添加一个“功能”。理想情况下,我希望表单在(比如说)一秒钟内保持不可见,这样如果操作完成得非常快,就不会在表单显示时出现“闪烁”,然后立即再次隐藏。
所以,我的问题是如何在表格显示之前引入 1s 延迟。显然,我仍然想立即开始操作,但是一旦我“等待”操作的结果,我就不再控制(可以这么说),因为控制权将返回给@987654328 的调用者@事件处理程序 - 它将继续显示表单的工作。
我怀疑本质上我真的需要第二个线程,并且我需要在阻塞主 UI 线程时在该线程上执行操作。 (我知道阻塞 UI 线程是不受欢迎的,但在这种情况下,我认为这实际上是我需要的)。
创建线程等的方法有很多,我不知道在新的“异步/等待”世界中如何做到这一点......
【问题讨论】:
-
目前还不清楚它是如何工作的 - 我们没有足够的上下文来说明您显示的代码在哪里适合您的其余代码。
-
@JonSkeet:已添加表单的完整代码。
-
那么,你想要 1 秒的延迟,如果整个过程需要 1.5 秒呢?同样的问题仍然存在。你需要考虑其他事情。
-
如果执行时间太短,我会在进程完成后关闭对话框之前添加此延迟。
-
我们通过使用单例来做到这一点。因此,呼叫显示进度表集是一个计时器。停止显示进度会杀死表单或计时器。
标签: c# winforms multithreading async-await c#-5.0