我正在尝试构建一个进度/取消表单以在我的 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);
... whereCountWidgets()
是一个可以等待的东西(在这种情况下,一个函数返回Task<int>
,带有适当的 IProgress 和CancellationToken
参数)。
到目前为止,它工作得很好,但我想添加一个“功能”。理想情况下,我希望表单保持不可见(比如说)一秒钟,这样如果操作真的很快完成,就不会出现“闪烁”,因为表单显示然后立即再次隐藏。
所以,我的问题是如何在表格显示之前引入 1s 延迟。显然,我仍然想立即开始操作,但是一旦我“等待”操作的结果,我就不再控制(可以这么说),因为控制将返回给Form.Load
事件处理程序的调用者- 这将继续显示表格的工作。
我怀疑本质上我真的需要第二个线程,并且我需要在阻塞主 UI 线程时在该线程上执行操作。(我知道阻止 UI 线程是不受欢迎的,但在这种情况下,我认为这实际上是我需要的)。
有很多不同的方法可以创建线程等,我不确定如何在新的“async/await”世界中做到这一点......