I'm trying to figure out how to nicely write a Winforms app that reports the progress of completing a long task onto the main form's controls without causing Cross-thread op exceptions and such. So far I've come up with this scheme which at least works, but doesn't look very nice:
Progress<MyProgress> progress = new Progress<MyProgress>();
private void Form1_Load(object sender, EventArgs e)
{
progress.ProgressChanged += (x, y) =>
Invoke(new Action(() => { toolStripStatusLabel1.Text = y.text;
toolStripProgressBar1.Value = y.percentage; }));
}
bool DoVeryHeavyStuff(IProgress<MyProgress> progress)
{
progress.Report(new MyProgress() { percentage = 0, text = "Doing heavy stuff…" });
}
async void button_doHeavyStuff_Click(object sender, EventArgs e)
{
bool success = await Task.Run(() => DoVeryHeavyStuff(progress));
}
As you can see, the right part of the progress.ProgressChanged +=
looks overly complex. Isn't there a way it can be written more simply?
One thing I could think of is creating a separate method, but that wouldn't help much, as it would still have to be referred to in that line.
progress.ProgressChanged += (x, y) =>
Invoke(new Action(() => progress_ProgressChanged(x, y)));
void progress_ProgressChanged(object sender, MyProgress e)
{
toolStripStatusLabel1.Text = e.text;
toolStripProgressBar1.Value = e.percentage;
}
I managed to break it apart like this, but it looks even worse:
progress.ProgressChanged += (x, y) => ReportProgressA(x, y);
void ReportProgressA(object sender, MyProgress e)
{
Invoke(ReportProgressB(sender, e));
}
Action ReportProgressB(object sender, MyProgress e)
{
return () => progress_ProgressChanged(sender, e);
}
Invoke
seems to be essential to avoiding cross-thread exceptions, but it requires a delegate as a parameter. I don't have much experience with delegates, so I don't know how to simplify the Action initialization code.
It looks like I can't just drop the explicit cast to Action
for some reason: