none
Cross-thread operation not valid RRS feed

  • Question

  • Hi

    I have started to get this message this evening even though i have not added anything that would cause a separate thread? I added a progress bar to an existing form and write a small function to calculate the step value but apart from that, nothing else. I now get this error:

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

    LoadProgress is the progress bar. I have also started to get the same error on a few text boxes.

    Could someone please explain in a simple way what it means exactly? I can see that its related to some sort of Multi-Thread but i havent added anything. I do have an existing Background worker but i didnt change anything in that.

    Thanks


    I am here to ask questions and learn from others. Cor Ligthert DO NOT REPLY TO ANY OF MY POSTS!!

    Friday, August 7, 2020 6:49 AM

All replies

  • Let's say a BackGroundWorker is being used and in the DoWork you try and update a control such as a ProgressBar, TextBox, DataGridView etc you need to do an Invoke which in simple terms prevents a cross thread violation.

    In the following example, LoadData called from DoWork accesses a DataGridView from the BackGroundWorker, a different thread then the main thread, we check to see if we need to use Invoke via InVokeRequired then do the work.

    Public Class Form1
        Delegate Sub SetDataTable(ByVal dt As DataTable)
        Private dataOperations As New DatabaseOperations
        Private Sub Form1_Shown(sender As Object, e As EventArgs) Handles Me.Shown
            BackgroundWorker1.RunWorkerAsync()
        End Sub
        Private Sub BackgroundWorker1_DoWork(sender As Object, e As ComponentModel.DoWorkEventArgs) _
            Handles BackgroundWorker1.DoWork
    
            LoadDataTable(dataOperations.LoadPeople())
    
        End Sub
        Private Sub LoadDataTable(dt As DataTable)
            If DataGridView1.InvokeRequired Then 'Invoke if required...
                Dim d As New SetDataTable(AddressOf LoadDataTable)
                Invoke(d, New Object() {dt})
            Else
                DataGridView1.DataSource = dt
                DataGridView1.ClearSelection()
                Console.WriteLine(Now)
            End If
        End Sub
    End Class
    


    Please remember to mark the replies as answers if they help and unmarked them if they provide no help, this will help others who are looking for solutions to the same or similar problem. Contact via my Twitter (Karen Payne) or Facebook (Karen Payne) via my MSDN profile but will not answer coding question on either.

    NuGet BaseConnectionLibrary for database connections.

    StackOverFlow
    profile for Karen Payne on Stack Exchange

    Friday, August 7, 2020 9:47 AM
    Moderator
  • Hi NachoShaw,

    How is the question going? If your question has been answered then please click the "Mark as Answer" Link at the bottom of the correct post(s), so that it will help other members to find the solution quickly if they face a similar issue.

    Best Regards,

    Xingyu Zhao


    MSDN Community Support
    Please remember to click "Mark as Answer" the responses that resolved your issue, and to click "Unmark as Answer" if not. This can be beneficial to other community members reading this thread. If you have any compliments or complaints to MSDN Support, feel free to contact MSDNFSF@microsoft.com.

    Wednesday, August 12, 2020 5:45 AM
    Moderator
  • Hi Karen

    Thanks for the reply and instruction. I understand what you example does now but im not sure it will help in my instance. I have a large process called Go that is called when i need to process the data. The GO function calls a series of other functions to populate different data tables. During the process, there is an amount of control updating on the UI form, mainly a text control that receives text as a visual progress update. There also some other visual indicators to show a step through process however, as some of the processes are reliant on another application, there periods of hanging while that external process completes.

    I tried adding my Go class to a background worker but because it constantly updates the UI form, i get the Cross Thread error.

    I was hoping that there would be a more simple approach to set a ProgressMarquee as that doesnt require any progress data, it just needs to indefinitely move until the processes are finished.

    I guess i could try and write the invokeRequired method into each control update from the Go Class but id rather remove the ProgressMarquee

    Thanks for your help

    Nacho


    I am here to ask questions and learn from others. Cor Ligthert DO NOT REPLY TO ANY OF MY POSTS!!

    Thursday, August 13, 2020 4:58 AM
  • If your DoWork function updates the progress bar directly, then replace this with BackgroundWorker1.ReportProgress. Then handle the ProgressChanged event, where you can update the progress bar directly. (Also set WorkerReportsProgress in Form Designer).

    You can also build and pass additional data to ReportProgress, to be used for labels, textboxes, etc. Documentation contain samples; see how two arguments of ReportProgress are used: https://docs.microsoft.com/en-us/dotnet/api/system.windows.forms.toolstripprogressbar.

    Thursday, August 13, 2020 7:43 AM
  • Hi

    Thanks for the reply. I think this is where my confusion is. The ProgressBarMarquee doesnt require any progress data so in order to keep it visually active, im unsure of what i could pass to it from a BackgroundWorker1.ReportProgress


    I am here to ask questions and learn from others. Cor Ligthert DO NOT REPLY TO ANY OF MY POSTS!!

    Thursday, August 13, 2020 1:40 PM
  • Hi

    Trying to get my head around how i can apply update data to a forms control while processing on a separate thread. I have 2 trains of thought:

    1. should i move everything from my Go Class into the BackgroundWorker1.DoWork so that i could invoke the form controls to update

    2. should i make a new class that updates all of the form controls for the update and create a new instance of that in the Go class that is called from the BackGroundWorker1.DoWork or ReportProgress?

    My CurrentGo Class is structured like this (truncated for this example)

    'Members Class is instantiated on start up for global members
    'LA Class is instantiated on startup, contains all user controls as pages
    
    'all controls that are updated are on the usercontrols
    
    Public Class Go
    Private ReadOnly DT As New Data 'GetData
    Private ReadOnly FR As New Folders ' CreateFolders
    
    Friend Sub Go()
    Dim AP As New AssyProcess ' ready for assembly processing
    Dim IC As New InventorCore ' get all inventor processes
    
    'show progress bar on user control used as a page
    AddGlobal.LA.UProg.StepProgressNext.Visible = True
    AddinGlobal.LA.UsrProgress.StepProgressNext.SelectedItemIndex = 0
    
    IC.SetActiveProject()
    F.CreateFolders()
    F.CopyFilesOver()
    
    'this updates the progress list
    AP.UpdateProcessList("Setup Complete", True, True)
    AddinGlobal.Members.stopWatch.Start()
    
    AddinGlobal.FrmLoader.LockWindow(True)
    AddinGlobal.InventorApp.ScreenUpdating = False
    AddinGlobal.InventorApp.SilentOperation = True
    AddinGlobal.LA.UsrProgress.StepProgressDone.SelectedItemIndex = 0
    AddinGlobal.LA.UsrProgress.StepProgressNext.SelectedItemIndex = 1
    IC.CreateStepFileFromXML(AddinGlobal.Members.loadedXMLdocAsPath)
    
    'this updates the progress list
    AP.UpdateProcessList("Solid model generated", True, True)
    
    IC.ImportSTEP(AddinGlobal.Members.StepFileLocation, AddinGlobal.Members.AssyName, AddinGlobal.Members.CurrWorkspace)
    AP.UpdateProcessList("Model imported", True, True)
    
    AddinGlobal.Members.ActiveAssemblyDocument = AddinGlobal.InventorApp.ActiveDocument
    AddinGlobal.InventorApp.ActiveDocument.Rebuild2()
    AddinGlobal.InventorApp.CommandManager.ControlDefinitions.Item("AppIsometricViewCmd").Execute()
    AddinGlobal.InventorApp.CommandManager.ControlDefinitions.Item("AppZoomallCmd").Execute()
    Dim GetCnt As Integer = AddinGlobal.Members.ActiveAssemblyDocument.ComponentDefinition.Occurrences.Count
       
    '**** UPDATE ****
    AddinGlobal.LA.UsrProgress.StepProgressDone.SelectedItemIndex = 1
    AddinGlobal.LA.UsrProgress.StepProgressNext.SelectedItemIndex = 2
    AP.RenameBrowserNodes(DirectCast(AddinGlobal.InventorApp.ActiveDocument, Inventor.AssemblyDocument))
    AP.UpdateProcessList("Components renamed", True, True) 
    
    AddinGlobal.Members.stopWatch.Stop()
    Dim ts As TimeSpan = AddinGlobal.Members.stopWatch.Elapsed
    Dim elapsedTime As String = String.Format("{0:00}:{1:00}:{2:00}", ts.Minutes, ts.Seconds, ts.Milliseconds / 10)
    AddinGlobal.LA.UsrDone.LblShowElapsed.Text = "Import process time = " & elapsedTime
    
    End Sub
    End Class
               

    As an additional question, i instantiate the LA class at runtime as they are required for for pre processing. There is 1 userform used for the whole progress process called uProg. This has all of the controls that get updated. Would it work if a created a new instance of this in the BackgroundWorker.DoWork and added it to the form panel or would that also give me a cross thread operation not valid?

    Thanks


    I am here to ask questions and learn from others. Cor Ligthert DO NOT REPLY TO ANY OF MY POSTS!!

    Thursday, August 13, 2020 5:09 PM
  • Hi

    Think im getting to understand the BackgroundWorker a little more (or at least I hope i am.)

    I update 3 controls during the main process that are currently called directly through a form button click. This button click calls the Go method and its this method that runs everything.

    When i first put the Go method call into a BackgroundWorker, i was getting a cross thread operation error which is because the backgroundworker and the form controls are on different threads?

    So, if i moved the control updates to the backgroundworker.ReportProgress method and where i originally had the control update calls, i called the backgroundworker.ReportProgress(send update value here) then use the invoke method to send that update value to the control, would that get around my problem or am i still not understanding this lol?

    Thanks


    I am here to ask questions and learn from others. Cor Ligthert DO NOT REPLY TO ANY OF MY POSTS!!

    Saturday, August 15, 2020 8:56 PM
  • Hi NachoShaw,

    Thanks for your feedback.

    >>Think im getting to understand the BackgroundWorker a little more

    Hope following references can help you have a better understanding of BackgroundWorker. 

    1. Correct way to use the BackgroundWorker
    2. How to: Make thread-safe calls to Windows Forms controls

    Hope them could be helpful.

    Best Regards,

    Xingyu Zhao


    MSDN Community Support
    Please remember to click "Mark as Answer" the responses that resolved your issue, and to click "Unmark as Answer" if not. This can be beneficial to other community members reading this thread. If you have any compliments or complaints to MSDN Support, feel free to contact MSDNFSF@microsoft.com.

    Monday, August 17, 2020 1:42 AM
    Moderator