I'm trying to construct a progress/cancel form for use within my WinForms application that runs any await-able "operation", while providing the user with some progress information and an opportunity to cancel the operation.
Because the form is shown using ShowDialog(), it's a modal form which nicely disables the form underneath - so I don't need to mess around with disabling all the controls on that other form.
They way I've implemented it, which I fully expect you to rip to shreds :-), is to await the result of the operation during the Form.Load event handler, and then close the form once the operation is either completed (whether that be because it ran to completion, was cancelled, or raise an exception).
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(); } } This is called like this:
int numberOfWidgets = ProgressForm<int>.Execute(CountWidgets); ...where CountWidgets() is an await-able thing (in this case a function returning Task<int>, with appropriate IProgress and CancellationToken parameters).
So far it works pretty well, but there's one "feature" that I'd like to add. Ideally, I'd like the form to remain invisible for (say) one second, so that if the operation completes really quickly there's no "flicker" as the form is shown and then immediately hidden again.
So, my question is how to introduce the 1s delay before the form is shown. Obviously, I still want to start the operation immediately, and yet as soon as I "await" the result of the operation, I'm no longer in control (so to speak) because control will be returned to the caller of the Form.Load event handler - which will continue the work of showing the form.
I suspect that essentially I really need a second thread, and that I need the operation to execute on that thread while I block the main UI thread. (I know that blocking the UI thread is frowned upon, but in this case I think it's actually what I need).
There are so many different ways of creating threads, etc. that I'm not sure how to do this in the new "async/await" world...