Link to home
Create AccountLog in
Avatar of RohitYadav
RohitYadav

asked on

Background worker class help

Hi,
I am trying to use progress bar for my Application.
On a button Click event, i do some processing which takes nearly 10 seconds.During that time my form freezes. I wanted to use a progress bar to show the progress.
I came over Background worker class, i think this should be useful. I am fairly new to C# and have never used threading before. Anyone can help me get started on it?
Thanks very much.
Avatar of mastoo
mastoo
Flag of United States of America image

Do you have the Visual Studio documentation?  If you look at BackgroundWorker class, one of the topics is Examples.  It illustrates updating a textbox so the technique for a progress bar would be identical.
Avatar of RohitYadav
RohitYadav

ASKER

Could you please post the example or any link you have for it? unfortunately i don't have the documentation.
Thanks very much.
in vb.net..........

Imports System
Imports System.ComponentModel
Imports System.Threading
Imports System.Windows.Forms

Public Class Form1
   Inherits Form
   
   ' This delegate enables asynchronous calls for setting
   ' the text property on a TextBox control.
   Delegate Sub SetTextCallback([text] As String)

   ' This thread is used to demonstrate both thread-safe and
   ' unsafe ways to call a Windows Forms control.
   Private demoThread As Thread = Nothing

   ' This BackgroundWorker is used to demonstrate the
   ' preferred way of performing asynchronous operations.
   Private WithEvents backgroundWorker1 As BackgroundWorker

   Private textBox1 As TextBox
   Private WithEvents setTextUnsafeBtn As Button
   Private WithEvents setTextSafeBtn As Button
   Private WithEvents setTextBackgroundWorkerBtn As Button
   
   Private components As System.ComponentModel.IContainer = Nothing
   
   
   Public Sub New()
      InitializeComponent()
    End Sub
   
   
   Protected Overrides Sub Dispose(disposing As Boolean)
      If disposing AndAlso Not (components Is Nothing) Then
         components.Dispose()
      End If
      MyBase.Dispose(disposing)
    End Sub
   
   
   ' This event handler creates a thread that calls a
   ' Windows Forms control in an unsafe way.
    Private Sub setTextUnsafeBtn_Click( _
    ByVal sender As Object, _
    ByVal e As EventArgs) Handles setTextUnsafeBtn.Click

        Me.demoThread = New Thread( _
        New ThreadStart(AddressOf Me.ThreadProcUnsafe))

        Me.demoThread.Start()
    End Sub
   
   
   ' This method is executed on the worker thread and makes
   ' an unsafe call on the TextBox control.
   Private Sub ThreadProcUnsafe()
      Me.textBox1.Text = "This text was set unsafely."
   End Sub

   ' This event handler creates a thread that calls a
   ' Windows Forms control in a thread-safe way.
    Private Sub setTextSafeBtn_Click( _
    ByVal sender As Object, _
    ByVal e As EventArgs) Handles setTextSafeBtn.Click

        Me.demoThread = New Thread( _
        New ThreadStart(AddressOf Me.ThreadProcSafe))

        Me.demoThread.Start()
    End Sub
   
   
   ' This method is executed on the worker thread and makes
   ' a thread-safe call on the TextBox control.
   Private Sub ThreadProcSafe()
      Me.SetText("This text was set safely.")
    End Sub

   ' This method demonstrates a pattern for making thread-safe
   ' calls on a Windows Forms control.
   '
   ' If the calling thread is different from the thread that
   ' created the TextBox control, this method creates a
   ' SetTextCallback and calls itself asynchronously using the
   ' Invoke method.
   '
   ' If the calling thread is the same as the thread that created
    ' the TextBox control, the Text property is set directly.

    Private Sub SetText(ByVal [text] As String)

        ' InvokeRequired required compares the thread ID of the
        ' calling thread to the thread ID of the creating thread.
        ' If these threads are different, it returns true.
        If Me.textBox1.InvokeRequired Then
            Dim d As New SetTextCallback(AddressOf SetText)
            Me.Invoke(d, New Object() {[text]})
        Else
            Me.textBox1.Text = [text]
        End If
    End Sub

   ' This event handler starts the form's
   ' BackgroundWorker by calling RunWorkerAsync.
   '
   ' The Text property of the TextBox control is set
   ' when the BackgroundWorker raises the RunWorkerCompleted
   ' event.
    Private Sub setTextBackgroundWorkerBtn_Click( _
    ByVal sender As Object, _
    ByVal e As EventArgs) Handles setTextBackgroundWorkerBtn.Click
        Me.backgroundWorker1.RunWorkerAsync()
    End Sub
   
   
   ' This event handler sets the Text property of the TextBox
   ' control. It is called on the thread that created the
   ' TextBox control, so the call is thread-safe.
   '
   ' BackgroundWorker is the preferred way to perform asynchronous
   ' operations.
    Private Sub backgroundWorker1_RunWorkerCompleted( _
    ByVal sender As Object, _
    ByVal e As RunWorkerCompletedEventArgs) _
    Handles backgroundWorker1.RunWorkerCompleted
        Me.textBox1.Text = _
        "This text was set safely by BackgroundWorker."
    End Sub

   #Region "Windows Form Designer generated code"
   
   
   Private Sub InitializeComponent()
      Me.textBox1 = New System.Windows.Forms.TextBox()
      Me.setTextUnsafeBtn = New System.Windows.Forms.Button()
      Me.setTextSafeBtn = New System.Windows.Forms.Button()
      Me.setTextBackgroundWorkerBtn = New System.Windows.Forms.Button()
      Me.backgroundWorker1 = New System.ComponentModel.BackgroundWorker()
      Me.SuspendLayout()
      '
      ' textBox1
      '
      Me.textBox1.Location = New System.Drawing.Point(12, 12)
      Me.textBox1.Name = "textBox1"
      Me.textBox1.Size = New System.Drawing.Size(240, 20)
      Me.textBox1.TabIndex = 0
      '
      ' setTextUnsafeBtn
      '
      Me.setTextUnsafeBtn.Location = New System.Drawing.Point(15, 55)
      Me.setTextUnsafeBtn.Name = "setTextUnsafeBtn"
      Me.setTextUnsafeBtn.TabIndex = 1
      Me.setTextUnsafeBtn.Text = "Unsafe Call"
      '
      ' setTextSafeBtn
      '
      Me.setTextSafeBtn.Location = New System.Drawing.Point(96, 55)
      Me.setTextSafeBtn.Name = "setTextSafeBtn"
      Me.setTextSafeBtn.TabIndex = 2
      Me.setTextSafeBtn.Text = "Safe Call"
      '
      ' setTextBackgroundWorkerBtn
      '
      Me.setTextBackgroundWorkerBtn.Location = New System.Drawing.Point(177, 55)
      Me.setTextBackgroundWorkerBtn.Name = "setTextBackgroundWorkerBtn"
      Me.setTextBackgroundWorkerBtn.TabIndex = 3
      Me.setTextBackgroundWorkerBtn.Text = "Safe BW Call"
      '
      ' backgroundWorker1
      '
      '
      ' Form1
      '
      Me.ClientSize = New System.Drawing.Size(268, 96)
      Me.Controls.Add(setTextBackgroundWorkerBtn)
      Me.Controls.Add(setTextSafeBtn)
      Me.Controls.Add(setTextUnsafeBtn)
      Me.Controls.Add(textBox1)
      Me.Name = "Form1"
      Me.Text = "Form1"
      Me.ResumeLayout(False)
      Me.PerformLayout()
   End Sub 'InitializeComponent
   
   #End Region
   
   <STAThread()>  _
   Shared Sub Main()
      Application.EnableVisualStyles()
      Application.Run(New Form1())
    End Sub
