Solved

C# Background task

Posted on 2011-02-17
31
1,139 Views
Last Modified: 2013-12-17
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
Comment
Question by:jtran007
  • 15
  • 6
  • 5
  • +3
31 Comments
 
LVL 29

Expert Comment

by:anarki_jimbel
Comment Utility
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
 
LVL 11

Expert Comment

by:Sudhakar Pulivarthi
Comment Utility
0
 
LVL 7

Expert Comment

by:dimaj
Comment Utility
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
 

Author Comment

by:jtran007
Comment Utility
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
 

Author Comment

by:jtran007
Comment Utility
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
 
LVL 7

Accepted Solution

by:
dimaj earned 250 total points
Comment Utility
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
 
LVL 29

Expert Comment

by:anarki_jimbel
Comment Utility
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
 
LVL 7

Expert Comment

by:dimaj
Comment Utility
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
 
LVL 3

Expert Comment

by:chandra_darbha
Comment Utility
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
 
LVL 3

Expert Comment

by:chandra_darbha
Comment Utility
0
 

Author Comment

by:jtran007
Comment Utility
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
 

Author Comment

by:jtran007
Comment Utility
Hi,

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

Thanks,
JT
0
 
LVL 85

Expert Comment

by:Mike Tomlinson
Comment Utility
@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
 

Author Comment

by:jtran007
Comment Utility
Hi chandra,

There is simpler solution:

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

Regards,
JT
0
 

Author Comment

by:jtran007
Comment Utility
Hi idle,

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

Thanks,
JT
0
How your wiki can always stay up-to-date

Quip doubles as a “living” wiki and a project management tool that evolves with your organization. As you finish projects in Quip, the work remains, easily accessible to all team members, new and old.
- Increase transparency
- Onboard new hires faster
- Access from mobile/offline

 
LVL 85

Expert Comment

by:Mike Tomlinson
Comment Utility
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
 

Author Comment

by:jtran007
Comment Utility
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
 

Author Comment

by:jtran007
Comment Utility
HI dimai,

some mor files Form1.cs
0
 

Author Comment

by:jtran007
Comment Utility
Hi dimai,

one more file, I don't know how to attacch more than one file.
Class1.cs
0
 
LVL 7

Expert Comment

by:dimaj
Comment Utility
So, you were super close!

Here are the files
Class1.cs
Form1.cs
0
 
LVL 85

Expert Comment

by:Mike Tomlinson
Comment Utility
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
 
LVL 7

Expert Comment

by:dimaj
Comment Utility
While you are right, I used it as an example in case he wants to use a different approach.
0
 

Author Comment

by:jtran007
Comment Utility
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
 

Author Comment

by:jtran007
Comment Utility
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
 
LVL 85

Expert Comment

by:Mike Tomlinson
Comment Utility
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
 

Author Comment

by:jtran007
Comment Utility
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
 
LVL 7

Expert Comment

by:dimaj
Comment Utility
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
 

Author Comment

by:jtran007
Comment Utility
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
 

Author Comment

by:jtran007
Comment Utility
Hi idle..,

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

Thanks,
JT
0
 
LVL 85

Assisted Solution

by:Mike Tomlinson
Mike Tomlinson earned 250 total points
Comment Utility
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
 

Author Closing Comment

by:jtran007
Comment Utility
Thanks,
JT
0

Featured Post

Highfive + Dolby Voice = No More Audio Complaints!

Poor audio quality is one of the top reasons people don’t use video conferencing. Get the crispest, clearest audio powered by Dolby Voice in every meeting. Highfive and Dolby Voice deliver the best video conferencing and audio experience for every meeting and every room.

Join & Write a Comment

In my previous two articles we discussed Binary Serialization (http://www.experts-exchange.com/A_4362.html) and XML Serialization (http://www.experts-exchange.com/A_4425.html). In this article we will try to know more about SOAP (Simple Object Acces…
For those of you who don't follow the news, or just happen to live under rocks, Microsoft Research released a beta SDK (http://www.microsoft.com/en-us/download/details.aspx?id=27876) for the Xbox 360 Kinect. If you don't know what a Kinect is (http:…
This video shows how to remove a single email address from the Outlook 2010 Auto Suggestion memory. NOTE: For Outlook 2016 and 2013 perform the exact same steps. Open a new email: Click the New email button in Outlook. Start typing the address: …
This video demonstrates how to create an example email signature rule for a department in a company using CodeTwo Exchange Rules. The signature will be inserted beneath users' latest emails in conversations and will be displayed in users' Sent Items…

762 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

10 Experts available now in Live!

Get 1:1 Help Now