Want to protect your cyber security and still get fast solutions? Ask a secure question today.Go Premium

x
?
Solved

Background worker not updating UI

Posted on 2011-05-09
10
Medium Priority
?
681 Views
Last Modified: 2012-05-11
Hi,

I was wondering if someone can help. I have attached the following code to help show you the problem.
I am trying to update the UI from the background worker without much luck:

Snippet ID=8162067 on Form1 where the UI control is
Snippet ID=8162090 in clsPart that runs as part of the DoWorkEventArgs

I run Form1.BackgroundWorker1.ReportProgress(0, x) that passes a structure back to Form1.
In debug mode the values look ok:

Console.WriteLine(CStr(CType(e.UserState, UpdatePart02.clsPart.Status).intCurrent))

But the following does not update the UI:
Me.txtCurrent.Text = CStr(CType(e.UserState, UpdatePart02.clsPart.Status).intCurrent)

Regards,

Ross

Imports System.ComponentModel

Public Class clsPart
    Private conEpr As New clsConnection
    Private objPart As Epicor.Mfg.BO.Part
    Private ds As Epicor.Mfg.BO.PartDataSet
    ' Public Event ProgressChanged As ProgressChangedEventHandler

    Public Structure Status
        Public intCurrent As Integer
        Public intTotal As Integer
    End Structure



    Public Sub LoadParts(ByVal dt As DataTable)
        Dim booFound As Boolean = False
        Dim intErrors As Integer = 0

        For i As Integer = 0 To dt.Rows.Count - 1
            ds = New Epicor.Mfg.BO.PartDataSet
            'Try
            objPart = New Epicor.Mfg.BO.Part(conEpr.getConnection)
            booFound = objPart.PartExists(dt.Rows(i).Item("PartNum"))

            ' If booFound = False Then
            'objPart.GetNewPart(ds)
            Try
                Dim x As New Status
                ds = objPart.GetByID(dt.Rows(i).Item("PartNum"))
                Console.WriteLine(dt.Rows(i).Item("PartNum") & " " & i & " OF: " & dt.Rows.Count - 1)

                x.intCurrent = i
                x.intTotal = dt.Rows.Count - 1
                Form1.BackgroundWorker1.ReportProgress(0, x)
                ds.Tables("Part").Rows(0).Item("InActive") = True
                ds.Tables("PartPlant").Rows(0).Item("ProcessMRP") = False
                objPart.Update(ds)
                x = Nothing
            Catch ex As Exception
                Console.WriteLine(ex.Message)
            End Try

            ds = Nothing
            objPart = Nothing
        Next i


    End Sub
    Public Function AmINull(ByVal nv As Object) As String
        If IsDBNull(nv) = True Then
            Return ""
        Else
            Return nv
        End If
    End Function
    Public Function ConvertYesNO(ByVal strVar As String) As Boolean
        Select Case strVar
            Case "YES"
                Return True
            Case "NO"
                Return False
        End Select

    End Function

End Class

Open in new window