End Class
in c#....

using System;
using System.ComponentModel;
using System.Threading;
using System.Windows.Forms;

namespace CrossThreadDemo
{
      public class Form1 : Form
      {
            // This delegate enables asynchronous calls for setting
            // the text property on a TextBox control.
            delegate void SetTextCallback(string text);

            // This thread is used to demonstrate both thread-safe and
            // unsafe ways to call a Windows Forms control.
            private Thread demoThread = null;

            // This BackgroundWorker is used to demonstrate the
            // preferred way of performing asynchronous operations.
            private BackgroundWorker backgroundWorker1;

            private TextBox textBox1;
            private Button setTextUnsafeBtn;
            private Button setTextSafeBtn;
            private Button setTextBackgroundWorkerBtn;

            private System.ComponentModel.IContainer components = null;

            public Form1()
            {
                  InitializeComponent();
            }

            protected override void Dispose(bool disposing)
            {
                  if (disposing && (components != null))
                  {
                        components.Dispose();
                  }
                  base.Dispose(disposing);
            }

            // This event handler creates a thread that calls a
            // Windows Forms control in an unsafe way.
            private void setTextUnsafeBtn_Click(
                  object sender,
                  EventArgs e)
            {
                  this.demoThread =
                        new Thread(new ThreadStart(this.ThreadProcUnsafe));

                  this.demoThread.Start();
            }

