Home:ALL Converter>Update label from mainform class with backgroundworker from another class

Update label from mainform class with backgroundworker from another class

Ask Time:2013-11-12T20:34:46         Author:UrsulRosu

Json Formatter

I have two classes.

Public Class MainForm

     Private Project As clsProject


Private Sub btnDo_Click
   ...
   Backgroundworker.RunWorkerAsync()

End Sub
 Private Sub BackgroundWorker1_DoWork(ByVal sender As System.Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork


    Project = New clsProject


End Sub

and two methods inside MainForm

 Public Shared Sub setLabelTxt(ByVal text As String, ByVal lbl As Label)
    If lbl.InvokeRequired Then
        lbl.Invoke(New setLabelTxtInvoker(AddressOf setLabelTxt), text, lbl)
    Else
        lbl.Text = text
    End If
End Sub
Public Delegate Sub setLabelTxtInvoker(ByVal text As String, ByVal lbl As Label)
end class

I want to update the labels of MainForm from the clsProject constructor.

 MainForm.setLabelTxt("Getting prsadasdasdasdasdry..", MainForm.lblProgress)

but it does not update them. What am I doing wrong?

Author:UrsulRosu,eproduced under the CC 4.0 BY-SA copyright license with a link to the original source and this disclaimer.
Link to original article:https://stackoverflow.com/questions/19929331/update-label-from-mainform-class-with-backgroundworker-from-another-class
Steven Doggart :

The problem is that you are using the global MainForm instance to access the label in a background thread here:\n\nPublic Class clsProject\n Public Sub New()\n ' When accessing MainForm.Label1 on the next line, it causes an exception\n MainForm.setLabelTxt(\"HERE!\", MainForm.Label1)\n End Sub\nEnd Class\n\n\nIt's OK to call MainForm.setLabelTxt, since that is a shared method, so it's not going through the global instance to call it. But, when you access the Label1 property, that's utilizing VB.NET's trickery to access the global instance of the form. Using the form through that auto-global-instance variable (which always shares the same name as the type) is apparently not allowed in non-UI threads. When you do so, it throws an InvalidOperationException, with the following error message:\n\n\n An error occurred creating the form. See Exception.InnerException for details. The error is: ActiveX control '8856f961-340a-11d0-a96b-00c04fd705a2' cannot be instantiated because the current thread is not in a single-threaded apartment.\n\n\nI'm guessing that the reason you are not seeing the error is because you are catching the exception somewhere and you are simply ignoring it. If you stop using that global instance variable, the error goes away and it works. For instance, if you change the constructor to this:\n\nPublic Class clsProject\n Public Sub New(f As MainForm)\n ' The next line works because it doesn't use the global MainForm instance variable\n MainForm.setLabelTxt(\"HERE!\", f.Label1)\n End Sub\nEnd Class\n\n\nThen, in your MainForm, you would have to call it like this:\n\nPrivate Sub BackgroundWorker1_DoWork(ByVal sender As System.Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork\n Project = New clsProject(Me) ' Must pass Me\nEnd Sub\n\n\nUsing the global instance from the background thread is not allowed, but when we use the same label from the background thread, without going through that global variable it works. \n\nSo it's clear that you cannot use the global MainForm variable from a background thread, but what may not be clear is that it's a bad idea to use it ever. First, it's confusing because it shares the same name as the MainForm type. More importantly, though, it is a global variable, and global state of any kind is almost always bad practice, if it can be avoided.\n\nWhile the above example does solve the problem, it's still a pretty poor way of doing it. A better option would be to pass the setLabelTxt method to the clsProject object or even better have the clsProject simply raise an event when the label needs to be changed. Then, the MainForm can simply listen for those events and handle them when they happen. Ultimately, that clsProject class is probably some sort of business class which shouldn't be doing any kind of UI work anyway.",
2013-11-12T13:29:03
varocarbas :

You cannot execute any action on GUI-elements from the BackgroundWorker directly. One way to \"overcome\" that is by forcing the given actions to be performed from the main thread via Me.Invoke; but this is not the ideal proceeding. Additionally, your code mixes up main form and external class (+ shared/non-shared objects) what makes the whole structure not too solid.\n\nA for-sure working solution is relying on the specific BGW methods for dealing with GUI elements; for example: ProgressChanged Event. Sample code:\n\nPublic Class MainForm\n Private Project As clsProject\n Public Shared bgw As System.ComponentModel.BackgroundWorker\n\n Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load\n bgw = BackgroundWorker1 'Required as far as you want to called it from a Shared method\n\n BackgroundWorker1.WorkerReportsProgress = True\n BackgroundWorker1.RunWorkerAsync()\n End Sub\n\n Private Sub BackgroundWorker1_DoWork(sender As System.Object, e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork\n Project = New clsProject\n End Sub\n\n Public Shared Sub setLabelTxt(ByVal text As String)\n bgw.ReportProgress(0, text) 'You can write any int as first argument as far as will not be used anyway\n End Sub\n\n Private Sub BackgroundWorker1_ProgressChanged(sender As Object, e As System.ComponentModel.ProgressChangedEventArgs) Handles BackgroundWorker1.ProgressChanged\n Me.Label1.Text = e.UserState 'You can access the given GUI-element directly\n Me.Label1.Update()\n End Sub\nEnd Class\n\nPublic Class clsProject\n Public Sub New()\n MainForm.setLabelTxt(\"Getting prsadasdasdasdasdry..\")\n End Sub\nEnd Class\n",
2013-11-12T13:11:45
yy