Getting cross thread error when executing tcViewer.TabPages.Add(t) statement.
Code is as below.
Private Function fff(t As TabPage)
tcViewer.TabPages.Add(t) 'giving cross thread error
End Function
Function WebBrowserThread()
Dim t As TabPage = New TabPage((k + 1).ToString())
t.Name = k.ToString()
tcViewer.Invoke(fff(t))
End Function
Please guide.
I think you should move the creation of the new TabPage onto the UI thread as well:
Private Function fff(k as Integer)
Dim t As TabPage = New TabPage((k + 1).ToString())
t.Name = k.ToString()
tcViewer.TabPages.Add(t)
End Function
Function WebBrowserThread()
tcViewer.Invoke(fff(k))
End Function
When you construct the TabPage, you eventually reach this call stack:
System.Windows.Forms.dll!System.Windows.Forms.Control.CreateHandle()
System.Windows.Forms.dll!System.Windows.Forms.Application.MarshalingControl.MarshalingControl()
System.Windows.Forms.dll!System.Windows.Forms.Application.ThreadContext.MarshalingControl.get()
System.Windows.Forms.dll!System.Windows.Forms.WindowsFormsSynchronizationContext.WindowsFormsSynchronizationContext()
System.Windows.Forms.dll!System.Windows.Forms.WindowsFormsSynchronizationContext.InstallIfNeeded()
System.Windows.Forms.dll!System.Windows.Forms.Control.Control(bool autoInstallSyncContext)
System.Windows.Forms.dll!System.Windows.Forms.ScrollableControl.ScrollableControl()
System.Windows.Forms.dll!System.Windows.Forms.Panel.Panel()
System.Windows.Forms.dll!System.Windows.Forms.TabPage.TabPage()
System.Windows.Forms.dll!System.Windows.Forms.TabPage.TabPage(string text)
At this point, the Handle is being created, and if you're doing that on the wrong thread, everything else is going to start going wrong (because the thread that the control was created on isn't going to run a message pump)
i don't know what invoking error you get but i suggest disabling cross-thread checking by adding this in the constructor or loaded event (very helpful when dealing with APIs)
Windows.Forms.Control.CheckForIllegalCrossThreadCalls = False
Check this http://tech.xster.net/tips/invoke-ui-changes-across-threads-on-vb-net/
in wpf such problems are easy to fix because you have a single thread for all the controls (Dispatcher.Invoke)
Update
dealing with the UI controls have to be on the UI thread
Me.Invoke(sub()
Dim t As TabPage = New TabPage((k + 1).ToString())
t.Name = k.ToString()
fff(t)
End Sub)
Me.Invoke(sub()
tcViewer.TabPages.Add(t)
End Sub)
Related
Public Function PiesTableTest(compairFile As String, version1 As String, Optional silent As Boolean = False) As Boolean
Dim dpgs As New frmDetailProgress
Dim retturn As Boolean
PiesThreadedTableTest(compairFile, version1, silent, dpgs)
End Function
Async Function PiesThreadedTableTest(compairFile As String, version1 As String, silent As Boolean, dpgs As frmDetailProgress) As Task(Of Boolean)
Dim ctl() As xmlControlAry
Dim xmlDoc As XElement
Dim xmlNodes As IEnumerable(Of XElement)
Dim notfound(0) As String
version = version1
nodeErrors = False
If Not silent Then
dpgs.lblTital.Text = "Pies Configuration Check"
dpgs.add("Pies Version = " & version)
dpgs.add("Loading Config Data....")
dpgs.Show()
End If
' load configuration data
GetPiesControl(ctl, version)
' load test xml file
xmlDoc = XElement.Load(compairFile)
xmlNodes = xmlDoc.Elements()
For Each ele As XElement In xmlNodes
NodeDrill("", ele, ctl, dpgs, notfound, silent)
Next
If nodeErrors And Not silent Then
dpgs.add("Testing done with Errors!!!", "R")
Else
dpgs.add("Testing Done NO ERRORS!", "G")
End If
Application.DoEvents()
If silent Then
dpgs.Dispose()
End If
'PiesThreadedTableTest = Not nodeErrors
If nodeErrors Then
Return False
Else
Return True
End If
End Function
I am trying to understand multi threading. frmDetailProgress is a "please wait " kind of form. and i have a animated gif on it. Plus it has a check box to close automatically after completion. Well the form is frozen till the process is done. I am trying to get the piesthreadedtabletest to run in another thread. I have read allot on this but i just don't understand the concept. I don't understand the await function enough to make this work. i get that await is designed to stop processing until something happens. But i want that form freed up to work. I get an error saying that the function will run synchronously unless i have an await - Why?
I got it working. It was a lack of understanding and i probably still need to learn more. I hope this will help someone in the future.
i created a class to call functions in the other class running in the second thread.
imports system.threading
public sub callThreadedProcedure()
dim tp as system.threading.thread ' this will be for the object running in the other thread
dim objectToRun as myclass ' this is the object you want to run in the thread
'this gets the object and puts it into the new thread
tp = new thread(sub() objectToRun.FunctionToRun(<put your parameters here if any>))
' start execution of the object in a new thread.
tp.start()
' that will get it to run in a separate thread. It works, there might be a better way
' and might not work in all situations, but for now it fixed my problem.
end sub
if you are trying to run functions in the original thread you need to pass a
reference to that object to the one in the second thread. you must then use invoke to run a function or sub from the second thread.
Invoke(sub() obj.function(<parameters>))
thanks Idle_mind invoked worked like it should.
I appreciate all that helped me along.
I've been working through my first project and have had a great deal a valuable help from the guys on SO but now I'm stuck again.
The below sub is used to add TreeNodes to a TreeView, excluding certain filetypes/names, upon addition of new data:
Sub DirSearch(ByVal strDir As String, ByVal strPattern As String, ByVal tvParent As TreeNodeCollection)
Dim f As String
Dim e As String
Dim tvNode As TreeNode
Dim ext() As String = strPattern.Split("|"c)
Try
For Each d In Directory.GetDirectories(strDir)
If (UCase(IO.Path.GetFileName(d)) <> "BACKUP") And (UCase(IO.Path.GetFileName(d)) <> "BARS") Then
tvNode = tvParent.Add(IO.Path.GetFileName(d))
For Each e In ext
For Each f In Directory.GetFiles(d, e)
If (UCase(IO.Path.GetFileName(f)) <> "DATA.XLS") And (UCase(IO.Path.GetFileName(f)) <> "SPIRIT.XLSX") Then
tvNode.Nodes.Add(IO.Path.GetFileName(f))
End If
Next
Next
DirSearch(d, strPattern, tvNode.Nodes)
End If
Next
Catch ex As Exception
MsgBox(ex.Message)
End Try
End Sub
I'm now getting an error:
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.
On the following line:
tvNode = tvParent.Add(IO.Path.GetFileName(d))
Obviously, I understand its to do with 'threading' and the use of BeginInvoke / Invoke but even after reading the MSDN documentation on the error, I have no idea where to start.
This error only occurs, if I add a file to the initial directory (which is also the subject of a File System Watcher to monitor new additions).
Would someone be so kind as to give me an explanation in layman's terms so I may be able to understand.
This code is being run on a background thread where it's illegal to modify UI elements. The Invoke / BeginInvoke methods are ways to schedule a piece of code to run on UI thread where elements can be modified. For example you could change your code to the following
Dim action As Action = Sub() tvNode.Nodes.Add(IO.Path.GetFileName(f))
tvNode.TreeView.Invoke(action)
This code will take the delegate instance named action and run it on the UI thread where edits to tvNode are allowed
Fixing the earlier Add call is a bit trickier because there is no Control instance on which we can call BeginInvoke. The signature of the method will need to be updated to take a Dim control as Control as a parameter. You can pass in the TreeView for that parameter if you like. Once that is present the first Add can be changed as such
Dim outerAction As Action = Sub() tvNode = tvParent.Add(IO.Path.GetFileName(d))
control.Invoke(outerAction)
I'm extremely novice at threading and I'm simply creating a single thread to run a large function. I've created a messagebox to appear at the end of the function towards the end of the program to tell me the load time it took. As i load the application, the messagebox will appear with a time it took and THEN the thread will kick off(although the UI is navigable while the components are loading from the thread) isn't the point of threading to be able to process multiple functions at the same time? Why is this waiting until the main thread is finished before the new thread kicks off?
I declare and start the new thread early in the app
For every Form in the application's namespace, there will be a default instance created in the My namespace under the Forms property.
----------------------/ Starting Main Thread /-----------------------------------
Private Sub FindCustomerLocation()
Dim Findcontractor_Thread As New Thread(AddressOf **FindContractor_ThreadExecute**)
Findcontractor_Thread.Priority = ThreadPriority.AboveNormal
Findcontractor_Thread.Start(**me**)
End Sub
------------------/ Running Thread /---------------
Private Sub **FindContractor_ThreadExecute**(beginform as *NameOfFormComingFrom*)
Dim threadControls(1) As Object
threadControls(0) = Me.XamDataGrid1
threadControls(1) = Me.WebBrowserMap
**FindContractor_WorkingThread**(threadControls,beginform) ' ANY UI Calls back to the Main UI Thread MUST be delegated and Invoked
End Sub
------------------/ How to Set UI Calls from a Thread / ---------------------
Delegate Sub **FindContractor_WorkingThread**(s As Integer,beginform as *NameOfFormComingFrom*)
Sub **FindContractor_WorkingThreadInvoke**(ByVal s As Integer,beginform as *NameOfFormComingFrom*)
If beginform.mouse.InvokeRequired Then
Dim d As New FindContractor_WorkingThread(AddressOf FindContractor_WorkingThreadInvoke)
beginform.Invoke(d, New Object() {s,beginform})
Else
beginform.Mouse.OverrideCursor = Cursors.Wait
'Do something...
beginform.Mouse.OverrideCursor = Nothing
End If
End Sub
Sources from Pakks answer and Tested!
You have to create multiple threads if you want them to run the way you are thinking (simultaneously).Take a look at this link and try creating more than one thread. This should help your problems. Cheers
http://msdn.microsoft.com/en-us/library/ck8bc5c6%28v=vs.80%29.aspx
I have a problem with the busyindicator control of silverlight.
I have a datagrid (datagrid1) with its source set to a wcf service (client).
I would like to use the busyindicator control (bi) of silvelright toolkit when the datagrid loads itself.
But I have an "Invalid cross thread access" when I use "ThreadPool".
Sub LoadGrid()
Dim caisse As Integer = ddl_caisse.SelectedValue
Dim env As Integer = ddl_env.SelectedValue
bi.IsBusy = True
ThreadPool.QueueUserWorkItem(Sub(state)
AddHandler client.Get_PosteSPTCompleted, AddressOf client_Get_PosteSPTCompleted
client.Get_PosteSPTAsync(caisse, env)
Dispatcher.BeginInvoke(Sub()
bi.IsBusy = False
End Sub)
End Sub)
End Sub
Private Sub client_Get_PosteSPTCompleted(sender As Object, e As ServiceReference1.Get_PosteSPTCompletedEventArgs)
DataGrid1.ItemsSource = e.Result ' Here, Invalid cross thread access
End Sub
I know that the datagrid control doesn't exist in the "new thread", but how have to I make to avoid this error?
Thank you.
William
First point: Why are you using the ThreadPool?
Using a ThreadPool would be a good idea if your service was synchronous, but your WCF service is asynchronous: it won't block your UI thread after being called using Get_PosteSPTAsync.
Second point: there seems to be an issue with your IsBusy property. You're first setting it to true, then you starts an operation asynchronously, then you set it to false immediately. You should set it to false in the Completed handler.
Third point: the client_Get_PosteSPTCompleted handler won't be executed in the same thread as the UI thread (even if you don't use the ThreadPool). That's why you'll have to use the dispatcher here so force using the UI thread.
Your DataGrid1.ItemsSource = e.Result must be modified to:
Dispatcher.BeginInvoke(Sub()
DataGrid1.ItemsSource = e.Result ' Fixes the UI thread issue
bi.IsBusy = False ' Sets busy as false AFTER completion (see point 2)
End Sub)
(note sure about the VB.Net syntax, but that's the idea)
Last point: I don't know about the client object lifetime, but you're adding a new handler to the Get_PosteSPTCompleted each time you call LoadGrid. Maybe you could think of detaching handlers after use.
I have a weird exception that I can't seem to debug. For anyone about to suggest worker threads I am prohibited from using them at my work... I asked why not myself and received a vague answer and clear directions to avoid it... anyways...
I decided I wanted to have a progress bar on a separate form that is initialized and shown directly from my library class (independent of the main form and executing on a different thread). Even though this class initializes the form itself, I am using control.InvokeRequired and control.Invoke anyway so I can reuse the form elsewhere.
The following is for initializing the progress bar:
Public Sub InitializePB(ByVal _Maximum As Integer, ByVal _Step As Integer, ByVal _StartValue As Integer, ByVal _Style As Windows.Forms.ProgressBarStyle)
If Me.InvokeRequired Then
'Different thread - invoke delegate
pb_Progress.Invoke(Sub() InitializePB(_Maximum, _Step, _StartValue, _Style))
Else
'Same thread - set values
pb_Progress.Minimum = 1
pb_Progress.Maximum = _Maximum
pb_Progress.Step = _Step
pb_Progress.Value = _StartValue
pb_Progress.Style = _Style
Me.Refresh()
End If
End Sub
Now for the step function:
Public Sub PerformStepPB()
If Me.InvokeRequired Then
'Different thread - invoke delegate
pb_Progress.Invoke(Sub() PerformStepPB())
Else
'Same thread - perform step
pb_Progress.PerformStep()
Me.Refresh()
End If
End Sub
Now the following is test code from where I launch form containing the progress bar and then send step calls:
pbForm = New frmProgress(_OwnerDesktopLoc)
pbForm.Show()
pbForm.InitializePB(100, 1, 1, Windows.Forms.ProgressBarStyle.Blocks)
Dim msElapsedVals As Integer = 0
While msElapsedVals <= 100
pbForm.PerformStepPB()
Thread.Sleep(100)
msElapsedVals += 1
End While
pbForm.Close()
pbForm.Dispose()
While Not pbForm.IsDisposed
Thread.Sleep(100)
End While
Exit Sub
So far so good... you will notice that I call the Form.Refresh method for the progress bar form every time the step method is called. I ended up doing this because when the form launched it would crash after several progress bar steps. At this point in time the form does show without crashing (because of the refresh) except that if I click the form it crashes (and no exception is caught by Visual Studio). The weird thing is that the code from the library class for updating the progress bar continues without any problems as if the problem is simply with the Win32 window. Here is the Windows message associated with the crash:
Description:
A problem caused this program to stop interacting with Windows.
Problem signature:
Problem Event Name: AppHangB1
Application Name: DL_RDS_Sort.vshost.exe
I tried launching the form from the main form thread but with the same results. I have no clue what's going on... any help would be greatly appreciated =D
Update on exactly how things are being called:
From MainForm a thread is started with the address of a subroutine defined within a library class :
Private Sub but_Sort_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles but_Sort.Click
Me.Sorter.OwnerDesktopLoc = New Drawing.Point(Me.DesktopLocation.X + CInt(Me.DesktopBounds.Width / 6), Me.DesktopLocation.Y + CInt(Me.DesktopBounds.Height / 2))
'Start sorting thread
Me.sortThread = New Threading.Thread(AddressOf Me.Sorter.Sort)
Me.sortThread.Start()
Then in this thread's routine I am testing the initialization of the form and calling the methods (shown earlier) I wrote for updating the performance bar properties:
Public Sub Sort()
Try
IIf(_OwnerDesktopLoc = Nothing, useProgressBar = False, useProgressBar = True)
'TESTING//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
pbForm = New frmProgress(_OwnerDesktopLoc)
pbForm.Show()
pbForm.InitializePB(100, 1, 1, Windows.Forms.ProgressBarStyle.Blocks)
Dim msElapsedVals As Integer = 0
While msElapsedVals <= 100
pbForm.PerformStepPB()
Thread.Sleep(100)
msElapsedVals += 1
End While
pbForm.Close()
pbForm.Dispose()
While Not pbForm.IsDisposed
Thread.Sleep(100)
End While
Exit Sub
'TESTING//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Note that there is a lot more code following the testing area but it never gets called and does not play a part in this issue because I exit the subroutine. I normally use ShowDialog but this halts the execution which is not what I want.
Some things worth mentioning:
-the frmProgress has the ControlBox property set to false.
-the frmProgress has the FormBorderStyle set to FixedDialog
Also: When I use the Show() method, the Windows "busy circle" cursor appears when I hover my mouse over the shown form. ShowDialog() does not have this behaviour... It's as if the form is waiting for something... Hope this clarifies my problem.