In the following code I disable button before processing tasks and would like to enable it after all tasks are finished.
List<Task> tasks = new List<Task>();
buttonUpdateImage.Enabled = false; // disable button
foreach (OLVListItem item in cellsListView.CheckedItems)
{
Cell c = (Cell)(item.RowObject);
var task = Task.Factory.StartNew(() =>
{
Process p = new Process();
...
p.Start();
p.WaitForExit();
});
task.ContinueWith(t => c.Status = 0);
tasks.Add(task);
}
Task.WaitAll(tasks.ToArray());
// enable button here
WaitAll
is blocking the UI thread. How can I wait until all tasks finish and then enable the button?
First, I'd install Microsoft.Bcl.Async
which will enable the use of async-await
in .NET 4.0.
Now, using the answer to this question, you can asynchronously register for process exit, with no need to use Task.Factory.StartNew
:
public static class ProcessExtensions
{
public static Task RunProcessAsync(this Process process, string fileName)
{
if (process == null)
throw new ArgumentNullException(nameof(process));
var tcs = new TaskCompletionSource<bool>();
process.StartInfo = new ProcessStartInfo
{
FileName = fileName
};
process.EnableRaisingEvents = true
process.Exited += (sender, args) =>
{
tcs.SetResult(true);
process.Dispose();
};
process.Start();
return tcs.Task;
}
}
Now, you can do this:
buttonUpdateImage.Enabled = false; // disable button
var tasks = cellsListView.CheckedItems.Cast<OLVListItem>()
.Select(async item =>
{
Cell cell = (Cell)item.RowObject;
var process = new Process();
await process.RunProcessAsync("path");
cell.Status = 0;
});
await Task.WhenAll(tasks);
buttonUpdateImage.Enabled = true;
List<Task> tasks = new List<Task>();
buttonUpdateImage.Enabled = false; // disable button
foreach (OLVListItem item in cellsListView.CheckedItems)
{
Cell c = (Cell)(item.RowObject);
var task = Task.Factory.StartNew(() =>
{
Process p = new Process();
...
p.Start();
p.WaitForExit();
}).ContinueWith(t => c.Status = 0); // this modification is very important, Please note it, this will fix a problem
tasks.Add(task);
}
// your code has been modified to just make the task object
// is the continued task instead of the task is the original task
// Task.WaitAll and add this instead of it.
Task.Factory.ContinueWhenAll(tasks.ToArray(), ac => buttonUpdateImage.Enabled = true;);
Another way .....
// Note this object.
Barrier b = new Barrier(cellsListView.CheckedItems.Count, () => {
// enable the button here
buttonUpdateImage.Enabled = true;
});
buttonUpdateImage.Enabled = false; // disable button
foreach (OLVListItem item in cellsListView.CheckedItems)
{
Cell c = (Cell)(item.RowObject);
var task = Task.Factory.StartNew(() =>
{
Process p = new Process();
...
p.Start();
p.WaitForExit();
}).ContinueWith(t => {
c.Status = 0;
b.SignalAndWait();
});
}
the first way is better than the second, I advise you to use the first one.
You can create a new task to run WaitAll
method,just like
Task.Factory.StartNew(() =>
{
Task.WaitAll(tasks.ToArray());
Action act = () =>
{
this.button1.Enabled = true;
};
this.button1.Invoke(act);
});