Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What ways are there for classes to provide progress information

Tags:

c#

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?

like image 670
Darcara Avatar asked Feb 22 '23 09:02

Darcara


2 Answers

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

like image 187
MGZero Avatar answered May 10 '23 07:05

MGZero


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);
    }
}
like image 33
vidstige Avatar answered May 10 '23 07:05

vidstige