            // This method is executed on the worker thread and makes
            // an unsafe call on the TextBox control.
            private void ThreadProcUnsafe()
            {
                  this.textBox1.Text = "This text was set unsafely.";
            }

            // This event handler creates a thread that calls a
            // Windows Forms control in a thread-safe way.
            private void setTextSafeBtn_Click(
                  object sender,
                  EventArgs e)
            {
                  this.demoThread =
                        new Thread(new ThreadStart(this.ThreadProcSafe));

                  this.demoThread.Start();
            }

            // This method is executed on the worker thread and makes
            // a thread-safe call on the TextBox control.
            private void ThreadProcSafe()
            {
                  this.SetText("This text was set safely.");
            }

            // This method demonstrates a pattern for making thread-safe
            // calls on a Windows Forms control.
            //
            // If the calling thread is different from the thread that
            // created the TextBox control, this method creates a
            // SetTextCallback and calls itself asynchronously using the
            // Invoke method.
            //
            // If the calling thread is the same as the thread that created
            // the TextBox control, the Text property is set directly.

            private void SetText(string text)
            {
                  // InvokeRequired required compares the thread ID of the
                  // calling thread to the thread ID of the creating thread.
                  // If these threads are different, it returns true.
                  if (this.textBox1.InvokeRequired)
                  {      
                        SetTextCallback d = new SetTextCallback(SetText);
                        this.Invoke(d, new object[] { text });
                  }
                  else
                  {
                        this.textBox1.Text = text;
                  }
            }

            // This event handler starts the form's
            // BackgroundWorker by calling RunWorkerAsync.
            //
            // The Text property of the TextBox control is set
            // when the BackgroundWorker raises the RunWorkerCompleted
            // event.
            private void setTextBackgroundWorkerBtn_Click(
                  object sender,
                  EventArgs e)
            {
                  this.backgroundWorker1.RunWorkerAsync();
            }
            
            // This event handler sets the Text property of the TextBox
            // control. It is called on the thread that created the
            // TextBox control, so the call is thread-safe.
            //
            // BackgroundWorker is the preferred way to perform asynchronous
            // operations.

            private void backgroundWorker1_RunWorkerCompleted(
                  object sender,
                  RunWorkerCompletedEventArgs e)
            {
                  this.textBox1.Text =
                        "This text was set safely by BackgroundWorker.";
            }

            #region Windows Form Designer generated code

            private void InitializeComponent()
            {
                  this.textBox1 = new System.Windows.Forms.TextBox();
                  this.setTextUnsafeBtn = new System.Windows.Forms.Button();
                  this.setTextSafeBtn = new System.Windows.Forms.Button();
                  this.setTextBackgroundWorkerBtn = new System.Windows.Forms.Button();
                  this.backgroundWorker1 = new System.ComponentModel.BackgroundWorker();
                  this.SuspendLayout();
                  //
                  // textBox1
                  //
                  this.textBox1.Location = new System.Drawing.Point(12, 12);
                  this.textBox1.Name = "textBox1";
                  this.textBox1.Size = new System.Drawing.Size(240, 20);
                  this.textBox1.TabIndex = 0;
                  //
                  // setTextUnsafeBtn
                  //
                  this.setTextUnsafeBtn.Location = new System.Drawing.Point(15, 55);
                  this.setTextUnsafeBtn.Name = "setTextUnsafeBtn";
                  this.setTextUnsafeBtn.TabIndex = 1;
                  this.setTextUnsafeBtn.Text = "Unsafe Call";
                  this.setTextUnsafeBtn.Click += new System.EventHandler(this.setTextUnsafeBtn_Click);
                  //
                  // setTextSafeBtn
                  //
                  this.setTextSafeBtn.Location = new System.Drawing.Point(96, 55);
                  this.setTextSafeBtn.Name = "setTextSafeBtn";
                  this.setTextSafeBtn.TabIndex = 2;
                  this.setTextSafeBtn.Text = "Safe Call";
                  this.setTextSafeBtn.Click += new System.EventHandler(this.setTextSafeBtn_Click);
                  //
                  // setTextBackgroundWorkerBtn
                  //
                  this.setTextBackgroundWorkerBtn.Location = new System.Drawing.Point(177, 55);
                  this.setTextBackgroundWorkerBtn.Name = "setTextBackgroundWorkerBtn";
                  this.setTextBackgroundWorkerBtn.TabIndex = 3;
                  this.setTextBackgroundWorkerBtn.Text = "Safe BW Call";
                  this.setTextBackgroundWorkerBtn.Click += new System.EventHandler(this.setTextBackgroundWorkerBtn_Click);
                  //
                  // backgroundWorker1
                  //
                  this.backgroundWorker1.RunWorkerCompleted += new System.ComponentModel.RunWorkerCompletedEventHandler(this.backgroundWorker1_RunWorkerCompleted);
                  //
                  // Form1
                  //
                  this.ClientSize = new System.Drawing.Size(268, 96);
                  this.Controls.Add(this.setTextBackgroundWorkerBtn);
                  this.Controls.Add(this.setTextSafeBtn);
                  this.Controls.Add(this.setTextUnsafeBtn);
                  this.Controls.Add(this.textBox1);
                  this.Name = "Form1";
                  this.Text = "Form1";
                  this.ResumeLayout(false);
                  this.PerformLayout();

            }

