PHP User Warning: fetch_template() calls should be replaced by the vB_Template class. Template name: bbcode_highlight in ..../includes/functions.php on line 4197

PHP User Warning: fetch_template() calls should be replaced by the vB_Template class. Template name: bbcode_highlight in ..../includes/functions.php on line 4197

PHP User Warning: fetch_template() calls should be replaced by the vB_Template class. Template name: bbcode_highlight in ..../includes/functions.php on line 4197

PHP User Warning: fetch_template() calls should be replaced by the vB_Template class. Template name: bbcode_highlight in ..../includes/functions.php on line 4197

PHP User Warning: fetch_template() calls should be replaced by the vB_Template class. Template name: bbcode_highlight in ..../includes/functions.php on line 4197

PHP User Warning: fetch_template() calls should be replaced by the vB_Template class. Template name: bbcode_highlight in ..../includes/functions.php on line 4197

PHP User Warning: fetch_template() calls should be replaced by the vB_Template class. Template name: bbcode_highlight in ..../includes/functions.php on line 4197

PHP User Warning: fetch_template() calls should be replaced by the vB_Template class. Template name: bbcode_highlight in ..../includes/functions.php on line 4197

PHP User Warning: fetch_template() calls should be replaced by the vB_Template class. Template name: bbcode_highlight in ..../includes/functions.php on line 4197

PHP User Warning: fetch_template() calls should be replaced by the vB_Template class. Template name: bbcode_highlight in ..../includes/functions.php on line 4197

PHP User Warning: fetch_template() calls should be replaced by the vB_Template class. Template name: bbcode_highlight in ..../includes/functions.php on line 4197

PHP User Warning: fetch_template() calls should be replaced by the vB_Template class. Template name: bbcode_highlight in ..../includes/functions.php on line 4197

PHP User Warning: fetch_template() calls should be replaced by the vB_Template class. Template name: bbcode_highlight in ..../includes/functions.php on line 4197

PHP User Warning: fetch_template() calls should be replaced by the vB_Template class. Template name: bbcode_highlight in ..../includes/functions.php on line 4197

PHP User Warning: fetch_template() calls should be replaced by the vB_Template class. Template name: bbcode_highlight in ..../includes/functions.php on line 4197

PHP User Warning: fetch_template() calls should be replaced by the vB_Template class. Template name: bbcode_highlight in ..../includes/functions.php on line 4197
Correct way to use the BackgroundWorker-VBForums
Results 1 to 27 of 27

