So, I'm having troubles implementing a separate thread. This is because I have a simple class, and in it I start a new thread. So, as it is not any form, I haven't found any way to make it call the function in the UI Thread.
So, I cannot use the Invoke method. Is there any way to call a function from another thread?
I am going to assume that you have events exposed from your class and that you want the event handlers to execute on a UI thread. I suppose you could have a callback that the caller specifies as well. Either way the pattern I will describe below will work in both cases
One way to make this happen is to have your class accept an ISynchronizeInvoke instance. Form and Control instances implement this interface so a reference to one of them could be used. You could make it a convention that if the an instance is not specified then event handlers executed by raising events on your class would execute in the worker thread instead of the thread hosting the ISynchronizeInvoke instance (usually a form or control).
Public Class YourClass
Private m_SynchronizingObject As ISynchronizeInvoke = Nothing
Public Sub New(ByVal synchronizingObject As ISynchronizeInvoke)
m_SynchronizingObject = synchronizingObject
End Sub
Public Property SynchronizingObject As ISynchronizeInvoke
Get
Return m_SynchronizingObject
End Get
Set(ByVal value As ISynchronizeInvoke)
m_SynchronizingObject = value
End Set
End Property
Private Sub SomeMethodExecutingOnWorkerThread()
RaiseSomeEvent()
End
Private Sub RaiseSomeEvent()
If Not SychronizingObject Is Nothing AndAlso SynchronizingObject.InvokeRequired Then
SynchronizingObject.Invoke(New MethodInvoker(AddressOf RaiseSomeEvent)
End If
RaiseEvent SomeEvent
End Sub
End Class
The first thing to notice is that you do not have to specify a synchronizing object. That means you do not have to have a Form or Control reference. If one is not specified then SomeEvent will be raised on the worker thread. This is the same pattern that is used in the System.Timers.Timer class.
Try to expose some events in your class, fire them when you need to notify your UI and finally make your UI Component register to these events,
when the event is fired, the listener methods will be executed. there you can use Control.Invoke or Control.BeginInvoke to execute your code on the UI thread.
Related
Basically I am loading a JSON object that contains combinations of values available or not at run time, so I need to know when a specific property is modified to then toggle all the other browsable etc.. and though that the PropertyChange event was the perfect way to handle it.
So I can add an event handler to get triggered on my expandoobject like this:
Dim test As Object = new ExpandoObject
AddHandler CType(test, INotifyPropertyChanged).PropertyChanged, AddressOf expando_PropertyChanged
and the handler is as basic as it gets
Public Shared Sub expando_PropertyChanged(ByVal sender As Object, ByVal e As PropertyChangedEventArgs)
Debug.Print("Property {0} set or changed", e.PropertyName)
End Sub
so far this works, if I add or modify a property right after that, I get notified.
however if I return this and set it as the selectedobject of my propertygrid, I cannot get the event to trigger.
I'm using a custom PropertyDescriptor and ICustomTypeDescriptor to set a few other attributes for the propertygrid, so I assumed it might be as easy as setting the attribute
<RefreshProperties(RefreshProperties.All)>
but I cannot find a way to override the Refresh in the PropertyDescriptor unlike Browsable or readonly, which kinda makes sense as the property grid would need to know ahead of time that it needs to be refreshable.
So I could not make the INotifyPropertyChanged work with the expando, it would work with a dynamicObject where I would implement it myself but that was requiring too much of a rewrite for me.
I ended up add a lambda on my expando that I call on the PropertyDescriptor SetValue
CType(_expando, Object).toggleSwitches.Invoke(_expando, _name, value)
note the use of Invoke here in vb.net that was also a PITA but I found this guy who had the same issue as I did: https://github.com/dotnet/vblang/issues/226
It is not necessary to use invoke in C# and as 99% of the examples are in C# it took me more time than I wanted to implement it.
Hopefully this will help someone too.
here is the lambda if interested as well:
_expando.toggleSwitches = Sub(obj As Object, caller As String, value As Object)
Debug.Print(caller & " " & value.ToString())
End Sub
Done my fair share of looking this up but it just doesn't make sense..
I know we have to use delegates to update a textbox thats on the Main UI.
Here is the code in the a nutshell:
Initiate the thread that will capture chats:
ChatQuery = New Thread(AddressOf FetchChats)
ChatQuery.Start()
FetchChats Code Simplified:
SetTextBoxWithInvoke(Form5.TextBox2, MESSAGE)
SetTextBoxWithInvokeCode:
Private Sub SetTextBoxWithInvoke(ByVal TB As TextBox, ByVal msg As String)
If TB.InvokeRequired Then
TB.Invoke(New AddToMessageBoxDelegate(AddressOf SetTextBoxWithInvoke), New Object() {TB, msg})
Else
TB.Text &= msg
End If
End Sub
The Problem?? Invoke is never required, and the new message is never appended to the textbox I need to be appended to.
Delegate:
Public Delegate Sub AddToMessageBoxDelegate(ByVal TB As TextBox, ByVal msg As String)
The problem is that you're using the default instance of the form here:
SetTextBoxWithInvoke(Form5.TextBox2, MESSAGE)
Default instances are thread-specific so, if you execute that code on a background thread, rather than using the existing Form5 instance that was created and displayed on the UI thread, it will create a new instance on the current thread. There's no need to invoke a delegate to access that instance so InvokeRequired is always False.
You need to use the actual instance of Form5 that already exists. How exactly you do that depends on the circumstances. If the code that makes the call is already in that form then just use Me, which is implicit if you don't explicitly use another reference anyway. Otherwise, it's up to you to get the required reference into the required object to be used.
If the code is not in a form already then maybe you should not be doing it that way at all. Instead, you can use the SynchronizationContext class. You get the current instance when your object is created on the UI thread and you can then call its Send or Post method to marshal back to the UI thread without an explicit Control reference. That might not work in your case though, because you'd still need a reference to the correct TextBox.
I am creating a project in VBA to automate handling of asynchronous quasi-multithreading (mouthful, sorry). This revolves around creating and running multiple copies of a class which Implements a certain interface, and raises some known events when the async task is complete. The interfacing is similar to this example
My program calls the class to execute its code, and listens to the events raised, that's all working fine. Now my final task is to take any given class which Implements the appropriate interface, and make multiple copies to set running in parallel.
How do I make copies of a class which is passed to a routine?
How can I take a class reference and make several New versions?
Or in code, each one of my thread classes (the classes which handle the async class which is passed) will have a Worker property to save their task.
Private workerObject As IWorker
Public Property Set Worker(workObj As IWorker) 'pass unknown class with IWorker interface
'What goes here?
Set workerObject = workObj
'This won't work as then every thread points to the same worker
'I want something to create a New one, like
Set workerObject = New ClassOf(workObj)
'But of course that doesn't work
End Property
You would need to inspect the possible types and act accordingly:
Dim workerObject As IWorker
If TypeOf workObj Is ImplementingClass1 Then
Set workerObject = New ImplementingClass1
ElseIf TypeOf workObj Is ImplementingClass2 Then
Set workerObject = New ImplementingClass2
End If
Alternatively you could add a factory method to the interface:
Public Function CreateNew() As IWorker: End Function
Implement it in the classes:
Public Function IWorker_CreateNew() As IWorker
Set IWorker_CreateNew = New ImplementingClass1
End Function
And then:
Set workerObject = workObj.IWorker_CreateNew()
I have an Async method that does not end immediately after I close its user control. So, when I close and reopen it very fast, my user control gets errors. How could I exit that Async function from another class. Is that possible?
Public Class Main
Private Sub mainfucn()
'exit otherfunc
End Sub
End Class
Public Class Other
Public Async Function otherfunc() As Task
' some code
End Sub
End Class
Well, you could slum it by implementing your own cancellation system yourself. For instance, probably the simplest way to do something like that would be with some sort of cancellation flag property, like this:
Public Class Main
Private _other As New Other()
Private Sub MainFunc()
other.Cancelled = True
End Sub
End Class
Public Class Other
Public Property Cancelled As Boolean ' Yes, I know I'm not British, but the American spelling of "Canceled" is phonetically stupid. But, then again, so is the spelling of "British", so...
Public Async Function OtherFunc() As Task
Cancelled = False
While Not Cancelled
' Some code
End While
End Sub
End Class
However, that would be pretty terrible, so I wouldn't recommend it. The two primary reasons why it's terrible is because, One, it assumes that there's only ever one Async method and that there's only ever one invocation of it running at a time. And Two, it is inconsistent with the standard async cancellation patterns of .NET.
I would strongly recommend that, instead of attempting to do it with your own (anti-) pattern, you should add a CancellationToken parameter to your Async function. However, the way that you would implement that within the method all depends on what it is doing asynchronously and how it does it. So it's impossible to give you a single good example for how to accomplish that. The best thing I could say is, you were on the right track with using a CancellationToken, so you should keep going down that path. If, after doing more research, you aren't able to get it working, then I would recommend posting a more specific question regarding how to implement a CancellationToken within the context of what your method is doing and how it operates.
I have the following lines of code as a test user control. When the project is built, and I drag this user control onto a form, I get an error dialogue to the effect that EF can't find the connection string for the context. Yet when I use the same variable in a form, all is well. It seems the user control is using a different context within which to look for the connection string than the usual app.config.
Public Class InvoiceWorkOrderSearch
Private _dataHelper As WorkOrderData = New WorkOrderData()
End Class
During Design time?
You can avoid this be only instancing the object if the control is in runtime mode.
The build in property to check for desing time (Me.DesignMode) is poor since it only tells you if you are currently designing the control itself. It returs false if you drop the usercontrol on a form.
You can use this code to check for designtime: http://dotnet-snippets.de/dns/designmode-workaround-windows-forms-SID299.aspx
Public Class InvoiceWorkOrderSearch
Private _dataHelper As WorkOrderData
Public Sub New()
If IsDesignMode(me) = False Then
_dataHelper = New WorkOrderData()
End If
End Sub()
End Class