Reputation: 16340
I have a form that acts as a progress form, and I execute tasks as follows:
protected override void OnShown(object sender, EventArgs e)
{
try
{
await Task.Run(() =>
{
//Run task here...
});
}
catch (OperationCanceledException oex)
{ }
catch
{
throw;
}
finally
{
Close();
}
}
And the calling method is:
try
{
using (var progress = new ProgressForm(() =>
{
//The task to run async...
}))
{
progress.ShowDialog();
};
}
catch (MyCustomException cex)
{ }
catch (Exception ex)
{ }
A MyCustomException
is thrown by the task, so the progress form just rethrows it. However, back in the calling method, this exception isn't caught (caught in the catch (Exception ex)
block) because the exception it gets from the progress form is somehow TargetInvocationException
, and its InnerException
is of type MyCustomException
.
Why does this happen and is there a way to make sure that MyCustomException
thrown from the progress form reaches the calling method as is?
Upvotes: 1
Views: 109
Reputation: 316
This worked for me:
try
{
await Task.Run(() =>
{
//Run task here...
});
}
catch (AggregateException ex)
{
foreach (Exception inner in ex.InnerExceptions)
{
if (inner is MyCustomException)
{
//todo smt..
throw inner;
}
}
}
Upvotes: 1
Reputation: 3446
Maybe you can implement your ProgressForm in a way that it is shown async
and await to it.
A sample implementation could look like that one:
public class ProgressForm : Form
{
private ProgressForm()
{
}
public static async Task ShowAsync(Form owner, Action<IProgress<(string Message, int Progress)>> action)
{
owner.Enabled = false;
try
{
using (var frm = new ProgressForm { StartPosition = FormStartPosition.CenterParent })
{
frm.Show(owner);
try
{
var progress = new Progress<(string Message, int Progress)>(frm.OnProgress);
await Task.Run(() => action(progress));
}
finally
{
frm.Close();
}
}
}
finally
{
owner.Enabled = true;
}
}
private void OnProgress((string Message, int Progress) args)
{
// Report progress to this ProgressForm here.
// This line is only for demonstration. Please add controls to the form.
this.Text = $@"{args.Message} {args.Progress}%";
}
}
Please keep in mind that this will call Show
instead of ShowDialog
. The owner is disabled so that it behaves like a modal form (only for that parent!).
The usage of may look like:
private async void button1_Click(object sender, EventArgs e)
{
void DoSomeWork(IProgress<(string Message, int Progress)> progress = null)
{
var loops = 5;
for (var i = 0; i < loops; ++i)
{
progress?.Report(($"Loop {i + 1}", (i + 1) * 100 / loops));
Thread.Sleep(500);
}
throw new DivideByZeroException("...");
}
try
{
await ProgressForm.ShowAsync(this, DoSomeWork);
}
catch (DivideByZeroException)
{
MessageBox.Show("Yeah...");
}
}
Hope that helps.
This code performs a Thread.Sleep
in a Task.Run
! This is only a demonstration and I need a sample that consumes time.
Upvotes: 0