I am trying to create a progress / cancel form for use in my WinForms application that launches any expected "operation", giving the user some progress information and the ability to cancel the operation.
Since the form is displayed using ShowDialog() , it is a modal form that nicely disables the form below - so I don't have to bother with disabling all controls in this other form.
How I implemented it, and I fully expect that you will tear to shreds :-), is to wait for the result of the operation during the Form.Load event Form.Load , and then close the form after the operation is either completed (either because that it ended, was canceled or an exception was thrown).
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)
This is called like this:
int numberOfWidgets = ProgressForm<int>.Execute(CountWidgets);
... where CountWidgets() is the expected thing (in this case, the function returns a Task<int> with the corresponding IProgress and CancellationToken parameters).
This works very well so far, but there is one “feature” that I would like to add. Ideally, I would like the form to remain invisible for (say) one second, so that if the operation completes very quickly, there is no “flicker” because the form is displayed and then immediately hides again.
So my question is how to introduce a delay of 1 s before the form is shown. Obviously, I still want to start the operation immediately, and yet, as soon as I “wait” for the result of the operation, I can no longer control (so to speak), because the control will be returned to the caller of Form.Load event handler, which will continue to work with showing the form.
I suspect that essentially I really need a second thread, and I need an operation to execute on that thread while I block the main UI thread. (I know that blocking the UI thread is underestimated, but in this case, I think this is really what I need).
There are so many different ways to create streams, etc., that I'm not sure how to do this in the new async / await world ...