            #endregion


            [STAThread]
            static void Main()
            {
                  Application.EnableVisualStyles();
                  Application.Run(new Form1());
            }

      }
}
Avatar of Fernando Soto
Hi RohitYadav;

This link has an example that uses a progress bar.
http://msdn2.microsoft.com/en-us/library/system.componentmodel.backgroundworker.aspx

This link is to the documentation for the Backgroundworker.
http://msdn2.microsoft.com/en-us/library/system.componentmodel.backgroundworker_members.aspx

If you have any other questions please ask.

Fernando
I just tried my hands on it without much reading. It throw an exception saying that text box was created by another thread and it cant be accessed.
Also can someone explain me the following events:

backgroundWorker1.DoWork += backgroundWorker1_DoWork;
           
backgroundWorker1.RunWorkerAsync("Message to worker");

Thanks nsanga and frenando for posting the links, I will just go over them also.

public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }
 
        private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
        {
            try
            {
                int i = 5;
                MessageBox.Show("Doing");
                textBox1.Text = i.ToString();
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.ToString());
            }
        }
 
        private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            MessageBox.Show("Done");
        }
 
        private void button1_Click(object sender, EventArgs e)
        {
            backgroundWorker1.DoWork += backgroundWorker1_DoWork;
            backgroundWorker1.RunWorkerAsync("Message to worker");
        }
 
        private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            MessageBox.Show("Done");
        }
    }

Open in new window

The DoWork method is running as the background thread so it can't directly access anything on the gui.  Both of these lines are bad there:

              MessageBox.Show("Doing");
              textBox1.Text = i.ToString();
 
You want to do that kind of stuff in the ProgressChanged method since it runs in the context of the main gui thread.
I have a method which i would be calling in the Do work method. And that method also has try, catch statements and the catch statement displays the exception in a message box.
But you said that MessgeBox.show is also a bad call ?
But when i run the above code it didnt show me any exception on the MEssageBox part.
Thanks again mastoo.
Hi,
I am having a bit trouble. I have a Function which populates tree, I want to call it in my Dowork function. Its a time consuming task thats why i dont want to call it this event.
Any help please?
Thank You,
void worker_DoWork(object sender, DoWorkEventArgs e)
        {
            InitializeSearh();
             PopulateTrees();
        }
        

Open in new window

Hi RohitYadav;

The code you posted seems correct are you getting an error?

Fernando
Its giving me an exception because I am accessing a Treeview object on the form, As somone earlier suggested :
"The DoWork method is running as the background thread so it can't directly access anything on the GUI"
This seems to be the problem.
I can pass my TreeView as an object RunWorkerAsync(). But the PopulateTree method is populating two treeViews. So i would need to pass two TreeViews to RunWorkerAsync. But it just accepts one.

How can i get around this? Or any other better way?
Thanks
Hi RohitYadav;

Here is sample code using your last post. I would not place MessageBox's in a thread because they will block the thread from executing until the user responds to it. So I would change it to another type of control on the UI and then call the ReportProgress and show it in a StatusBar control so that the thread is not blocked.

Fernando
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Diagnostics;
using System.Threading;
 