Private Sub BackgroundWorker1_DoWork(ByVal sender As System.Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
        objPart.LoadParts(dt)
        objExcel = Nothing
        objPart = Nothing
    End Sub


    Private Sub BackgroundWorker1_ProgressChanged(ByVal sender As Object, ByVal e As System.ComponentModel.ProgressChangedEventArgs) Handles BackgroundWorker1.ProgressChanged
        Console.WriteLine(CStr(CType(e.UserState, UpdatePart02.clsPart.Status).intCurrent))
        Console.WriteLine(CStr(CType(e.UserState, UpdatePart02.clsPart.Status).intTotal))
        
Me.txtCurrent.Text = CStr(CType(e.UserState, UpdatePart02.clsPart.Status).intCurrent)
        Me.txtTotal.Text = CStr(CType(e.UserState, UpdatePart02.clsPart.Status).intTotal) 
Me.Refresh
    End Sub

Open in new window

0
Comment
Question by:ross13
10 Comments
 
LVL 70

Expert Comment

by:Éric Moreau
ID: 35720872
have you set the WorkerReportsProgress property to true?
0
 
LVL 86

Expert Comment

by:Mike Tomlinson
ID: 35721204
Is Form1 the startup object?...or is it being loaded from a different from with the "new" keyword?
0
 
LVL 83

Expert Comment

by:CodeCruiser
ID: 35721537
Add an

Application.DoEvents

at the end of progress changed event handler.
0
Independent Software Vendors: We Want Your Opinion

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

 
LVL 86

Expert Comment

by:Mike Tomlinson
ID: 35721804
If DoEvents() fixes it then the background thread is raising too many progress events.  Consider raising progress every XXX iterations (like 50?...you'll have to experient), or raise them based on a time interval (like once a second).
0
 

Author Comment

by:ross13
ID: 35728074
Hi,

I have you set the WorkerReportsProgress property to true.
Form1 is the startup object.

I have also tried adding Application.DoEvents to the end of BackgroundWorker1_ProgressChanged.

Still no joy.

Cheers,

Ross
0
 
LVL 86

Expert Comment

by:Mike Tomlinson
ID: 35728812
I would make a custom event for your "clsPart" class:

     Public Class clsPart

         Public Event LoadingProgress(ByVal progress As clsPart.Status)

     End Class

Then, instead of directly invoking ReportProgress(), raise the event from your class:

     Me.LoadingProgress(x)

When you instantiate "clsPart", use AddHandler() to wire up the event in the main form (or declare it as "WithEvents" and use Handles).

When the event is received in the main form, then call ReportProgress() from there.
0
 

Author Comment

by:ross13
ID: 35729373
Hi,

Thanks for that. I am not sure that i have done this correctly I gave it a go by adding the following:

Public Class clsPart
     Public Event LoadingProgress(ByVal progress As clsPart.Status)


     Public Sub LoadParts(ByVal dt As DataTable)
        RaiseEvent LoadingProgress(x)
     End Sub
End Class

Within my main form:

Public Class Form1
WithEvents objPart As New clsPart

    Private Sub ReportProgress(ByVal sender As Object) Handles objPart.LoadingProgress
        Me.txtCurrent.Text = CStr(CType(sender, UpdatePart02.clsPart.Status).intCurrent)
        Me.txtTotal.Text = CStr(CType(sender, UpdatePart02.clsPart.Status).intTotal)

    End Sub

End Class

The ReportProgress does fire but i receive the following error message when trying to set the values in report progress:

Cross-thread operation not valid: Control 'txtCurrent' accessed from a thread other than the thread it was created on.

Cheers,

Ross
0
 
LVL 86

Accepted Solution

by:
Mike Tomlinson earned 2000 total points
ID: 35729439
Change:

    Private Sub ReportProgress(ByVal sender As Object) Handles objPart.LoadingProgress
        Me.txtCurrent.Text = CStr(CType(sender, UpdatePart02.clsPart.Status).intCurrent)
        Me.txtTotal.Text = CStr(CType(sender, UpdatePart02.clsPart.Status).intTotal)
    End Sub

To:

    Private Sub ReportProgress(ByVal sender As Object) Handles objPart.LoadingProgress
        Me.BackgroundWorker1.ReportProgress(0, sender)
    End Sub

Now your ProgressChanged() event will fire:

    Private Sub BackgroundWorker1_ProgressChanged(ByVal sender As Object, ByVal e As System.ComponentModel.ProgressChangedEventArgs) Handles BackgroundWorker1.ProgressChanged
        Dim
        Me.txtCurrent.Text = CStr(CType(sender, UpdatePart02.clsPart.Status).intCurrent)
        Me.txtTotal.Text = CStr(CType(sender, UpdatePart02.clsPart.Status).intTotal)
    End Sub
0
 
LVL 7

Expert Comment

by:vbigham
ID: 35730675
It sounds like you need to marshal back to the UI thread, since winForms are not thread safe and neither their controls, you will need to run the update code on the same thread that created the controls.

A common method for doing that in this scenario would probably look something like this:
 
Private Sub ReportProgress(ByVal sender As Object) Handles objPart.LoadingProgress
    If Not Me.InvokeRequired Then
        UpdateStatus(CType(sender, UpdatePart02.clsPart.Status))

    Else
        Me.Invoke(AddressOf UpdateStatus, CType(sender, UpdatePart02.clsPart.Status))
    End If
End Sub


Private Sub UpdateStatus(ByVal prtStatus As UpdatePart02.clsPart.Status)
    If prtStatus Is Nothing Then Return

    Me.txtCurrent.Text = CStr(prtStatus.intCurrent)
    Me.txtTotal.Text = CStr(prtStatus.intTotal)
End Sub

Open in new window

0
 

Author Closing Comment

by:ross13
ID: 35734009
Thanks for that. Now working ok.

Cheers,

Ross
0

Featured Post

Keep up with what's happening at Experts Exchange!

Sign up to receive Decoded, a new monthly digest with product updates, feature release info, continuing education opportunities, and more.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

If you're writing a .NET application to connect to an Access .mdb database and use pre-existing queries that require parameters, you've come to the right place! Let's say the pre-existing query(qryCust) in Access takes a Date as a parameter and l…
Calculating holidays and working days is a function that is often needed yet it is not one found within the Framework. This article presents one approach to building a working-day calculator for use in .NET.
Exchange organizations may use the Journaling Agent of the Transport Service to archive messages going through Exchange. However, if the Transport Service is integrated with some email content management application (such as an anti-spam), the admin…
As many of you are aware about Scanpst.exe utility which is owned by Microsoft itself to repair inaccessible or damaged PST files, but the question is do you really think Scanpst.exe is capable to repair all sorts of PST related corruption issues?
Suggested Courses

581 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