Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • World
  • Users
  • Groups
Skins
  • Light
  • Cerulean
  • Cosmo
  • Flatly
  • Journal
  • Litera
  • Lumen
  • Lux
  • Materia
  • Minty
  • Morph
  • Pulse
  • Sandstone
  • Simplex
  • Sketchy
  • Spacelab
  • United
  • Yeti
  • Zephyr
  • Dark
  • Cyborg
  • Darkly
  • Quartz
  • Slate
  • Solar
  • Superhero
  • Vapor

  • Default (No Skin)
  • No Skin
Collapse
Code Project
  1. Home
  2. General Programming
  3. C#
  4. Report Progress from a class with a backgroundWorker

Report Progress from a class with a backgroundWorker

Scheduled Pinned Locked Moved C#
tutorial
11 Posts 4 Posters 0 Views 1 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • M Offline
    M Offline
    MacRaider4
    wrote on last edited by
    #1

    Ok so I seem to have my backgroundWorkers worked out. However I can't figure out how to report progress back to the original form. I'll try to pice together the code to have this make sense. FYI I don't have anything in the progressUpdated even yet.

    // This is the main form (obviously)
    namespace MailTest
    {
    public partial class Form1 : Form
    {
    List<BGW> BGWs = new List<BGW>();
    public Form1()
    {
    InitializeComponent();

            for (int i = 0; i < 3; i++)
            {
                BGWs.Add(new BGW());
                BGWs\[i\].ProgressChanged += BGW\_ProgressChanged;
                BGWs\[i\].RunWorkerCompleted += BGW\_WorkerCompleted;
            }
    
            
            btnGetMessageInfo.Enabled = false;
            //btnCancelConnection.Enabled = false;
        }
        void BGW\_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            BGW bgw = (BGW)sender;
        }
        void BGW\_WorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            BGW bgw = (BGW)sender;
        }
        class BGW : BackgroundWorker
        {
            public BGW()
            {
                this.WorkerReportsProgress = true;
                this.WorkerSupportsCancellation = true;
            }
            protected override void OnDoWork(DoWorkEventArgs e)
            {
                base.OnDoWork(e);
                WriteMail.WriteAndParseMail();
            }
        }
    

    // Further down
    private void btnGetMessageInfo_Click(object sender, EventArgs e)
    {
    processNumberOfWorkers.howMayWorkers();
    MessageBox.Show("There are " + GlobalVars.intHowManyWorkers + " workers needed!");
    btnGetMessageInfo.Enabled = false;
    backgroundWorker2.RunWorkerAsync();
    //backgroundWorker1.RunWorkerAsync();
    BGWs[0].RunWorkerAsync();
    }

    // The class being called from the worker
    class WriteMail
    {
    public static void WriteAndParseMail()
    {
    int intEmail;
    bool bolSrvMsg;
    //Start of Test Connection
    // create a TCP Client for a TCP Connection
    string txtLogString = null;

            TcpClient tcpClient = new TcpClient();
    

    // further down where the report progress is
    if (percentComplete <= highestPercentageReached)
    {
    if (percentComplete == 0)

    L I 2 Replies Last reply
    0
    • M MacRaider4

      Ok so I seem to have my backgroundWorkers worked out. However I can't figure out how to report progress back to the original form. I'll try to pice together the code to have this make sense. FYI I don't have anything in the progressUpdated even yet.

      // This is the main form (obviously)
      namespace MailTest
      {
      public partial class Form1 : Form
      {
      List<BGW> BGWs = new List<BGW>();
      public Form1()
      {
      InitializeComponent();

              for (int i = 0; i < 3; i++)
              {
                  BGWs.Add(new BGW());
                  BGWs\[i\].ProgressChanged += BGW\_ProgressChanged;
                  BGWs\[i\].RunWorkerCompleted += BGW\_WorkerCompleted;
              }
      
              
              btnGetMessageInfo.Enabled = false;
              //btnCancelConnection.Enabled = false;
          }
          void BGW\_ProgressChanged(object sender, ProgressChangedEventArgs e)
          {
              BGW bgw = (BGW)sender;
          }
          void BGW\_WorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
          {
              BGW bgw = (BGW)sender;
          }
          class BGW : BackgroundWorker
          {
              public BGW()
              {
                  this.WorkerReportsProgress = true;
                  this.WorkerSupportsCancellation = true;
              }
              protected override void OnDoWork(DoWorkEventArgs e)
              {
                  base.OnDoWork(e);
                  WriteMail.WriteAndParseMail();
              }
          }
      

      // Further down
      private void btnGetMessageInfo_Click(object sender, EventArgs e)
      {
      processNumberOfWorkers.howMayWorkers();
      MessageBox.Show("There are " + GlobalVars.intHowManyWorkers + " workers needed!");
      btnGetMessageInfo.Enabled = false;
      backgroundWorker2.RunWorkerAsync();
      //backgroundWorker1.RunWorkerAsync();
      BGWs[0].RunWorkerAsync();
      }

      // The class being called from the worker
      class WriteMail
      {
      public static void WriteAndParseMail()
      {
      int intEmail;
      bool bolSrvMsg;
      //Start of Test Connection
      // create a TCP Client for a TCP Connection
      string txtLogString = null;

              TcpClient tcpClient = new TcpClient();
      

      // further down where the report progress is
      if (percentComplete <= highestPercentageReached)
      {
      if (percentComplete == 0)

      L Offline
      L Offline
      Luc Pattyn
      wrote on last edited by
      #2

      MacRaider4 wrote:

      I'm probably missing something obvious

      For some obscure reason there is a BackgroundWorker.WorkerReportsProgress property, and in good Microsoft tradition its default value is the least useful one. :)

      Luc Pattyn [Forum Guidelines] [My Articles] Nil Volentibus Arduum

      Please use <PRE> tags for code snippets, they preserve indentation, improve readability, and make me actually look at the code.

      M 1 Reply Last reply
      0
      • L Luc Pattyn

        MacRaider4 wrote:

        I'm probably missing something obvious

        For some obscure reason there is a BackgroundWorker.WorkerReportsProgress property, and in good Microsoft tradition its default value is the least useful one. :)

        Luc Pattyn [Forum Guidelines] [My Articles] Nil Volentibus Arduum

        Please use <PRE> tags for code snippets, they preserve indentation, improve readability, and make me actually look at the code.

        M Offline
        M Offline
        MacRaider4
        wrote on last edited by
        #3

        I thought the only two choices for that are true and false? which I have in :

        public BGW()
        {
        this.WorkerReportsProgress = true;
        this.WorkerSupportsCancellation = true;
        }

        is this where I get a lot of snickering behind my back :-D

        L 1 Reply Last reply
        0
        • M MacRaider4

          Ok so I seem to have my backgroundWorkers worked out. However I can't figure out how to report progress back to the original form. I'll try to pice together the code to have this make sense. FYI I don't have anything in the progressUpdated even yet.

          // This is the main form (obviously)
          namespace MailTest
          {
          public partial class Form1 : Form
          {
          List<BGW> BGWs = new List<BGW>();
          public Form1()
          {
          InitializeComponent();

                  for (int i = 0; i < 3; i++)
                  {
                      BGWs.Add(new BGW());
                      BGWs\[i\].ProgressChanged += BGW\_ProgressChanged;
                      BGWs\[i\].RunWorkerCompleted += BGW\_WorkerCompleted;
                  }
          
                  
                  btnGetMessageInfo.Enabled = false;
                  //btnCancelConnection.Enabled = false;
              }
              void BGW\_ProgressChanged(object sender, ProgressChangedEventArgs e)
              {
                  BGW bgw = (BGW)sender;
              }
              void BGW\_WorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
              {
                  BGW bgw = (BGW)sender;
              }
              class BGW : BackgroundWorker
              {
                  public BGW()
                  {
                      this.WorkerReportsProgress = true;
                      this.WorkerSupportsCancellation = true;
                  }
                  protected override void OnDoWork(DoWorkEventArgs e)
                  {
                      base.OnDoWork(e);
                      WriteMail.WriteAndParseMail();
                  }
              }
          

          // Further down
          private void btnGetMessageInfo_Click(object sender, EventArgs e)
          {
          processNumberOfWorkers.howMayWorkers();
          MessageBox.Show("There are " + GlobalVars.intHowManyWorkers + " workers needed!");
          btnGetMessageInfo.Enabled = false;
          backgroundWorker2.RunWorkerAsync();
          //backgroundWorker1.RunWorkerAsync();
          BGWs[0].RunWorkerAsync();
          }

          // The class being called from the worker
          class WriteMail
          {
          public static void WriteAndParseMail()
          {
          int intEmail;
          bool bolSrvMsg;
          //Start of Test Connection
          // create a TCP Client for a TCP Connection
          string txtLogString = null;

                  TcpClient tcpClient = new TcpClient();
          

          // further down where the report progress is
          if (percentComplete <= highestPercentageReached)
          {
          if (percentComplete == 0)

          I Offline
          I Offline
          Ian Shlasko
          wrote on last edited by
          #4

          Easy... Your BGWs are defined in Form1, while you're trying to access them from within WriteMail. I would suggest adding a "sender" parameter to WriteAndParseMail(), so you can pass the worker into it.

          protected override void OnDoWork(DoWorkEventArgs e)
          {
          base.OnDoWork(e);
          WriteMail.WriteAndParseMail(this);
          }

          And in the worker thread...

          highestPercentageReached = percentComplete + 1;
          string strCounter = currentMail + " of " + intEmail;
          (sender as BackgroundWorker).ReportProgress(percentComplete, strCounter);

          Proud to have finally moved to the A-Ark. Which one are you in?
          Author of the Guardians Saga (Sci-Fi/Fantasy novels)

          M 1 Reply Last reply
          0
          • M MacRaider4

            I thought the only two choices for that are true and false? which I have in :

            public BGW()
            {
            this.WorkerReportsProgress = true;
            this.WorkerSupportsCancellation = true;
            }

            is this where I get a lot of snickering behind my back :-D

            L Offline
            L Offline
            Luc Pattyn
            wrote on last edited by
            #5

            Sorry, I initially didn't read all your code, I just reported the most likely oversight. I looked through your code now, and I would say: 1. your ReportProgress method should get called, however it is empty. What makes you say it doesn't work? 2. your Mail stuff is static, having multiple BGW's active in WriteAndParseMail() is not safe, it requires data synchronization. 3. I'm not sure you can have multiple TcpClient instances operating concurrently like that. 4. your percentage stuff is incomplete, so I can't be sure it is correct. 5. for overall progress, I often use a timer (Windows.Forms.Timer) which just gathers the number and updates the GUI at fixed intervals, say once a second. That turns out to be easier than having to check for actual percentage changes (which is however more economical). :)

            Luc Pattyn [Forum Guidelines] [My Articles] Nil Volentibus Arduum

            Please use <PRE> tags for code snippets, they preserve indentation, improve readability, and make me actually look at the code.

            M 1 Reply Last reply
            0
            • L Luc Pattyn

              Sorry, I initially didn't read all your code, I just reported the most likely oversight. I looked through your code now, and I would say: 1. your ReportProgress method should get called, however it is empty. What makes you say it doesn't work? 2. your Mail stuff is static, having multiple BGW's active in WriteAndParseMail() is not safe, it requires data synchronization. 3. I'm not sure you can have multiple TcpClient instances operating concurrently like that. 4. your percentage stuff is incomplete, so I can't be sure it is correct. 5. for overall progress, I often use a timer (Windows.Forms.Timer) which just gathers the number and updates the GUI at fixed intervals, say once a second. That turns out to be easier than having to check for actual percentage changes (which is however more economical). :)

              Luc Pattyn [Forum Guidelines] [My Articles] Nil Volentibus Arduum

              Please use <PRE> tags for code snippets, they preserve indentation, improve readability, and make me actually look at the code.

              M Offline
              M Offline
              MacRaider4
              wrote on last edited by
              #6

              When I use the hard coded version I have:

                  private void backgroundWorker1\_ProgressChanged(object sender, ProgressChangedEventArgs e)
                  {
                      pgbWrite.Value = e.ProgressPercentage;
                      lblCurrentCount.Text = e.UserState.ToString();
                  }
              

              Of which does work (when it's not calling from the class), sorry I should have included that. What makes me say it doesn't work is when I uncomment that section of code out I get a error saying it's not in the current context. I have had 2 TcpClients running at the same time, so that doesn't seem to be a issue. Is this what you mean by percentage stuff being incomplete?

                          percentComplete = (int)((float)currentMail / (float)intEmail \* 100);
                          if (percentComplete <= highestPercentageReached)
                          {
                              if (percentComplete == 0)
                              {
                                  percentComplete = 1;
                              }
                              highestPercentageReached = percentComplete + 1;
                              string strCounter = currentMail + " of " + intEmail;
                              //backgroundWorker1.ReportProgress(percentComplete, strCounter);
                              BGWs\[0\].ReportProgress(percentComplete, strCounter);
                          }
              
              1 Reply Last reply
              0
              • I Ian Shlasko

                Easy... Your BGWs are defined in Form1, while you're trying to access them from within WriteMail. I would suggest adding a "sender" parameter to WriteAndParseMail(), so you can pass the worker into it.

                protected override void OnDoWork(DoWorkEventArgs e)
                {
                base.OnDoWork(e);
                WriteMail.WriteAndParseMail(this);
                }

                And in the worker thread...

                highestPercentageReached = percentComplete + 1;
                string strCounter = currentMail + " of " + intEmail;
                (sender as BackgroundWorker).ReportProgress(percentComplete, strCounter);

                Proud to have finally moved to the A-Ark. Which one are you in?
                Author of the Guardians Saga (Sci-Fi/Fantasy novels)

                M Offline
                M Offline
                MacRaider4
                wrote on last edited by
                #7

                I tried that but no luck, though I'm sure it's me. This was so much easier in VB...

                I 1 Reply Last reply
                0
                • M MacRaider4

                  I tried that but no luck, though I'm sure it's me. This was so much easier in VB...

                  I Offline
                  I Offline
                  Ian Shlasko
                  wrote on last edited by
                  #8

                  Oh, ya know what... I wonder if the BackgroundWorker only allows ReportProgress to be called during the DoWork... Technically, your routine is running AFTER DoWork completes... I really don't think it's intended to be run that way. The standard way to use a BackgroundWorker is to hook the DoWork EVENT, and put your code there. No need to subclass it, unless you really want to centralize those two boolean property settings. Just guessing here, of course... I've never seen the BW subclassed... Never had any need to do that.

                  Proud to have finally moved to the A-Ark. Which one are you in?
                  Author of the Guardians Saga (Sci-Fi/Fantasy novels)

                  M D 2 Replies Last reply
                  0
                  • I Ian Shlasko

                    Oh, ya know what... I wonder if the BackgroundWorker only allows ReportProgress to be called during the DoWork... Technically, your routine is running AFTER DoWork completes... I really don't think it's intended to be run that way. The standard way to use a BackgroundWorker is to hook the DoWork EVENT, and put your code there. No need to subclass it, unless you really want to centralize those two boolean property settings. Just guessing here, of course... I've never seen the BW subclassed... Never had any need to do that.

                    Proud to have finally moved to the A-Ark. Which one are you in?
                    Author of the Guardians Saga (Sci-Fi/Fantasy novels)

                    M Offline
                    M Offline
                    MacRaider4
                    wrote on last edited by
                    #9

                    Ok I've been doing some more reading, would this be better using a "thread" vs a BGW? Now granted I don't need to put this in a seperate class, however for what ever reason when I use BGWs[0].RunWorkerAsync(); vs backgroundWorker1.RunWorkerAsync(); I'm not able to have the "function" in the same class (the Form) thus why I moved it to a seperate class. Plus when I was getting into more advanced someone reccomended keeping as little as you can on your main form and using classes for as much as possible. Is this a good habbit, bad habbit or just personal preference? Where I work there are two of us, and he does all the C++ stuff and I do the VB (6+ years, though .net in only the last year) and just starting with C# (about 6 months ago). I have been able to subclass a BGW in VB but obviously not in C#, yet again if I don't have to not a big deal. Basically what I need to do is be able to run the same function up to 3 times concurrently. I believe if my memory serves me correctally each "occurance" of the function will have it's own local variables. I also believe that if I use a thread vs a bgw I can lock what I'm writing to and instead of throwing a error, the threads will wait until the current one is done updating. Lets say it's just lblOutput.Text = e.UserState.ToString(); (as if it were a bgw perhaps in a thread you can use the variable directally)? Does this help to explain what I'm trying to accomplish and maybe give you all a better idea of what path I should be taking?

                    I 1 Reply Last reply
                    0
                    • I Ian Shlasko

                      Oh, ya know what... I wonder if the BackgroundWorker only allows ReportProgress to be called during the DoWork... Technically, your routine is running AFTER DoWork completes... I really don't think it's intended to be run that way. The standard way to use a BackgroundWorker is to hook the DoWork EVENT, and put your code there. No need to subclass it, unless you really want to centralize those two boolean property settings. Just guessing here, of course... I've never seen the BW subclassed... Never had any need to do that.

                      Proud to have finally moved to the A-Ark. Which one are you in?
                      Author of the Guardians Saga (Sci-Fi/Fantasy novels)

                      D Offline
                      D Offline
                      DaveyM69
                      wrote on last edited by
                      #10

                      Ian Shlasko wrote:

                      I wonder if the BackgroundWorker only allows ReportProgress to be called during the DoWork

                      No, internally it uses a SendOrPostCallback delegate and an AsyncOperation. If the AsyncOperation is null (there is no active worker thread) then the delegate is called directly. If it's not null then the AsyncOperation's Post method is called with the delegate and args as parameters which automatically invokes it on the syncronization context that RunWorkerAsync existed in.

                      public void ReportProgress(int percentProgress, object userState)
                      {
                      if (!this.WorkerReportsProgress)
                      {
                      throw new InvalidOperationException(SR.GetString("BackgroundWorker_WorkerDoesntReportProgress"));
                      }
                      ProgressChangedEventArgs arg = new ProgressChangedEventArgs(percentProgress, userState);
                      if (this.asyncOperation != null)
                      {
                      this.asyncOperation.Post(this.progressReporter, arg);
                      }
                      else
                      {
                      this.progressReporter(arg);
                      }
                      }

                      Dave
                      Binging is like googling, it just feels dirtier. Please take your VB.NET out of our nice case sensitive forum. Astonish us. Be exceptional. (Pete O'Hanlon)
                      BTW, in software, hope and pray is not a viable strategy. (Luc Pattyn)

                      1 Reply Last reply
                      0
                      • M MacRaider4

                        Ok I've been doing some more reading, would this be better using a "thread" vs a BGW? Now granted I don't need to put this in a seperate class, however for what ever reason when I use BGWs[0].RunWorkerAsync(); vs backgroundWorker1.RunWorkerAsync(); I'm not able to have the "function" in the same class (the Form) thus why I moved it to a seperate class. Plus when I was getting into more advanced someone reccomended keeping as little as you can on your main form and using classes for as much as possible. Is this a good habbit, bad habbit or just personal preference? Where I work there are two of us, and he does all the C++ stuff and I do the VB (6+ years, though .net in only the last year) and just starting with C# (about 6 months ago). I have been able to subclass a BGW in VB but obviously not in C#, yet again if I don't have to not a big deal. Basically what I need to do is be able to run the same function up to 3 times concurrently. I believe if my memory serves me correctally each "occurance" of the function will have it's own local variables. I also believe that if I use a thread vs a bgw I can lock what I'm writing to and instead of throwing a error, the threads will wait until the current one is done updating. Lets say it's just lblOutput.Text = e.UserState.ToString(); (as if it were a bgw perhaps in a thread you can use the variable directally)? Does this help to explain what I'm trying to accomplish and maybe give you all a better idea of what path I should be taking?

                        I Offline
                        I Offline
                        Ian Shlasko
                        wrote on last edited by
                        #11

                        MacRaider4 wrote:

                        (as if it were a bgw perhaps in a thread you can use the variable directally)?

                        Nope, that limitation is universal to all types of multithreading in WinForms (And WPF, for that matter). No touching the GUI controls except from the GUI thread.

                        MacRaider4 wrote:

                        Plus when I was getting into more advanced someone reccomended keeping as little as you can on your main form and using classes for as much as possible. Is this a good habbit, bad habbit or just personal preference?

                        That's generally a good idea, except in really small tools when it would be complete overkill. The form is your connection to the GUI, and business logic goes elsewhere... In your particular situation, I think I would probably handle it something like this: (Consider this C#ish pseudocode, as I don't have VS installed on my home machine at the moment)

                        // This can go in a static class somewhere
                        public static void SendMessagesAsync(ProgressChangedHandler progressCallback, params SomeClass[] messages)
                        {
                        for (int idx = 0; idx < messages.Length; idx++)
                        {
                        BackgroundWorker wkr = new BackgroundWorker();
                        wkr.WorkerSupportsProgress = true;
                        wkr.ProgressChanged += progressCallback; // This will go back to your main form and display something
                        wkr.DoWorkEventArgs += wkr_DoWork; // This is inside the same static class, below, not part of its public interface
                        wkr.RunWorkerAsync(messages[idx]);
                        }
                        }

                        // All of your workers can use the same DoWork, as long as they're given different arguments
                        private static void wkr_DoWork(object sender, DoWorkEventArgs e)
                        {
                        BackgroundWorker wkr = sender as BackgroundWorker; // Use this to report progress
                        SomeClass args = e.Argument as SomeClass; // Here's any info you need to pass to the worker

                        // ... Do your processing here ... WriteMail, or whatever
                        }

                        // And on your form...

                        private void SomethingCalledFromYourForm()
                        {
                        MyStaticClass.SendMessagesAsync(wkr_ProgressChanged, new SomeClass[] {
                        new SomeClass() { Whatever = parameters, YouWant = toset },
                        new SomeClass() { Subject = someone, Body = "something else?", Recipient = "someone@somewhere.sometime" }
                        });
                        }

                        I don't know what you need to pass to the WriteMail function, but that's generally how you get information into a background worker... Make a class to hold it, and pass it as the argument to RunWorkerAsync().

                        Pro

                        1 Reply Last reply
                        0
                        Reply
                        • Reply as topic
                        Log in to reply
                        • Oldest to Newest
                        • Newest to Oldest
                        • Most Votes


                        • Login

                        • Don't have an account? Register

                        • Login or register to search.
                        • First post
                          Last post
                        0
                        • Categories
                        • Recent
                        • Tags
                        • Popular
                        • World
                        • Users
                        • Groups