namespace WindowsApplication28
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }
 
        private void button1_Click(object sender, EventArgs e)
        {
            // This line of code is done when you add the BackgroundWork to the user interface
            // At design time
            // backgroundWorker1.DoWork += backgroundWorker1_DoWork;
            backgroundWorker1.WorkerReportsProgress = true;
            backgroundWorker1.RunWorkerAsync("Message to worker");
        }
 
        private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
        {
            List<String> reportStatus = new List<String>();
 
            try
            {
                int i = 5;
                MessageBox.Show("Doing");
                reportStatus.Clear();
                reportStatus.Add("TextBox1");
                reportStatus.Add(i.ToString());
                backgroundWorker1.ReportProgress(i, reportStatus);
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.ToString());
            }
        }
 
        private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            List<String> stat = (List<String>)e.UserState;
            switch (stat[0])
            {
                case "TextBox1":
                    textBox1.Text = stat[1];
                    break;
                case "ProgressBar1":
                    progressBar1.Value += Convert.ToInt32(stat[1]);
                    break;
                default :
                    Console.WriteLine("No such control");
                    break;
            }
        }
 
        private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            MessageBox.Show("Done");
        }
    }
}

Open in new window

In my sample code the first item in the list is the control to be updated and the second is the value to set it to.
                reportStatus.Clear();
                reportStatus.Add("TextBox1");
                reportStatus.Add(i.ToString());
It does not mater which is which as long as you stay consistent.
In reposnse to your comment id - 21061853, try this code and see if you still see the same error
public partial class Form1 : Form
{
	public Form1()
	{
		InitializeComponent();
	}
 
	private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
	{
		if( this.InvokeRequired )
			this.Invoke( new DoWorkEventHandler(sender, e) );
		else
		{}
		try
		{
			int i = 5;
			MessageBox.Show("Doing");
			textBox1.Text = i.ToString();
		}
		catch (Exception ex)
		{
			MessageBox.Show(ex.ToString());
		}
	}
 
	private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
	{
		MessageBox.Show("Done");
	}
 
	private void button1_Click(object sender, EventArgs e)
	{
		backgroundWorker1.DoWork += backgroundWorker1_DoWork;
		backgroundWorker1.RunWorkerAsync("Message to worker");
	}
 
	private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
	{
		MessageBox.Show("Done");
	}
}

Open in new window

Thanks,
I tried your code but i got a error. "Method name expected".
Sorry i am fairly new to C#, just trying my hands on this new Background worker at work.
I am having the same problem of Invoking the control.
I want to populate a TreeView using the Do work Event. I have a class which contains that method of populating tree.
Can anyone tell me how to achieve some easy explanation?
Thanks

 private void bWorker_DoWork(object sender, DoWorkEventArgs e)
        {
     
                if (!bWorker.CancellationPending)
                {
                    
                    bWorker.ReportProgress(0);
                    InitializeSmartUpdateSearh();
                    PopulateTrees()
                }
 
        }
 
 
