I have multiple classes that do lengthy tasks in threads and want them to output some kind of progress, so i can display it to a progress bar or a counter.
I could use an event or a delegate, defined in an interface, but it seems for every implementation I will need to write the exact same FireEvent code to check if the event is null and raise it, if it's not.
Using an abstract class seems bad too, since functionality like that does not belong in the top most class, which means I have to implement it at different places again.
How do I do that in the most reusable way, without duplicate code everywhere?
If you're using a BackgroundWorker
for your other threads, you can use the ReportProgress
method, which will raise the ProgressChanged
event.
http://msdn.microsoft.com/en-us/library/cc221403%28v=vs.95%29.aspx
I usually reverse the relationship between the view and the model so that the view knows about the model. In this example the progress dialog would have a reference to a IProgress
interface. It then hooks up to a ProgressChanged
event and the view can thus update itself when it needs to. The main advantage of this is the code inside the various classes are not duplicated - Only the code that tells how much is left is inside those classes. This way it is also very easy to clamp progress updates of classes that emit progress status very often.
Just to give you an idea what I usually do:
interface IProgress
{
event EventHandler ProgressChanged;
int ProgressTarget { get; }
int CurrentProgress { get; }
}
And a implementing class. I don't even know if it works as it should - It's just to give an impression on how to implement this interface.
class StreamCopier: IProgress
{
private Stream _source;
private Stream _destination;
public StreamCopier(Stream source, Stream destination)
{
_source = source;
_destination = destination;
}
public void WriteAll()
{
int b;
while ((b = _source.ReadByte()) != -1)
{
_destination.WriteByte((byte)b);
EventRaiser.Raise(ProgressChanged, this); // Just one call here! Can't be less
}
}
public event EventHandler ProgressChanged;
public int ProgressTarget {
get { return (int)_source.Length; }
}
public int CurrentProgress {
get { return (int)_destination.Position; }
}
}
And then the EventRaiser
class. Note how the handler reference is passed on the parameter stack, and therefor no thread-safe copy to a 'tmp' is necessary! :)
static class EventRaiser
{
public static void Raise(EventHandler handler, object sender, EventArgs args)
{
handler(sender, args);
}
public static void Raise(EventHandler handler, object sender)
{
Raise(handler, sender, EventArgs.Empty);
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With