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: 