private void PopulateTrees()
        {
            try
            {
                Smhelper.PopulateUpdatesTree(tView1, Smhelper.dt1);
                Smhelper.PopulateUpdatesTree(tViewNew2, Smhelper.dt2);
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

Open in new window

Basically what you're seeing is a threading issue. You're trying access the TreeView control created on GUI thread from a worker thread and hence the exception.

You will have to delegate this to the GUI thread so to supress the exception. I'm having little problems in helping you as you're using .NET 2.0 and I don't have it on my machine :(

Can you tell me what you see from the message popup?

private void bWorker_DoWork(object sender, DoWorkEventArgs e)
        {
     MessageBox.Show( this.InvokeRequired.ToString() );
                if (!bWorker.CancellationPending)
                {
                    
                    bWorker.ReportProgress(0);
                    InitializeSmartUpdateSearh();
                    PopulateTrees()
                }
 
        }
 

Open in new window

Yes you are right.
The MessageBox showed True.
Does it mean we have to invoke the Whole Form Control, because this refers to form i think.

Also when it goes inside the poplatetrees method this exception occours;
"Action being performed on this control is being called from the wrong thread. Marshal to the correct thread using Control.Invoke or Control.BeginInvoke to perform this action."
This is what you mentioned also.
So do we have to invoke the treeView?
Essentially what it means is the call was on different thread than the GUI thread and it requires us to transfer this call to the GUI thread.

Next step can you tell me whether you see this.Invoke() method signature, if yes give me those details.
Yes sir, i see this.invoke() method signature. It required a delegate method and a array of objects required for that method. I am not much familiar with delegate also.
I tried to do something similar looking at the code avalible on some site.
But that also gave me the same exception. I don't uinderstand it. :(

private delegate void PaintMessage(TreeView tView1, TreeView tView2);
        private PaintMessage myAddNode = null;
 
        public frmUpdateWizard()
        {
            try
            {
                InitializeComponent();
                myAddNode = new PaintMessage(this.PopulateTreeNewProductLines);
            }
            catch { }
        }
 
 
private void bWorker_DoWork(object sender, DoWorkEventArgs e)
        {
            MessageBox.Show(this.InvokeRequired.ToString());
 
            BeginInvoke(myAddNode, new object[] {tViewUpdatedProductLines, tViewNewProductLines});    
 
                        if (!bWorker.CancellationPending)
                {
                    
                    InitializeSmartUpdateSearh();
                    PopulateTreeNewProductLines(tViewUpdatedProductLines, tViewNewProductLines);
                                    }
            
        }

Open in new window

Please replace the MessageBox with the following code in backgroundWorker1_DoWork method

if( this.InvokeRequired )
        this.Invoke( new DoWorkEventHandler(backgroundWorker1_DoWork), new object[] {sender, e} );
Alright i tried to code. But i get back to the same problem that my window freezes while the operation is being performned.
Why are we doing Invoke on this? should be not just be invoking the controls that we need to process.
Also does this.invoke passes the job back to the main thread? is that why is it freezing.
I am getting exception in my code after adding your line.
"Failed to enable constraints. One or more rows contain values violating non-null, unique, or foreign-key constraints."
I was not having any such problem before.
Thanks very much for your help, can me please throw some light on the situation.
Appreciate your help.
Got it to work but as soon the code reaches begin invoke the application Freezes. Is the processing being passed on back to main thread?
Thanks.

private void bWorker_DoWork(object sender, DoWorkEventArgs e)
        {
            //try
            //{
            //if (this.InvokeRequired)
            //    this.Invoke(new DoWorkEventHandler(bWorker_DoWork), new object[] { sender, e });
 
        
                if (!bWorker.CancellationPending)
                {
                    bWorker.ReportProgress(0);
                    InitializeSmartUpdateSearh();
                    BeginInvoke(nm,new object[]{tViewUpdatedProductLines, tViewNewProductLines});
                    //PopulateTreeNewProductLines();
                    //Smhelper.PopulateUpdatesTree(TVie, Smhelper.dtProductLineUpdates);
                   // Smhelper.PopulateUpdatesTree(tViewNewProductLines, Smhelper.dtNewProductLine);
                }
            //}
            //catch (Exception ex)
            //{
            //    throw ex;
            //}
        }

Open in new window

I meant this
	private void bWorker_DoWork(object sender, DoWorkEventArgs e)
        {
            if (this.InvokeRequired)
                this.Invoke(new DoWorkEventHandler(bWorker_DoWork), new object[] { sender, e });
 
 	else
            // The code to perform the population
        }

Open in new window

I used Invoke instead of BeginInvoke and it worked.
begin invokes passes the processing back to the main thread while invoke maintains it on the same thread.
Am i understanding it right?
Thanks very much for your help.
Well actually both Invoke/BeginInvoke does the same thing except that Invoke is a synchronous call and BeingInvoke is Asynchronous call. If you're using BeginInvoke you will have to use EndInvoke to get the results of execution.

But what matters is you're solved the problem ;)
I tried your code, it still freezes the application.
private void bWorker_DoWork(object sender, DoWorkEventArgs e)
        {
                if (!bWorker.CancellationPending)
                {
                    if (this.InvokeRequired)
                    {
                        this.Invoke(new DoWorkEventHandler(bWorker_DoWork), new object[] { sender, e });
                    }
                    else
                    {
                        //bWorker.ReportProgress(0);
                        InitializeSmartUpdateSearh();
                        PopulateTreeNewProductLines(tViewNewProductLines, tViewUpdatedProductLines);
                    }
                }
                    //Invoke(nm,new object[]{tViewUpdatedProductLines, tViewNewProductLines});
        }

Open in new window

Yes its done for the work, but i still give more emphasis to understanding things rather then just getting them done.
If both does the same thing, then Why does the application Freezes in BeginInvoke then?
Is that it's freezing in both the Invoke and BeginInvoke cases or only one?

If it's synchronous call then it's expected to freeze the GUI till it completes populating the treeview as this is now the main thread. If it's BeginInvoke then it should actually hang/freeze.

Put a break point and see whether exactly your code is spending most of it's time.
Correct a typo
----------------
Is that it's freezing in both the Invoke and BeginInvoke cases or only one?

If it's synchronous call then it's expected to freeze the GUI till it completes populating the treeview as this is now the main thread. If it's BeginInvoke then it should actually hang/freeze.

Put a break point and see where exactly your code is spending most of it's time.
Ok As soon as the Invoke is called the application still freezes. Attached is the code which i used . The whole point of using Background worker was to avoid application from freezing while the things are getting processed.

 private void bWorker_DoWork(object sender, DoWorkEventArgs e)
        {
            if (!bWorker.CancellationPending)
            {
 
                bWorker.ReportProgress(0);
                InitializeSmartUpdateSearh();
                bWorker.ReportProgress(30);
                Invoke(nm,new object[]{tViewUpdatedProductLines, tViewNewProductLines});
                bWorker.ReportProgress(100);
            }
        }

Open in new window

may be you should go one level down in nm function and see where the time is being utilized most

Invoke(nm,new object[]{tViewUpdatedProductLines, tViewNewProductLines});
It calls a function which populates two Trees, First tree being populated with 20,000 Nodes and the other one with 1000 nodes. Most time is being utilized when we populate the Tree with 20,000 nodes.
Attached is the function which the Delegate calls.


private void PopulateTreeNewProductLines(TreeView tV1, TreeView tV2)
        {
            try
            {
                Smhelper.PopulateUpdatesTree(tV1, Smhelper.dt1);
                Smhelper.PopulateUpdatesTree(tV2, Smhelper.dt2);
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

Open in new window

Why don't you change call to BeginInvoke and see if there's any performance improvement.
I tried it, that also did the same thing. Still the application Freezes.
:(
I don't know why
please post your latest code
Here it is...
private void bWorker_DoWork(object sender, DoWorkEventArgs e)
        {
            if (!bWorker.CancellationPending)
            {
 
                bWorker.ReportProgress(0);
                InitializeSmartUpdateSearh();
                //bWorker.ReportProgress(30);
                BeginInvoke(new MyDelegate(PopulateTreeNewProductLines),new object[]{tViewUpdatedProductLines, tViewNewProductLines});
                //bWorker.ReportProgress(100);
            }
        }

Open in new window

Very much evident from the no. of records, can you prune the record nos to minimal and see the affect of the above code?
I dont think its because of the number of records, because the InitializeSmartUpdateSearh() method deals with similar Data.
Note: application dosent Freeze on InitializeSmartUpdateSearh
Its only after BeginInvoke it freezes.
put a break point in PopulateTreeNewProductLines method and see if reaches there and also exits.

or may be change the PopulateTreeNewProductLines method to

void PopulateTreeNewProductLines(param1, param2)
{
 return;
}
Yes it reaches there and exists because the application Freezes for 10 seconds and in the end i am able to see the populated tree.
I even verified it by putting breakpoints.
Somehow i want to avoid that Populating Tree function to process on main thread.
My understanding on threads is little so i apologize if i missed something or said something stupid.
Thanks
Hi RohitYadav;

Have you tried my code from my previous post on  03.06.2008 at 02:22PM EST, ID: 21063743 ?

The code has bee re-posted here.

Fernando
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Diagnostics;
using System.Threading;
 
namespace WindowsApplication28
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }
 
        private void button1_Click(object sender, EventArgs e)
        {
            // This line of code is done when you add the BackgroundWork to the user interface
            // At design time
            // backgroundWorker1.DoWork += backgroundWorker1_DoWork;
            backgroundWorker1.WorkerReportsProgress = true;
            backgroundWorker1.RunWorkerAsync("Message to worker");
        }
 
        private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
        {
            List<String> reportStatus = new List<String>();
 
            try
            {
                int i = 5;
                MessageBox.Show("Doing");
                reportStatus.Clear();
                reportStatus.Add("TextBox1");
                reportStatus.Add(i.ToString());
                backgroundWorker1.ReportProgress(i, reportStatus);
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.ToString());
            }
        }
 
        private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            List<String> stat = (List<String>)e.UserState;
            switch (stat[0])
            {
                case "TextBox1":
                    textBox1.Text = stat[1];
                    break;
                case "ProgressBar1":
                    progressBar1.Value += Convert.ToInt32(stat[1]);
                    break;
                default :
                    Console.WriteLine("No such control");
                    break;
            }
        }
 
        private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            MessageBox.Show("Done");
        }
    }
}

