patterncsharpMinor
Reporting progress to main form's controls from within a threaded heavy task
Viewed 0 times
reportingheavymaincontrolswithinprogressformfromthreadedtask
Problem
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:
As you can see, the right part of the
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.
I managed to break it apart like this, but it looks even worse:
It looks like I can't jus
Progress progress = new Progress();
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 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 jus
Solution
What you've provided is a lambda expression.
The Invoke method takes a delegate as an argument. Before I explain what a delegate is, the simple answer to your question is provide one of the predefined delegate types
What is a delegate?
A delegate plain and simple is a way to describe a method signature - that is, what does the method return (if anything) and what arguments (if any) does it take.
Another way to think about this is when I'm done doing my work I always notify someone to let them know the status of goals achieved. To make this process flexible I allow you to specify that unit of work. I will invoke the custom method and send it the status of my work. For this to happen obviously I need to set some general ground rules, the rules being that the method I invoke needs to take a string (the status I send you) and return a response to me (maybe that response is "ok go home" or etc).
The beauty of specifying only the method signature is that you can make your own custom method to be invoked (as you want to above) or you could specify any method that matches the signature. In our case string in, string out would match
Depending on your use case you might return void from the method because you just want an email to be kicked off once I internally invoke the method you've given to me.
You see a lot of this programming in thing like JavaScript, it is asynchronous, it returns control back to you right away and allows you to specify a callback function, essentially a delegate that should be invoked once it's done.
The Invoke method takes a delegate as an argument. Before I explain what a delegate is, the simple answer to your question is provide one of the predefined delegate types
Action or Func. These may seem like magic, but trust me, they are super simple concepts once you wrap your head around them.Invoke(new Action(() => {...})) Action takes no parameters and returns voidInvoke(new Func(() => { return false; })); Func returns T and has no parameters.Action and Func are simply predefined delegates. Action is defined like so:public delegate void Action();What is a delegate?
A delegate plain and simple is a way to describe a method signature - that is, what does the method return (if anything) and what arguments (if any) does it take.
Another way to think about this is when I'm done doing my work I always notify someone to let them know the status of goals achieved. To make this process flexible I allow you to specify that unit of work. I will invoke the custom method and send it the status of my work. For this to happen obviously I need to set some general ground rules, the rules being that the method I invoke needs to take a string (the status I send you) and return a response to me (maybe that response is "ok go home" or etc).
The beauty of specifying only the method signature is that you can make your own custom method to be invoked (as you want to above) or you could specify any method that matches the signature. In our case string in, string out would match
String.Reverse (just the name). Obviously in our scenario I may be confused by the response, but this way of programming allows you to plug in different implementations. One implementation could be that I notify you via phone call, or email, or text message etc. Depending on your use case you might return void from the method because you just want an email to be kicked off once I internally invoke the method you've given to me.
You see a lot of this programming in thing like JavaScript, it is asynchronous, it returns control back to you right away and allows you to specify a callback function, essentially a delegate that should be invoked once it's done.
Context
StackExchange Code Review Q#64498, answer score: 3
Revisions (0)
No revisions yet.