Thread: Correct way to use the BackgroundWorker

  1. #1

    Thread Starter
    VB Addict Pradeep1210's Avatar
    Join Date
    Apr 2004
    Location
    Inside the CPU...
    Posts
    6,612

    Correct way to use the BackgroundWorker

    I see forum members often getting confused and asking about how the background worker should be correctly coded.

    Without going into much of the theory, I will demonstrate it with the help of an example. Pay attention the the comments in the code for explanation about each part of the code.

    For this demo, put a BackgroundWorker, two Labels and two Buttons on your form. The default names (BackgroundWorker1, Label1, Label2, Button1, Button2) apply. Otherwise change appropriately in the code, or the names of the Controls.

    Then put the following code:
    vb.net Code:
    1. Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
    2.     Button1.Text = "Start"
    3.     Button2.Text = "Cancel"
    4.     Label1.Text = ""
    5.     Label2.Text = ""
    6. End Sub
    7.  
    8. Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
    9.     Button1.Enabled = False
    10.     Button2.Enabled = True
    11.  
    12.     '' these properties should be set to True (at design-time or runtime) before calling the RunWorkerAsync
    13.     '' to ensure that it supports Cancellation and reporting Progress
    14.     BackgroundWorker1.WorkerSupportsCancellation = True
    15.     BackgroundWorker1.WorkerReportsProgress = True
    16.  
    17.     '' call this method to start your asynchronous Task.
    18.     BackgroundWorker1.RunWorkerAsync()
    19. End Sub
    20.  
    21. Private Sub Button2_Click(sender As System.Object, e As System.EventArgs) Handles Button2.Click
    22.     '' to cancel the task, just call the BackgroundWorker1.CancelAsync method.
    23.     Button2.Enabled = False
    24.     BackgroundWorker1.CancelAsync()
    25. End Sub
    26.  
    27. Private Sub BackgroundWorker1_DoWork(sender As Object, e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
    28.     '' The asynchronous task you want to perform goes here
    29.     '' the following is an example of how it typically goes.
    30.  
    31.     Const Max As Integer = 1000
    32.  
    33.     For i = 1 To Max
    34.         '' do something
    35.         '' (I put a sleep to simulate time consumed)
    36.         Threading.Thread.Sleep(100)
    37.  
    38.         '' report progress at regular intervals
    39.         BackgroundWorker1.ReportProgress(CInt(100 * i / Max), "Running..." & i.ToString)
    40.  
    41.         '' check at regular intervals for CancellationPending
    42.         If BackgroundWorker1.CancellationPending Then
    43.             BackgroundWorker1.ReportProgress(CInt(100 * i / Max), "Cancelling...")
    44.             Exit For
    45.         End If
    46.     Next
    47.  
    48.     '' any cleanup code go here
    49.     '' ensure that you close all open resources before exitting out of this Method.
    50.     '' try to skip off whatever is not desperately necessary if CancellationPending is True
    51.  
    52.     '' set the e.Cancel to True to indicate to the RunWorkerCompleted that you cancelled out
    53.     If BackgroundWorker1.CancellationPending Then
    54.         e.Cancel = True
    55.         BackgroundWorker1.ReportProgress(100, "Cancelled.")
    56.     End If
    57. End Sub
    58.  
    59. Private Sub BackgroundWorker1_ProgressChanged(sender As Object, e As System.ComponentModel.ProgressChangedEventArgs) Handles BackgroundWorker1.ProgressChanged
    60.     '' This event is fired when you call the ReportProgress method from inside your DoWork.
    61.     '' Any visual indicators about the progress should go here.
    62.     Label1.Text = CType(e.UserState, String)
    63.     Label2.Text = e.ProgressPercentage.ToString & "% complete."
    64. End Sub
    65.  
    66. Private Sub BackgroundWorker1_RunWorkerCompleted(sender As Object, e As System.ComponentModel.RunWorkerCompletedEventArgs) Handles BackgroundWorker1.RunWorkerCompleted
    67.     '' This event is fired when your BackgroundWorker exits.
    68.     '' It may have exitted Normally after completing its task,
    69.     '' or because of Cancellation, or due to any Error.
    70.  
    71.     If e.Error IsNot Nothing Then
    72.         '' if BackgroundWorker terminated due to error
    73.         MessageBox.Show(e.Error.Message)
    74.         Label1.Text = "Error occurred!"
    75.  
    76.     ElseIf e.Cancelled Then
    77.         '' otherwise if it was cancelled
    78.         MessageBox.Show("Task cancelled!")
    79.         Label1.Text = "Task Cancelled!"
    80.  
    81.     Else
    82.         '' otherwise it completed normally
    83.         MessageBox.Show("Task completed!")
    84.         Label1.Text = "Error completed!"
    85.     End If
    86.  
    87.     Button1.Enabled = True
    88.     Button2.Enabled = False
    89. End Sub

    Run the code and see that the Start and Cancel works smoothly.
    Pay attention to the comments, to adapt this stub as per your actual needs.


    [EDIT: updated with more information]
    (copied here from: http://www.vbforums.com/showpost.php...32&postcount=8)


    Passing more information than just a String value via ProgressChanged event
    Actually you should never try to access any Control inside the DoWork.

    There are just two ways you would need them inside the DoWork event.
    1. You want to pick up some value from some control.
    2. You want to update some control's value.


    To pick some value from any control, put such values in variables, so that you don't need to reference the controls from inside the DoWork event. Do that before you call the RunWorkerAsync method (which in turn fires the DoWork event).

    If you want to set something in any control, you should use the ReportProgress method to pass your values out of BackgroundWorker. The values you pass from ReportProgress can be accessed from the ProgressChanged event. You can assign those values to your controls or do anything else you want to do with them.
    So you do:
    Code:
    ' Inside DoWork event
    worker.ReportProgress(10, "Run coding 1")
    
    ' In ProgressChanged event
    Me.Label1.Text = CType(e.UserState, String)
    This was just a simple example where a string is passed.

    But in many cases you may want to pass more information than this to the ProgressChanged event.
    e.g. you have 10 labels, and you also want to pass which label's text property to update, along with the string value you want to set.

    Fortunately, the ReportProgress can pass a value of type Object to the ProgressChanged event. This means that you can pass virtually anything via that method. It can be as small as a string or number, or it can be a complex structure like some class object or array etc.

    Below is an example where I pass the control name and its value to set.

    1. Create a structure/class which can hold the control name (name of label) and the text value to set.
    Code:
    Public Structure ControlWithText
        Public ControlName As Control
        Public Text As String
    
        Public Sub New(ByVal ctrl As Control, ByVal text As String)
            Me.ControlName = ctrl
            Me.Text = text
        End Sub
    End Structure
    2. From inside the DoWork event, you pass an object instance of this structure.
    e.g.
    Code:
    worker.ReportProgress(10, New ControlWithText(Label1, "Run coding 1"))
    
    worker.ReportProgress(20, New ControlWithText(Label2, "Run coding 2"))
    3. Now in your ProgressChanged event, you will have this object available. Just get the control name and the text out of it and do your task appropriately.
    Code:
    If TypeOf e.UserState Is ControlWithText Then
        Dim cwt As ControlWithText = CType(e.UserState, ControlWithText)
        cwt.ControlName.Text = cwt.Text
    End If
    Last edited by Pradeep1210; May 31st, 2012 at 02:09 AM. Reason: added support for things more than just String
    Pradeep, Microsoft MVP (Visual Basic)
    Please appreciate posts that have helped you by clicking icon on the left of the post.
    "A problem well stated is a problem half solved." Charles F. Kettering

    Read articles on My Blog 101 LINQ Samples JSON Validator XML Schema Validator "How Do I" videos on MSDN VB.NET and C# Comparison Good Coding Practices VBForums Reputation Saver String Enum Super Simple Tetris Game


    (2010-2013)
    NB: I do not answer coding questions via PM. If you want my help, then make a post and PM me it's link. If I can help, trust me I will...

  2. #2
    Member
    Join Date
    May 2012
    Posts
    57

    Re: Correct way to use the BackgroundWorker

    Great tutorial, thank you!

    I see that i could use far more out of this way then i used to know it!

  3. #3

    Thread Starter
    VB Addict Pradeep1210's Avatar
    Join Date
    Apr 2004
    Location
    Inside the CPU...
    Posts
    6,612

    Re: Correct way to use the BackgroundWorker

    That's Great
    Pradeep, Microsoft MVP (Visual Basic)
    Please appreciate posts that have helped you by clicking icon on the left of the post.
    "A problem well stated is a problem half solved." Charles F. Kettering

    Read articles on My Blog 101 LINQ Samples JSON Validator XML Schema Validator "How Do I" videos on MSDN VB.NET and C# Comparison Good Coding Practices VBForums Reputation Saver String Enum Super Simple Tetris Game


    (2010-2013)
    NB: I do not answer coding questions via PM. If you want my help, then make a post and PM me it's link. If I can help, trust me I will...

  4. #4
    New Member
    Join Date
    Apr 2012
    Location
    Florida, USA
    Posts
    5

    Re: Correct way to use the BackgroundWorker

    Thanks for the great tutorial Pradeep1210!

  5. #5
    New Member
    Join Date
    May 2012
    Location
    Kuwait
    Posts
    5

    Re: Correct way to use the BackgroundWorker

    Perfect & Error-Less :-)

    Good job!
    Delivering the exact results at the moment of interest is no coincidence. It's magic!

  6. #6
    Frenzied Member
    Join Date
    Jul 2006
    Location
    MI
    Posts
    1,848

    Re: Correct way to use the BackgroundWorker

    Pradeep ... can you show an example of how to fill a DataGridView with the results of a query?

  7. #7
    Frenzied Member MattP's Avatar
    Join Date
    Dec 2008
    Location
    WY
    Posts
    1,227

    Re: Correct way to use the BackgroundWorker

    Quote Originally Posted by nbrege View Post
    Pradeep ... can you show an example of how to fill a DataGridView with the results of a query?
    You would simple fill the DataTable in the DoWork event.

    Once the DataTable is filled you'd pass it to the UserState parameter of ReportProgress. (The 2nd parameter)

    In the ProgressChanged event cast e.UserState to a DataTable and set it to the DataGridView's DataSource.
    This pattern in common to all great programmers I know: they're not experts in something as much as experts in becoming experts in something.

    The best programming advice I ever got was to spend my entire career becoming educable. And I suggest you do the same.

  8. #8
    New Member
    Join Date
    Feb 2013
    Posts
    7

    Re: Correct way to use the BackgroundWorker

    Hi,
    Thanks for great info.
    What about showing a message in another form, such as a none BorderStyleForm with a pic on it saying "please wait" or "loading" during the progress and closing the form after completion?

  9. #9

    Thread Starter
    VB Addict Pradeep1210's Avatar
    Join Date
    Apr 2004
    Location
    Inside the CPU...
    Posts
    6,612

    Re: Correct way to use the BackgroundWorker

    I was away from this forums for some time and couldn't reply back to the answers.
    Not sure if you already have your answer or not, but here's how we will do it:

    1. Create your form that shows the progress. You can design it whatever way you like. Say the name of that form is "ProgressForm".
    vb.net Code:
    1. Public Class ProgressForm
    2. ...
    3. ...
    4. ...
    5. End Class

    2. Create a public property/method in the ProgressForm that sets the text in the label (or whatever control you want to show the progress in):
    vb.net Code:
    1. Public Property ProgressText() As String
    2. Get
    3.     Return Label1.Text
    4. End Get
    5. Set(value As String)
    6.     Label1.Text = value
    7. End Set
    8. End Property

    3. Declare a variable of ProgressForm type at the top of your form (the main form on which your BackgroundWorker is):
    vb.net Code:
    1. Dim MyProgressForm as ProgressForm

    4. Before calling the BackgroundWorker's RunWorkerAsync() method, Show that form:
    vb.net Code:
    1. Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
    2. ...
    3. ...
    4. ...
    5.     '' call this method to start your asynchronous Task.
    6.     MyProgressForm.Show(Me)
    7.     BackgroundWorker1.RunWorkerAsync()
    8. End Sub
    5. Now in the BackgroundWorker's ProgressChanged event, set the property of your ProgressForm that shows appropriate message:
    vb.net Code:
    1. Private Sub BackgroundWorker1_ProgressChanged(sender As Object, e As System.ComponentModel.ProgressChangedEventArgs) Handles BackgroundWorker1.ProgressChanged
    2.     '' This event is fired when you call the ReportProgress method from inside your DoWork.
    3.     '' Any visual indicators about the progress should go here.
    4.  
    5.     MyProgressForm.ProgressText = CType(e.UserState, String)
    6.  
    7. End Sub

    6. After finishing your work, close that form in the RunWorkerCompleted event:
    vb.net Code:
    1. Private Sub BackgroundWorker1_RunWorkerCompleted(sender As Object, e As System.ComponentModel.RunWorkerCompletedEventArgs) Handles BackgroundWorker1.RunWorkerCompleted
    2. ...
    3. ...
    4. ...
    5.     MyProgressForm.Close()
    6. End Sub

    That's all that is required to be done.

    NOTE: All the above code is free-hand typed and may contain typos. It is only meant to explain the concept.
    Pradeep, Microsoft MVP (Visual Basic)
    Please appreciate posts that have helped you by clicking icon on the left of the post.
    "A problem well stated is a problem half solved." Charles F. Kettering

    Read articles on My Blog 101 LINQ Samples JSON Validator XML Schema Validator "How Do I" videos on MSDN VB.NET and C# Comparison Good Coding Practices VBForums Reputation Saver String Enum Super Simple Tetris Game


    (2010-2013)
    NB: I do not answer coding questions via PM. If you want my help, then make a post and PM me it's link. If I can help, trust me I will...

  10. #10
    New Member
    Join Date
    Feb 2013
    Posts
    7

    Re: Correct way to use the BackgroundWorker

    Pradeep1210 thanks for your reply,
    Now on I use this approach
    I have created a form with a picturebox in it and a gif file which shows "loading", named it as 'ProgressForm'
    In another class which BGW is defined and one of its method supposed to do time consuming jobs, the following are declared:
    Code:
    'at class level
         Private frmProgress As New ProgressForm
    
    'at start Button method body
        BGW.RunWorkerAsync() 
    
    In BGW_DoWork :
        worker.ReportProgress(100, New ProgressForm)  
        'Time consuming job goes here
    
    'In BGW_ProgressChanged : 
        If TypeOf e.UserState Is ProgressForm Then
            frmProgress.Show()
        End If
    
    'In BGW_RunWorkerCompleted : 
        frmProgress.Close()

    I know this approach is like yours, but I want to know whether there is a way to write it better or in a professional way.
    After all, thanks a lot for your time and explanation.
    Last edited by peymanebrahimi; Apr 23rd, 2013 at 05:11 AM.

  11. #11

    Thread Starter
    VB Addict Pradeep1210's Avatar
    Join Date
    Apr 2004
    Location
    Inside the CPU...
    Posts
    6,612

    Re: Correct way to use the BackgroundWorker

    The way you are doing it is completely different than what I showed you...

    It seems like you want to create a new instance of your ProgressForm from inside the worker thread, and use that in the ProgressChanged event. I won't recommend this approach. UI elements like any controls or forms must be created on the UI thread only, otherwise they will usually cause cross-thread operation problem. You don't get any errors at present because you never use that form instance. It is as good as passing a boolean value.
    Besides this, everytime you report the progress, a new instance of your ProgressForm will be created. And you never use that form instance! So you just keep on filling your memory space unnecessarily. It will ultimately get garbage-collected. But that will also consume your precious CPU cycles. This is not the correct approach anyways.

    The other problem is, you only show the form. You never give any UI updates. Calling the frmProgress.Show() method repeatedly will have no effect. It is same as calling it once.
    This may be according to your requirements, but I would have preferred updating a Label or ProgressBar on the ProgressForm. This way the user at least knows what percentage of the work is done and how much more time he should expect to wait before it is fully completed.

    If you examine the approach I showed you, the following things differ which you should consider:
    1. Only one instance of the ProgressForm is created for the lifecycle.
    2. The ProgressForm gives information about how much percentage of the task is done. (I just showed you how to update a Label, but you can use other controls like ProgressBar etc.)
    3. I don't do the "New ProgressForm" with declaration. I do it just before I start the BackgroundWorker task. This way I don't need to keep an instance of that ProgressForm in memory until I actually begin doing the time consuming task, and that form really needs to be shown.
    Pradeep, Microsoft MVP (Visual Basic)
    Please appreciate posts that have helped you by clicking icon on the left of the post.
    "A problem well stated is a problem half solved." Charles F. Kettering

    Read articles on My Blog 101 LINQ Samples JSON Validator XML Schema Validator "How Do I" videos on MSDN VB.NET and C# Comparison Good Coding Practices VBForums Reputation Saver String Enum Super Simple Tetris Game


    (2010-2013)
    NB: I do not answer coding questions via PM. If you want my help, then make a post and PM me it's link. If I can help, trust me I will...

  12. #12
    New Member
    Join Date
    Feb 2013
    Posts
    7

    Re: Correct way to use the BackgroundWorker

    Pradeep1210 thank you.
    Details of making new instance each time reporting progress and GC were really helpful.
    I have used progressbar to show the amount of progress in another scenario and in this one I want to show only the 'Please wait ...' or 'loading ...' which is sufficient enough for now.
    I changed the code as below:

    Code:
    'at class level
         Private frmProgress As ProgressForm
    
    'at start Button method body
        frmProgress .show()
        BGW.RunWorkerAsync() 
    
    In BGW_DoWork :
        worker.ReportProgress(100)  
        'Time consuming job goes here
    
    'In BGW_ProgressChanged : 
        
    
    'In BGW_RunWorkerCompleted : 
        frmProgress.Close()


    But, sorry for this silly question, you specified (Me) on show method of the form. Does it refer to the same form as buuton1 is placed in?
    I took a look at msdn Form show Method (http://msdn.microsoft.com/en-us/library/szcefbbd.aspx) It says 'owner As IWin32Window' as the argument of the method.
    The actual question: How can I be sure to close the 'Please Wait ...' form on BGW completed event. AndAlso what is the best argument for report progress which does not have any idea about stage of the work, just 'Please wait ...".
    Thanks again very much for your time and nice detailed explanation.

  13. #13

    Thread Starter
    VB Addict Pradeep1210's Avatar
    Join Date
    Apr 2004
    Location
    Inside the CPU...
    Posts
    6,612

    Re: Correct way to use the BackgroundWorker

    Yes for the scenerio you mentioned, just showing the form before callingn the RunWorkerAsync method and closing it in the RunWorkerCompleted method is sufficient. You don't even need to report the progress (worker.ReportProgress method), since you will not be making use of it.

    Form.Show(Me)
    Passing "Me" as owner of your progress form will ensure that it will always remain on the top of your form, even if it is not focused. Give it a try and you would be able to see the effect yourself.
    Pradeep, Microsoft MVP (Visual Basic)
    Please appreciate posts that have helped you by clicking icon on the left of the post.
    "A problem well stated is a problem half solved." Charles F. Kettering

    Read articles on My Blog 101 LINQ Samples JSON Validator XML Schema Validator "How Do I" videos on MSDN VB.NET and C# Comparison Good Coding Practices VBForums Reputation Saver String Enum Super Simple Tetris Game


    (2010-2013)
    NB: I do not answer coding questions via PM. If you want my help, then make a post and PM me it's link. If I can help, trust me I will...

  14. #14
    New Member
    Join Date
    Feb 2013
    Posts
    7

    Re: Correct way to use the BackgroundWorker

    Thank you man.
    It was fast and helpful.

  15. #15
    New Member
    Join Date
    Jun 2016
    Posts
    4

    Re: Correct way to use the BackgroundWorker

    Hi !
    I've been trying to wrap my head around all the information reading up on BackgroundWorker to relate to my application. I don't need to "check up" on the progress of the Thread's progress so am only using DoWork, but I'd like to update the main form's GUI. The code below even though executing line by line in debugging, will not actually add to the listbox or update the label. Why won't this work?

    Thanks for the help !!

    vb.net Code:
    1. Public Class Main
    2.     Public Sub log(strMsg As String)
    3.         If Me.InvokeRequired = True Then
    4.             Me.Invoke(New Threading.WaitCallback(AddressOf log), strMsg)
    5.         Else
    6.             Dim origDate As DateTime = DateTime.Now
    7.             lstLog.Items.Add(origDate.ToString("[hh:mm:ss]") & " " & strMsg)
    8.             lblCurrentTask.Text = strMsg
    9.         End If
    10.     End Sub
    11. End Class
    12.  
    13. Class AnotherClass
    14.     Dim bw As BackgroundWorker = New BackgroundWorker
    15.     Sub New()
    16.         bw.WorkerSupportsCancellation = True
    17.         AddHandler bw.DoWork, AddressOf bw_DoWork
    18.     End Sub
    19.     Private Sub bw_DoWork(ByVal sender As Object, ByVal e As DoWorkEventArgs)
    20.         Main.log("Message sent to main GUI")
    21.     End Sub
    22.     Public Sub startScript()
    23.         If Not bw.IsBusy = True Then
    24.             bw.RunWorkerAsync()
    25.         End If
    26.     End Sub
    27.     Public Sub stopScript()
    28.         If bw.WorkerSupportsCancellation = True Then
    29.             bw.CancelAsync()
    30.         End If
    31.     End Sub
    32. End Class
    Last edited by virosoft; Aug 15th, 2016 at 07:05 AM. Reason: code box

  16. #16

    Thread Starter
    VB Addict Pradeep1210's Avatar
    Join Date
    Apr 2004
    Location
    Inside the CPU...
    Posts
    6,612

    Re: Correct way to use the BackgroundWorker

    Just as I mentioned in my original post, you should not call the Form or controls on the form directly from inside the DoWork event. Instead, report progress and update any GUI elements there.

    The following should work:

    vb.net Code:
    1. Class AnotherClass
    2.     Dim bw As BackgroundWorker = New BackgroundWorker
    3.  
    4.     Sub New()
    5.         bw.WorkerSupportsCancellation = True
    6.         bw.WorkerReportsProgress = True
    7.         AddHandler bw.DoWork, AddressOf bw_DoWork
    8.         AddHandler bw.ProgressChanged, AddressOf bw_ProgressChanged
    9.     End Sub
    10.  
    11.     Private Sub bw_DoWork(ByVal sender As Object, ByVal e As DoWorkEventArgs)
    12.         'Main.log("Message sent to main GUI")
    13.         bw.ReportProgress(0)     '-- report some progress so that bw_ProgressChanged event is raised
    14.     End Sub
    15.  
    16.     Private Sub bw_ProgressChanged(sender As Object, e As System.ComponentModel.ProgressChangedEventArgs)
    17.         Main.log("Message sent to main GUI")
    18.     End Sub
    19.  
    20.     Public Sub startScript()
    21.         If Not bw.IsBusy = True Then
    22.             bw.RunWorkerAsync()
    23.         End If
    24.     End Sub
    25.     Public Sub stopScript()
    26.         If bw.WorkerSupportsCancellation = True Then
    27.             bw.CancelAsync()
    28.         End If
    29.     End Sub
    30. End Class
    Pradeep, Microsoft MVP (Visual Basic)
    Please appreciate posts that have helped you by clicking icon on the left of the post.
    "A problem well stated is a problem half solved." Charles F. Kettering

    Read articles on My Blog 101 LINQ Samples JSON Validator XML Schema Validator "How Do I" videos on MSDN VB.NET and C# Comparison Good Coding Practices VBForums Reputation Saver String Enum Super Simple Tetris Game


    (2010-2013)
    NB: I do not answer coding questions via PM. If you want my help, then make a post and PM me it's link. If I can help, trust me I will...

  17. #17
    New Member
    Join Date
    Jun 2016
    Posts
    4

    Re: Correct way to use the BackgroundWorker

    Thanks Pradeep1210 !
    I used a structure to be able to pass multiple arguments, this way I could use conditions to also manipulate other form elements.

    vb.net Code:
    1. Private Sub bw_DoWork(ByVal sender As Object, ByVal e As DoWorkEventArgs)
    2.         ...
    3.         objArgs.eventType = "LogEntry" : objArgs.eventMsg = "Message Information" : bw.ReportProgress(0, objArgs)
    4.         ...
    5.     End Sub
    6.  
    7.     Private Structure struct_EvokeEvent
    8.         Public eventType As String
    9.         Public eventMsg As String
    10.     End Structure
    11.     Private Sub bw_ProgressChanged(sender As Object, e As System.ComponentModel.ProgressChangedEventArgs)
    12.         Dim objArgs As struct_EvokeEvent = e.UserState
    13.         Select Case objArgs.eventType
    14.             Case "LogEntry"
    15.                 Main.log(objArgs.eventMsg)
    16.             Case "SetupConfig"
    17.                 task.taskDesc = Main.gridEvents.Rows(0).Cells(0).Value
    18.                 task.taskParam = Main.gridEvents.Rows(0).Cells(1).Value
    19.                 toSelect(Main.gridEvents, 0)
    20.         End Select
    21.     End Sub

  18. #18
    New Member
    Join Date
    May 2011
    Posts
    4

    Re: Correct way to use the BackgroundWorker

    Quote Originally Posted by Pradeep1210 View Post
    I was away from this forums for some time and couldn't reply back to the answers.
    Not sure if you already have your answer or not, but here's how we will do it:

    1. Create your form that shows the progress. You can design it whatever way you like. Say the name of that form is "ProgressForm".
    vb.net Code:
    1. Public Class ProgressForm
    2. ...
    3. ...
    4. ...
    5. End Class

    2. Create a public property/method in the ProgressForm that sets the text in the label (or whatever control you want to show the progress in):
    vb.net Code:
    1. Public Property ProgressText() As String
    2. Get
    3.     Return Label1.Text
    4. End Get
    5. Set(value As String)
    6.     Label1.Text = value
    7. End Set
    8. End Property

    3. Declare a variable of ProgressForm type at the top of your form (the main form on which your BackgroundWorker is):
    vb.net Code:
    1. Dim MyProgressForm as ProgressForm

    4. Before calling the BackgroundWorker's RunWorkerAsync() method, Show that form:
    vb.net Code:
    1. Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
    2. ...
    3. ...
    4. ...
    5.     '' call this method to start your asynchronous Task.
    6.     MyProgressForm.Show(Me)
    7.     BackgroundWorker1.RunWorkerAsync()
    8. End Sub
    5. Now in the BackgroundWorker's ProgressChanged event, set the property of your ProgressForm that shows appropriate message:
    vb.net Code:
    1. Private Sub BackgroundWorker1_ProgressChanged(sender As Object, e As System.ComponentModel.ProgressChangedEventArgs) Handles BackgroundWorker1.ProgressChanged
    2.     '' This event is fired when you call the ReportProgress method from inside your DoWork.
    3.     '' Any visual indicators about the progress should go here.
    4.  
    5.     MyProgressForm.ProgressText = CType(e.UserState, String)
    6.  
    7. End Sub

    6. After finishing your work, close that form in the RunWorkerCompleted event:
    vb.net Code:
    1. Private Sub BackgroundWorker1_RunWorkerCompleted(sender As Object, e As System.ComponentModel.RunWorkerCompletedEventArgs) Handles BackgroundWorker1.RunWorkerCompleted
    2. ...
    3. ...
    4. ...
    5.     MyProgressForm.Close()
    6. End Sub

    That's all that is required to be done.

    NOTE: All the above code is free-hand typed and may contain typos. It is only meant to explain the concept.
    Hi @Pradeep1210 I tryed your code but I have this exception, please help me!!! Name:  error.jpg
Views: 27542
Size:  34.1 KB

  19. #19
    Super Moderator si_the_geek's Avatar
    Join Date
    Jul 2002
    Location
    Bristol, UK
    Posts
    41,144

    Re: Correct way to use the BackgroundWorker

    You need to create an instance of an object before you can use it... one way is to add this line just before the .Show line:
    Code:
    MyProgressForm = New ProgressForm

  20. #20
    New Member
    Join Date
    May 2011
    Posts
    4

    Re: Correct way to use the BackgroundWorker

    thanks so much!
    Yes, I saw it as soon as I wrote my post

  21. #21
    Hyperactive Member
    Join Date
    Nov 2018
    Posts
    323

    Re: Correct way to use the BackgroundWorker

    Quote Originally Posted by MattP View Post
    You would simple fill the DataTable in the DoWork event.

    Once the DataTable is filled you'd pass it to the UserState parameter of ReportProgress. (The 2nd parameter)

    In the ProgressChanged event cast e.UserState to a DataTable and set it to the DataGridView's DataSource.
    Can someone please help me with this? I have difficulty to "pass" the datatable to the "UserState parameter of ReportProgress". How is that done?
    Also "in the ProgressChanged event cast e.UserState to a DataTable"?

    Thanks

  22. #22
    Super Moderator si_the_geek's Avatar
    Join Date
    Jul 2002
    Location
    Bristol, UK
    Posts
    41,144

    Re: Correct way to use the BackgroundWorker

    You pass the parameter like this:
    Code:
       BackgroundWorker1.ReportProgress(0, theDataTable)
    ...and in the ProgressChanged event, cast it back like this:
    Code:
            Dim theDataTable as DataTable = DirectCast(e.UserState, DataTable)

  23. #23
    Hyperactive Member
    Join Date
    Nov 2018
    Posts
    323

    Re: Correct way to use the BackgroundWorker

    OMG, that easy and still so far for me. Thank you. Can you please tell me why that first parameter should be zero and not 100?

  24. #24
    .NUT jmcilhinney's Avatar
    Join Date
    May 2005
    Location
    Sydney, Australia
    Posts
    102,912

    Re: Correct way to use the BackgroundWorker

    Quote Originally Posted by Grand View Post
    Can you please tell me why that first parameter should be zero and not 100?
    The first parameter should be whatever you want it to be. If you are actually going to use the value to provide feedback to the user then it should accurately represent the current progress. If you're not going to use it for anything in the ProgressChanged event handler then what's the point of using anything other than zero?

    I'm not convinced that you're doing the right thing here though. You are passing a DataTable and you seem to be indicating that the progress is at 100%, i.e. the background work is complete. If you are trying to pass data back to the UI thread when the work is complete then you should not be calling ReportProgress and handling ProgressChanged. Instead, you should be setting the e.Result property and letting the DoWork event handler complete. You then handle the RunWorkerCompleted event and get the data back from the e.Result property there. If this DataTable is the result of the backgroundwork then that is how it should be handled. ReportProgress/ProgressChanged is just to keep the user updated along the way. That might involve just the numerical progress or it might be a batch of data that you have processed so far but if you are only transferring data in a single block at the end, don't do it using ReportProgress/ProgressChanged.

  25. #25
    Hyperactive Member
    Join Date
    Nov 2018
    Posts
    323

    Re: Correct way to use the BackgroundWorker

    Quote Originally Posted by jmcilhinney View Post
    If you are trying to pass data back to the UI thread when the work is complete then you should not be calling ReportProgress and handling ProgressChanged.
    Instead, you should be setting the e.Result property and letting the DoWork event handler complete. You then handle the RunWorkerCompleted event and get the data back from the e.Result property there. If this DataTable is the result of the backgroundwork then that is how it should be handled.
    Spot on. That is exactly what I wanted to do. Would you also please tell me how exactly I set e.result property? And how I bring back the table so I can bind it with the datagridview?
    Thanks.

    I tried this but the table is not returend here:

    Code:
        Private Sub BackgroundWorker1_RunWorkerCompleted(ByVal sender As Object, ByVal e As RunWorkerCompletedEventArgs) Handles BackgroundWorker1.RunWorkerCompleted
    
            Dim MyDataTable As DataTable = DirectCast(e.Result, DataTable)
            DataGridView2.DataSource = MyDataTable
            SpinnerForm.Close() 'Close the form with spinner
    
        End Sub

  26. #26
    Super Moderator si_the_geek's Avatar
    Join Date
    Jul 2002
    Location
    Bristol, UK
    Posts
    41,144

    Re: Correct way to use the BackgroundWorker

    You would set the property like this:
    Code:
    e.result = TheDataTable

  27. #27
    .NUT jmcilhinney's Avatar
    Join Date
    May 2005
    Location
    Sydney, Australia
    Posts
    102,912

    Re: Correct way to use the BackgroundWorker

    Quote Originally Posted by Grand View Post
    Spot on. That is exactly what I wanted to do. Would you also please tell me how exactly I set e.result property? And how I bring back the table so I can bind it with the datagridview?
    Thanks.

    I tried this but the table is not returend here:

    Code:
        Private Sub BackgroundWorker1_RunWorkerCompleted(ByVal sender As Object, ByVal e As RunWorkerCompletedEventArgs) Handles BackgroundWorker1.RunWorkerCompleted
    
            Dim MyDataTable As DataTable = DirectCast(e.Result, DataTable)
            DataGridView2.DataSource = MyDataTable
            SpinnerForm.Close() 'Close the form with spinner
    
        End Sub
    That part is right but you didn't put the DataTable there in the first place to get it back again. I told you exactly what to do:
    Instead, you should be setting the e.Result property and letting the DoWork event handler complete.
    How can you be asking how to set a property? You're already doing it in code you already have. The code you just posted sets the DataSource property of a DataGridView. Why should setting any other property be any different?
    Last edited by jmcilhinney; Feb 12th, 2019 at 06:31 PM.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Featured


Click Here to Expand Forum to Full Width