Improve company productivity with a Business Account.Sign Up

x
  • Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 1168
  • Last Modified:

C# Background task

Hi,

I have a background task which is updating progress bar that are located in different window.
How can I do it?

Thanks,
JT
0
jtran007
Asked:
jtran007
  • 15
  • 6
  • 5
  • +3
2 Solutions
 
anarki_jimbelCommented:
There are couple of good tutorial on a background worker:

http://dotnetperls.com/progressbar
http://dotnetperls.com/backgroundworker

If you start a bg process in a one form but need to update a progressbar in another form - just pas reference for this progresbar from one form to onother

0
 
dimajCommented:
You also have to test whether a control you are trying to update was created by the same thread otherwise you're gong to get exceptions...

This link shows how to deal with this: http://elegantcode.com/2009/07/03/wpf-multithreading-using-the-backgroundworker-and-reporting-the-progress-to-the-ui/
0
Upgrade your Question Security!

Your question, your audience. Choose who sees your identity—and your question—with question security.

 
jtran007Author Commented:
Hi Anarki,

You said pass reference from one for to the other. I don't understand what you mean.
Suppose in Form1, I have progressBar, and backgroundworker1. While backgroundworker1
is still running in the background, I switch to Form2 which has progressBar2. How backgroundworker1
know to update progressBar2. Please give me an example.

Thanks,
JT
0
 
jtran007Author Commented:
Hi,

Can one background task reports value to different progress bar locating in different forms?
If yes, how can it be done?

Thanks.
JT
0
 
dimajCommented:
Sure it can.

You can create an event and fire it off every time something has happened. The other form should have a reference to the first form and subscribe to the event that first form is generating.

Your event should send data such as what is the current task progress. When second form receives your event, it will get the data out of it and modify the value of your progress bar.

Does this make sense?
0
 
anarki_jimbelCommented:
OK, in the link I sent we have:

        private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            // Change the value of the ProgressBar to the BackgroundWorker progress.
            progressBar1.Value = e.ProgressPercentage;
            // Set the text.
            this.Text = e.ProgressPercentage.ToString();
        }

If you have a reference to a Form2 and it's ProgressBar2 (e.g.) - just do same!:

        private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            // Change the value of the ProgressBar to the BackgroundWorker progress.
            progressBar1.Value = e.ProgressPercentage;
            // it's another story how you get this reference!
            form2Instance.progressBar2.Value = e.ProgressPercentage;
            // Set the text.
            this.Text = e.ProgressPercentage.ToString();
        }

The trick is to get this reference. It shouldn't be too hard. Or may be I misunderstand your task...
0
 
dimajCommented:
While your approach will work (assuming you face dealt with threading issues), it's a better practice to use events since your structure becomes more portable
0
 
chandra_darbhaCommented:
When updating UI elements / controls from a Background process you should use Control.Invoke.

private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            if (progressBar1.InvokeRequired)
            {
                Action<ProgressChangedEventArgs> t = new Action<ProgressChangedEventArgs>(SetValues);
                progressBar1.Invoke(t, e);
            }
        }

        private void SetValues(ProgressChangedEventArgs e)
        {
            // Change the value of the ProgressBar to the BackgroundWorker progress.
            progressBar1.Text = e.ProgressPercentage;
            // Set the text.
            this.Text = e.ProgressPercentage.ToString();
        }

Thanks,
Chandra
0
 
jtran007Author Commented:
Hi Anarchi,

The code  // it's another story how you get this reference!
            form2Instance.progressBar2.Value = e.ProgressPercentage;

I use form2.progressBar2.value , but it is not working.
Please help.
JT
0
 
jtran007Author Commented:
Hi,

From backgroundworker, how can I ask GUI switch to another form?

Thanks,
JT
0
 
Mike TomlinsonMiddle School Assistant TeacherCommented:
@chandra_darbha...the BackgroundWorker() control events are ALREADY MARSHALED for you so using Invoke() in this case is redundant.

See: http://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker.aspx

    "You must be careful not to manipulate any user-interface objects in your DoWork event handler. Instead, communicate to the user interface through the ProgressChanged and RunWorkerCompleted events."

Thus if you wanted to update the GUI directly from the DoWork() handler (or from a manually created thread) then you'd need to use Invoke()/Delegats.

***********************************************************************

@jtran007,

I assume that in Form1, you are displaying Form2 with something like this?

    private void Foo()
    {
        Form2 f2 = new Form2();
        f2.Show();
    }

The scope of "f2" is limited to the method in which it resides.  You could move the reference up to the CLASS (Form1) level so that it can be accessed in the ProgressChanged() event:

public partial class Form1 : Form
    {

        private Form2 f2; // keep a reference to our instance of Form2 so we can access it!

        private void button1_Click(object sender, EventArgs e)
        {
            button1.Enabled = false;
            f2 = new Form2();
            f2.Show();
            backgroundWorker1.RunWorkerAsync();
        }

        private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            if (f2 != null)
            {
                // Change the Modifiers() property of "progressBar1" on Form2 to Public:
                f2.progressBar1.Value = e.ProgressPercentage;
            }
        }
    }

Open in new window

0
 
jtran007Author Commented:
Hi chandra,

There is simpler solution:

In teh bwgProgressChanged(...)
{
 progressBar.value = e.percentage; // one line of code
}

Regards,
JT
0
 
jtran007Author Commented:
Hi idle,

Your suggestion is not working. I tried it and the reference to progressbar on other form
is invalid.

Thanks,
JT
0
 
Mike TomlinsonMiddle School Assistant TeacherCommented:
How is it "invalid"?  
*You would have to change the Modifiers() Property of the ProgressBar to Public so it could be seen outside of the Form.