Open in new window

Yes Frenando i tried your code and it worked. Thanks very much for that. Now my Dowork events needs to make a call to a method which populates tree.
It just dosent let me populate tree using the background worker thread. I used begin invoke and invoke but it seems to pass the job to the main thread and application freezes until the processing is done.
ASKER CERTIFIED SOLUTION
Avatar of Fernando Soto
Fernando Soto
Flag of United States of America image

Link to home
membership
Create an account to see this answer
Signing up is free. No credit card required.
Create Account
This still pauses my application because when we call the PopulateTrees(), as  it passes the job back to UI thread. That dosent solve the problem. It keeps my application frozen for 10 seconds. Which i was trying to avoid. I have attached a very simpler version of my populate Tree function.
Somone suggested me BeginUpdate() and Endupdate(), that seems to be a good choice but i cant get it work yet.
If you have any suggestions please post it, i would also be posting my code if i get it solved.
Thanks all for helping

private void PopulateTree(TreeView tview)
{
   for(int x=0; x<1000; x++)
   {
      tview.Nodes.Add(i.ToString());
   }
}
 
 
 
 
Thanks

Open in new window

Hi RohitYadav;

As you stated the Backgrondworker is passing control to the UI thread and that is the bottleneck and the reason why I wanted to see your PopulateTree method. This code sample will create the TreeNode's in the Backgroundwork thread then pass it the UI thread to be inserted into the tree all at once with the tview.Nodes.AddRange method which will do a better job of filling the tree.

