views:

195

answers:

3

Creating a .net application in C#, windows forms. How do I update the progress bar 1 step every cycle of a 100 cycle loop? (I’m processing an excel sheet in the loop.) The progress bar controls are in the UI class which connects to the controller class which connects to a custom class (MVC pattern). The loop is in the custom class. Do I need to send the UI class instance all the way down in each method or is there a better way?

Right now the progress bar updates after the loop finishes. Application.doevents and .update or .refresh don’t work.

+1  A: 

I usually have one class that does the invocation checks on the UI. UI -> "UpdaterClass" -> other class.

The Updater class has predefined methods and references to the UI controls. So Updater.StepProgressBar() is what I call to step the UI progress bar. I pass the Updater class reference to any class that is going to need to update the UI directly.

This way, all UI updates from different threads are handled by one class. Not the most generic way to impliment it, but it never fails.

Example psuedocode:

class Updater()
{

  public ProgressBar pb;

  delegate void UpdateProgressBar();

  public StepProgressBar()
  {
     if(pb.InvokeRequired)
     {
          BeginInvoke(new UpdateProgressBar(this.StepProgressBar);
     }
     else
     {
          pb.Step();
      }
   }

}

Something like that.

Ryan Bennett
This will not be practical for me as I have a number of controller classes that access more than one UI and each controller has multiple libraries under it.
hi tech credo
+1  A: 

You could use a delegate. When your background process creates the custom class, tie on a delegate that gets called from the custom class to report update. Then you can react to that call in the UI layer and update the progress bar from there.

e.g. (warning, psuedocode):

MyCustomClass class = new MyCustomClass();
class.ProgressUpdated += (s,e)=>{ /* update UI */};
class.StartLoop();
Robaticus
uh sorry never used delegates before, I'll have to read up on them first.
hi tech credo
Whats the difference between delegate, event and threading?
hi tech credo
@Dan Tao has given a great example of how to do this. I suggest you do some outside reading on events and delegates before you try to do this. Background processing has its own sets of pitfalls that you need to know how to avoid before you get into anything too complex.
Robaticus
+4  A: 

Say the below is your class responsible for doing the work with the loop in it. Add an event to indicate your progress. Then from your UI simply handle that event and update the progress bar accordingly.

sealed class Looper
{
    public event EventHandler ProgressUpdated;

    private int _progress;
    public int Progress
    {
        get { return _progress; }
        private set
        {
            _progress = value;
            OnProgressUpdated();
        }
    }

    public void DoLoop()
    {
        _progress = 0;
        for (int i = 0; i < 100; ++i)
        {
            Thread.Sleep(100);
            Progress = i;
        }
    }

    private void OnProgressUpdated()
    {
        EventHandler handler = ProgressUpdated;
        if (handler != null)
        {
            handler(this, EventArgs.Empty);
        }
    }
}

You might implement this by having a BackgroundWorker as part of your UI, where in the backgroundWorker.DoWork event you call looper.DoLoop(). Then in your handler for the looper.ProgressUpdated event you can call backgroundWorker.ReportProgress to increment your progress bar from the UI thread.

Note that it would probably make more sense to include the progress itself in the information carried by your ProgressUpdated event (I just didn't feel like writing out a new class deriving from EventArgs to illustrate this; you probably get the picture anyway).

Also note that the above really doesn't make sense unless you're executing your code with the loop on a separate thread from the UI thread. Otherwise, all of your work is getting done before the next UI refresh anyway, so your progress bar would not be providing any value (it would just go from 0 to 100 when the loop completed).

Just an example of how this sort of thing can be achieved.

Dan Tao
Whats that called where you have the public struct controlling the private variable? Is that encapsulation?
hi tech credo
@hi tech credo: You mean `Progress`? That's just a .NET property, not a `struct`. Yes, it's an important *part* of encapsulation... sounds like there's probably a lot about this answer that might be new to you. Let me know if you run into problems or get confused and I'll do my best to help.
Dan Tao
Ok so my main thread will be running the UI, and I use the background worker to call my controller method, which in turn calls the method in custom class with the loop.Do I need to run a loop in the UI class to repeatedly call backgroundWorker.ReportProgress? or does it update automatically?Thankyou for your patient answer, I've never done threading before.
hi tech credo
your looper class should know nothing about the UI. Your UI code should add an event handler to the looper's OnProgressUpdated event. @Dan - thanks for doing the code I was too lazy-- uh, I mean too busy-to code out myself. :)
Robaticus
@hi tech credo: No, you don't want any sort of loop in your UI thread. The idea is to handle the `ProgressUpdated` event and react to it. To learn more about handling events, look for a good article on the subject such as this one: http://msdn.microsoft.com/en-us/library/aa645739(VS.71).aspx.
Dan Tao
Using BGW to call Excel methods is not a good idea. Excel is apartment threaded (STA), calling it from a worker thread actually makes it a lot slower.
Hans Passant
@Hans: My bad -- I completely missed that the OP is specifically trying to do something in Excel (not that I would've known about that anyway).
Dan Tao
@Hans: Whao, thats heavy. How does one maximise speed without compormising UI interactivity?
hi tech credo
@hi tech credo: Note that you don't have to create an instance of Excel and go the VSTO route just to process an XLS file. You might get some mileage from a library such as [Koogra](http://sourceforge.net/projects/koogra/) that allows you to manipulate the workbooks and spreadsheets in Excel files directly, without going through Excel itself. If you use this approach then I believe Hans's comment won't apply.
Dan Tao
I used ace.olebd and PIA to read all data and copy it into a data set. After manipulation I just write the whole result to a new xls file. Writing takes the longest time as I have to do it cell by cell.Update: I've got the background process running :) onto event handlers!
hi tech credo
I've placed my changedEventHandler in the UI class and the delegate and OnChanged methods in the controller class. In the UI class the changed method runs the background worker report progress method to update the progress bar. Now the onChanged method was placed one level deep in the controller class. How do I place the onChanged method 2 levels deep?I have one main progress bar updated as the major controller methods finish and a sub progress bar that should update based on the classes called by the controller.
hi tech credo