Otherwise I'm 100% sure the approach would work and the problem is either that your setup is different or you are trying to implement it incorrectly.

Show us more complete code if you still can't get it to work...

0
 
jtran007Author Commented:
Hi dimai,

I think your solution is the best. How can you priotize events, attached is the codes which show progress bar is always started first eventhough I check the text box before I fire the progressbar.

Program.cs
0
 
jtran007Author Commented:
HI dimai,

some mor files Form1.cs
0
 
jtran007Author Commented:
Hi dimai,

one more file, I don't know how to attacch more than one file.
Class1.cs
0
 
dimajCommented:
So, you were super close!

Here are the files
Class1.cs
Form1.cs
0
 
Mike TomlinsonMiddle School Assistant TeacherCommented:
You still don't need the Invoke() code though as you're raising the Class1 event from the ProgressChanged() event which is already marshaled to the UI thread (since Class1 was created from the Form1 thread).
0
 
dimajCommented:
While you are right, I used it as an example in case he wants to use a different approach.
0
 
jtran007Author Commented:
Hi Idle,

Sorry you are right. I did not change its modifiers to public. However I 'll take dimai as the final
solution. Thanks,

Jt
0
 
jtran007Author Commented:
Hi ,

I run into problem now. Since I use events to pass info b/w forms, however one of the object which
creates a thread to collect data from serial port, and use eventst to pass data to my form.  Since the thread can't pass info to GUI form even using event. Is this correct?

Thanks all,
JT

0
 
Mike TomlinsonMiddle School Assistant TeacherCommented:
The form can receive events on a different thread no problem.  If you need to update the GUI with that info, though, then use Invoke() with a Delegate as demonstrated by the others.

If you don't like using Invoke() then use the BackgroundWorker() instead of a manual thread.  You can also use a SynchronizationContext() instead of Invoke().
0
 
jtran007Author Commented:
Hi dimai,

Thanks. Your sample causes cross-thread operation:

   public void Foo()
        {
            // simply an example of raising the event:
            //this.Progress(25, "1/4 of the way there!");
            this.Message("1/4 of the way there!");
        }

and the cross-thread occurs here:
    void c1_Message(string msg)
        {
            textBox1.Text = msg;
        }

Since the foo happened in the backgroundworker (another thread) can't call  GUI control. Do I miss
something?

Thanks,
JT
0
 
dimajCommented:
Ohh!

I'm sorry... You have to follow the same structure as I used for c1_Progress...

if (textBox1.InvokeRequired) {
  this.Invoke(new MethodInvoker(delegate {textBox1.Text = msg; }));
}
else {
  textBox1.Text = msg;
}
0
 
jtran007Author Commented:
Hi dimai,

I found the meaning of your suggestion by changing the c1_Message as shown:

    void c1_Message(string msg)
        {
            if (textBox1.InvokeRequired)
            {
                this.Invoke(new MethodInvoker(delegate { textBox1.Text = msg; }));
            }
            else
            {
                textBox1.Text = msg;
            }
        }

It is working now. Thanks,
JT
0
 
jtran007Author Commented:
Hi idle..,

Is it possible you give a sample code using SynchronisationContext()?

Thanks,
JT
0
 
Mike TomlinsonMiddle School Assistant TeacherCommented:
Here's an example using a manual Thread and the SynchronizationContext:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Threading;

namespace WindowsFormsApplication1
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            button1.Enabled = false;
            Class1 c1 = new Class1();
            c1.Progress += new Class1.ProgressDelegate(c1_Progress);
            c1.Complete += new Class1.CompleteDelegate(c1_Complete);
            c1.Start();
        }

        private void c1_Progress(int val)
        {
            // ... when we get here we are already on the main UI thread ...
            progressBar1.Value = val;
        }

        private void c1_Complete()
        {
            // ... when we get here we are already on the main UI thread ...
            button1.Enabled = true;
            MessageBox.Show("Done!");
        }

    }

    public class Class1
    {

        public event ProgressDelegate Progress;
        public delegate void ProgressDelegate(int val);

        public event CompleteDelegate Complete;
        public delegate void CompleteDelegate();

        private Thread T = null;
        private SynchronizationContext SC = null;

        public Class1()
        {
            this.SC = System.Windows.Forms.WindowsFormsSynchronizationContext.Current;
        }

        public void Start()
        {
            if (this.T == null)
            {
                this.T = new Thread(new ThreadStart(this.Worker));
                this.T.IsBackground = true;
                this.T.Start();
            }
        }

        private void Worker()
        {
            int max = 10;
            for (int i = 0; i <= max; i++)
            {
                System.Threading.Thread.Sleep(500);

                if (this.SC != null)
                {
                    int percentage = (int)((double)i / (double)max * (double)100);
                    this.SC.Post(new SendOrPostCallback(this.RaiseProgressEvent), percentage);
                }
            }

            if (this.SC != null)
            {
                this.SC.Post(new SendOrPostCallback(this.RaiseCompleteEvent), null);
            }
        }

        private void RaiseProgressEvent(Object o)
        {
            // ... when we get here we are already on the main UI thread ...
            this.Progress((int)o);
        }

        private void RaiseCompleteEvent(Object o)
        {
            // ... when we get here we are already on the main UI thread ...
            this.T = null;
            this.Complete();
        }

    }

}

Open in new window

0
 
jtran007Author Commented:
Thanks,
JT
0
Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.

Join & Write a Comment

Featured Post

Get your problem seen by more experts

Be seen. Boost your question’s priority for more expert views and faster solutions

  • 15
  • 6
  • 5
  • +3
Tackle projects and never again get stuck behind a technical roadblock.
Join Now