Fernando
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Diagnostics;
using System.Threading;
using System.Collections;
 
namespace WindowsApplication28
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }
 
        private void button1_Click(object sender, EventArgs e)
        {
            // Allow Backgroundworker to report progress
            backgroundWorker1.WorkerReportsProgress = true;
            backgroundWorker1.RunWorkerAsync("Message to worker");
        }
 
        private void PopulateTrees(TreeNode[] nodes)
        {
            // Clear the TreeView before Filling
            tview.Nodes.Clear();
            tview.Nodes.AddRange(nodes);
        }
 
        private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
        {
            // Data to send to the Report Progerss
            List<Object> reportStatus = new List<Object>();
 
            try
            {
                int i = 5;
                reportStatus.Clear();
                reportStatus.Add("TextBox1");
                reportStatus.Add(i.ToString());
                backgroundWorker1.ReportProgress(i, reportStatus);
 
                // Create Tree View Nodes in the BackgroundWorker thread so
                // not to tie up the UI thread. Then call the reportStatus 
                // to update the Tree View.
                // nCollection is to hold the neww nodes while creating them
                List<TreeNode> nCollection = new List<TreeNode>();
                for (int idx = 0; idx < 1000; idx++)
                {
                    // Create the nodes
                    nCollection.Add( new TreeNode(idx.ToString()));
                }
                // Copy list of nodes to an array of TreeNode's
                TreeNode[] nodes = new TreeNode[nCollection.Count];
                nCollection.CopyTo(nodes, 0);
                // Populate the tree view with the collection of nodes
                reportStatus.Clear();
                reportStatus.Add("PopulateTrees");
                reportStatus.Add(nodes);
                backgroundWorker1.ReportProgress(i, reportStatus);
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.ToString());
            }
        }
 
        private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            List<Object> stat = (List<Object>)e.UserState;
            switch (stat[0].ToString())
            {
                case "TextBox1":
                    textBox1.Text = stat[1].ToString();
                    break;
                case "ProgressBar1":
                    progressBar1.Value += Convert.ToInt32(stat[1]);
                    break;
                case "PopulateTrees":
                    PopulateTrees((TreeNode[])stat[1]);
                    break;
                default :
                    Console.WriteLine("No such control");
                    break;
            }
        }
 
        private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            MessageBox.Show("Done");
        }
    }
}

Open in new window

That worked i had to invoke on  Treeview.Nodes.clear and TreeView.Nodes.Add in the populateTree fuinction .
Frenando you approach to backgroud worker is quite unique. You definetly did provided a good solution. But my problem was a bit more compliacted As i had Nodes inside Nodes inside Nodes also.
Thanks very much evryone for all your help.
I am posting a simpler version of the code i used to Populate the tree.
public void PopulateUpdatesTree(TreeView tview)
        {
            try
            {
                if (tview.InvokeRequired)
                {
                    tview.Invoke(new MyDelegate3(ClearTreeNodes), new object[] { tview});
                }
                else
                {
                    tview.Nodes.Clear();
                }
   for(int x=0; x<1000; x++)
   {
      TreeNode tn=new TreeNode(i.tostring());
   
                
                            if (tview.InvokeRequired)
                            {
                                tview.Invoke(new MyDelegate2(AddToTreeView), new object[] { tview, tn});
                            }
                            else
                            {
 
                              tview.Nodes.Add(tn);
                            }
                        }
                    }
 
                }
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

Open in new window