Calling Sub from EventHandler - vb.net

I'm using the below to update controls from another thread (works great) How would I call a Sub (Named UpdateList)? The UpdateList updates a listview with a list of databases on a selected SQL instance, requires no arguments.
Private Sub CompleteEventHandler(ByVal sender As Object, ByVal e As Microsoft.SqlServer.Management.Common.ServerMessageEventArgs)
SetControlPropertyValue(Label8, "text", e.ToString)
UpdateList()
MessageBox.Show("Restore Complete")
End Sub
Delegate Sub SetControlValueCallback(ByVal oControl As Control, ByVal propName As String, ByVal propValue As Object)
Private Sub SetControlPropertyValue(ByVal oControl As Control, ByVal propName As String, ByVal propValue As Object)
If (oControl.InvokeRequired) Then
Dim d As New SetControlValueCallback(AddressOf SetControlPropertyValue)
oControl.Invoke(d, New Object() {oControl, propName, propValue})
Else
Dim t As Type = oControl.[GetType]()
Dim props As PropertyInfo() = t.GetProperties()
For Each p As PropertyInfo In props
If p.Name.ToUpper() = propName.ToUpper() Then
p.SetValue(oControl, propValue, Nothing)
End If
Next
End If
End Sub
Based On:
http://www.shabdar.org/cross-thread-operation-not-valid.html

Calling a method from an event handler is not a special case – use a normal call.
The issue here is the cross-thread call from a background thread to the GUI thread. In order to overcome it, you can place the following code at the beginning of the UpdateList code:
If Me.InvokeRequired Then
Me.Invoke(New Action(AddressOf UpdateList))
Return
End If

Related

TWAIN events in BackgroundWorker

I have a project in which I use a backgroundworker to do the scanning function from a scanner asynchronously by WIA2. It works well with WIA.
Now I am trying to do the same by scanning with TWAIN. I can scan using TWAIN ok. However when I try to make it work in the background I cannot make it work properly since the event of scanning in TWAIN have a TransferImage handler and a ScanningComplete event handler which get aroused when the scan finishes. The transferImage is ok since it does not affect my background event. However I want to access a panel in the scanningComplete event, make it .Visible = False
A piece of what happens:
Private Sub rBEScan_Click(sender As Object, e As EventArgs) Handles rBEScan.Click
rPScanning.Visible = True
Me.rBEScan.Enabled = False
bGWScan.RunWorkerAsync()
End Sub
Private Sub bGWScan_DoWork(sender As Object, e As System.ComponentModel.DoWorkEventArgs) Handles bGWScan.DoWork
Dim path As String = ""
Dim correct As Boolean = False
If scanMode = 1 Then
correct = ScannerRead(path, 1)
Else 'TWAIN
images = Nothing
images = New List(Of System.Drawing.Bitmap)
correct = scanTWAIN(gLocalScanner, path)
End If
Dim obj As Object
obj = correct
e.Result = obj
End Sub
Private Sub bGWScan_RunWorkerCompleted(sender As Object, e As System.ComponentModel.RunWorkerCompletedEventArgs) _
Handles bGWScan.RunWorkerCompleted
Dim obj As Object
obj = e.Result
Dim lobj As Boolean
lobj = DirectCast(obj, Boolean)
rPScanning.Visible = False
Me.rBEScannerEskaneatu.Enabled = True
End Sub
Private Shared Function scanTWAIN(ByVal id As String, ByVal path As String) As Boolean ' prompt to scan more pages,
' SCAN TWAIN FUNCTION
AddHandler twain.TransferImage,
Sub(sender As Object, args As TwainDotNet.TransferImageEventArgs)
If (Not (args.Image Is Nothing)) Then
images.Add(args.Image)
End If
End Sub
' Re-enable the form after scanning completes
AddHandler twain.ScanningComplete,
Sub(sender As Object, e As TwainDotNet.ScanningCompleteEventArgs)
'Enabled = True
Dim lobj As String = ""
lobj = FuncionScanner.pdfIrudiraTwain(images, path, gLocalPreguntarRotacion, orritxurisep, orriDok, orriguztiakDok)
PrincipalR.rPScanning.Visible = False
PrincipalR.rBEScan.Enabled = True
End Sub
ScanningFunctionOfTwain With my settings.
End Sub
My problem is that I end the background worker before scanning the images, since the events are handled and aroused in another function that is called asynchronously.
Any idea of how can I put the rPScanning.Visible = False
and rBEScan.Visible = False
When the event within the background worker ends.
If it is not possible should I use another backgroundworker in the eventHandler of the scan pages in TWAIN.
Thank #JQSOFT,
As you said I had to use a delegate sub to do handle the panel visibility. Here is the change in the code:
Private Shared Function scanTWAIN(ByVal id As String, ByVal path As String) As Boolean ' prompt to scan more pages,
' SCAN TWAIN FUNCTION
AddHandler twain.TransferImage,
Sub(sender As Object, args As TwainDotNet.TransferImageEventArgs)
If (Not (args.Image Is Nothing)) Then
images.Add(args.Image)
End If
End Sub
' Re-enable the form after scanning completes
AddHandler twain.ScanningComplete,
Sub(sender As Object, e As TwainDotNet.ScanningCompleteEventArgs)
Dim objNewThread As New Thread(Sub() FuncionScanner.pdfTwainThread(images, path, gLocalPreguntarRotacion,
orritxurisep, orriDok, orriguztiakDok,
rPScanning, rBEScan))
objNewThread.IsBackground = True
objNewThread.Start()
End Sub
ScanningFunctionOfTwain With my settings.
End Sub
I have put it in a new thread the after scan function and it works well.
Since I use the new thread in another class I put the new delegate sub as well.
Public Shared Sub pdfTwainThread(ByVal lista As List(Of System.Drawing.Bitmap),
ByVal path As String, ByVal ori As Boolean,
ByVal orritxurisep As Boolean, ByVal orriDok As Boolean,
ByVal orriguztiakDok As Boolean,
ByRef rp As Telerik.WinControls.UI.RadPanel,
ByRef rb As Telerik.WinControls.UI.RadButtonElement)
Dim l As String = ""
l = pdfIrudiraTwain(lista, path, ori, orritxurisep, orriDok, orriguztiakDok)
panel_visible(False, rp, rb)
Dim txt As String = ""
Dim txt1 As String = ""
txt1 = Func_nombre(366)
If l <> "" Then
txt = Func_nombre(436)
MessageBox.Show(txt, txt1, MessageBoxButtons.OK, MessageBoxIcon.Information)
Else
txt = Func_nombre(437)
MessageBox.Show(txt, txt1, MessageBoxButtons.OK, MessageBoxIcon.Error)
End If
End Sub
Delegate Sub PanelV(vf As Boolean, ByRef rp As Telerik.WinControls.UI.RadPanel,
ByRef rbscan As Telerik.WinControls.UI.RadButtonElement)
Private Shared Sub panel_visible_hemen(ByVal vis As Boolean, ByRef rp As Telerik.WinControls.UI.RadPanel,
ByRef rbscan As Telerik.WinControls.UI.RadButtonElement)
If rp.InvokeRequired Then
Dim d As New PanelV(AddressOf panel_visible_hemen)
rp.Invoke(d, New Object() {vis, rp, rbscan})
Else
rp.SendToBack()
rp.Visible = vis
rbscan.Enabled = True
End If
End Sub
I pass the panel and the buttonElement byref to the new function which I update in a delegate sub.
Thanks a lot for the help #JQSOFT.
I have post the answer just in case someone else needs.

Threading: How to update label or close form

I haven't really done much with threads before and I'm having a problem updating a label and closing a form.
When debugging, the CloseDialog() sub is definitely running in the main thread so I don't understand why it's not closing. There are no loops or anything else running on the form to keep it open. I'm also having a problem updating the text on a label with information passed from another thread in real time.
The AddOUToTreeView sub gets invoked and works fine, but the subs from frmStatus never do anything.
frmMain:
Private WithEvents bkg As New ADSearcher
Private Sub startSearch_Click(ByVal sender As Object, ByVal e As EventArgs) Handles startSearch.Click
With bkg
.RootPath = "LDAP://domain.com"
.FilterString = "(objectCategory=organizationalUnit)"
If Not integratedAuth Then
.UserID = "user"
.Password = "pass"
End If
.PageSize = 5
.PropertiesToLoad = New String() {"cn", "name", "distinguishedName", "objectCategory"}
Dim search As New Threading.Thread(AddressOf .StartSearch)
search.Start()
Dim statusDialog As frmStatus = New frmStatus
statusDialog.Show() 'I want to use ShowDialog() but removed it when trouble shooting
End With
End Sub
Private Delegate Sub displayStatus(ByVal entriesFound As Integer)
Private Sub bkg_ResultFound(ByVal ousFound As Integer) Handles bkg.ResultFound
Dim display As New displayStatus(AddressOf frmStatus.UpdateOUSearcherStatus)
Me.Invoke(display, New Object() {ousFound})
End Sub
Private Delegate Sub displayResult(ByVal node As TreeNode)
Private Delegate Sub closeStatusDialog()
Private Sub bkg_SearchCompleted(ByVal ouNodes As TreeNode) Handles bkg.SearchCompleted
Dim display As New displayResult(AddressOf AddOUToTreeView)
Me.Invoke(display, New Object() {ouNodes})
Dim closeStatus As New closeStatusDialog(AddressOf frmStatus.CloseDialog)
Me.Invoke(closeStatus)
End Sub
Private Sub AddOUToTreeView(ByVal node As TreeNode)
tvOU.Nodes.Add(node)
tvOU.TopNode.Expand()
End Sub
frmStatus (Both of these functions do nothing):
Public Sub CloseDialog()
'Me.DialogResult = Windows.Forms.DialogResult.OK
Me.Close()
'Me.Dispose()
End Sub
Public Sub UpdateOUSearcherStatus(ByVal entriesFound As Integer)
'lblOUsFound.Text = Format("{0} Organizational Units Found", ousFound)
lblOUsFound.Text = entriesFound.ToString
End Sub
In my ADSearcher class I have:
Public Event ResultFound(ByVal ousFound As Integer)
Public Event SearchCompleted(ByVal ouNodes As TreeNode)
and raise the events with:
RaiseEvent ResultFound(resultCount)
'and
RaiseEvent SearchCompleted(rootNode)

dynamic timer in vb.net

I've got a vb app in .Net 4 that i'm working on. The app fires up dynamic threads to run some jobs. Each thread runs a certain job and within that thread, it performs a infinate loop. It needs to be this way, i can't have the thread and then begin again...
anyway.. i was using threading.thread.sleep in the loop to have the loop pause for a bit before running again. It seems that this is not a good way to go and not recommended. So i thought perhaps i could use a dynamic timer or something else to have the thread wait for say 30 seconds before running again. i was hoping someone could give me some direction and example code.
thanks
shannon
~~~~~~~~~~edit
hey.. i figured out the problem and no.. it wasn't the sleep in the background thread.... it was a sleep i didn't realize i put in in the UI thread.. I started commented out everything and started adding in 1 by 1 and found it. Sorry PEBKAM error...
Thanks for the input
Use the following class to do the delay, i developed with same concept of javascript:settimeout :
Public Class JSsetTimeout
Public res As Object = Nothing
Dim WithEvents tm As Timer = Nothing
Dim _MethodName As String
Dim _args() As Object
Dim _ClassInstacne As Object = Nothing
Public Shared Sub SetTimeout(ByVal ClassInstacne As Object, ByVal obj As String, ByVal TimeSpan As Integer, ByVal ParamArray args() As Object)
Dim jssto As New JSsetTimeout(ClassInstacne, obj, TimeSpan, args)
End Sub
Public Sub New(ByVal ClassInstacne As Object, ByVal obj As String, ByVal TimeSpan As Integer, ByVal ParamArray args() As Object)
If obj IsNot Nothing Then
_MethodName = obj
_args = args
_ClassInstacne = ClassInstacne
tm = New Timer With {.Interval = TimeSpan, .Enabled = False}
AddHandler tm.Tick, AddressOf tm_Tick
tm.Start()
End If
End Sub
Private Sub tm_Tick(ByVal sender As Object, ByVal e As System.EventArgs) Handles tm.Tick
tm.Stop()
RemoveHandler tm.Tick, AddressOf tm_Tick
If Not String.IsNullOrEmpty(_MethodName) AndAlso _ClassInstacne IsNot Nothing Then
res = CallByName(_ClassInstacne, _MethodName, CallType.Method, _args)
Else
res = Nothing
End If
End Sub
End Class
Usage
JSsetTimeout.SetTimeout(ClassContainingMethod, "MethodName", 30000, OptionalParameters)

VB.Net Multithreaded SerialPort

,Im new with serialport programming. Is there any way to pass SerialPort(ex. .name, COM, etc..) properties in UI Thread in multithreaded application?
What I want is similar in the code below but set my properties as variables. For example(property=.name, property = .text). So that i can return it to the UI thread by calling object.property
Public Delegate Sub SetTextCallback(ByVal control As Control, ByVal text As String)
Public Sub SetText(ByVal control As Control, ByVal text As String)
If control.InvokeRequired Then
Dim d As New SetTextCallback(AddressOf SetText)
frmMain.Invoke(d, New Object() {control, text})
Else
control.Text = text
End If
End Sub
I want is something like this (there's equivalent snippet in c#.Net but I can't do it in VB.NET):
Public Delegate Sub SetTextCallback(ByVal control As Control, ByVal prop as property, ByVal text As String)
Public Sub SetText(ByVal control As Control, byVal prop as property, ByVal text As String)
If control.InvokeRequired Then
Dim d As New SetTextCallback(AddressOf SetText)
frmMain.Invoke(d, New Object() {control, prop, text})
Else
control.prop = text
End If
End Sub
Thanks in advance...

Raising an event on a new thread in VB.NET

I need to raise an event from a form on a new thread.
(I don't believe the reason for this is relevant, but just in case: I'll be raising events from code within a form's WndProc sub. If the code handling the event blocks with something on a form [such as a msgbox], then all sorts of trouble occurs with disconnected contexts and what not. I've confirmed that raising events on new threads fixing the problem.)
This is what I am currently doing:
Public Event MyEvent()
Public Sub RaiseMyEvent()
RaiseEvent MyEvent
End Sub
Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message)
Dim t As New Threading.Thread(AddressOf RaiseMyEvent)
t.Start()
End Sub
Is there a better way?
It is my understanding that events in VB are actually made up of delegates in the background. Is there any way to raise events in new threads without creating subs for each? Or, is there a more appropriate method I should be using?
You can eliminate the RaiseMyEvent sub like this:
Public Class Class1
Public Event MyEvent()
Sub Demo()
Dim t As New Threading.Thread(Sub() RaiseEvent MyEvent())
t.Start()
End Sub
End Class
Don't know if this will help, but i'll always do threading and events like this:
Event MyEvent(ByVal Var1 As String, ByVal Var2 As String)
Private Delegate Sub del_MyEvent(ByVal Var1 As String, ByVal Var2 As String)
Private Sub StartNewThread()
'MAIN UI THREAD
Dim sVar1 As String = "Test"
Dim sVar2 As String = "Second Var"
Dim oThread As New Threading.Thread(New Threading.ParameterizedThreadStart(AddressOf StartNewThread_Threaded))
With oThread
.IsBackground = True
.Priority = Threading.ThreadPriority.BelowNormal
.Name = "StartNewThread_Threaded"
.Start(New Object() {sVar1, sVar2})
End With
End Sub
Private Sub StartNewThread_Threaded(ByVal o As Object)
'CHILD THREAD
Dim sVar1 As String = o(0)
Dim sVar2 As String = o(1)
'Do threaded operation
Threading.Thread.Sleep(1000)
'Raise event
RaiseEvent_MyEvent(sVar1, sVar2)
End Sub
Public Sub RaiseEvent_MyEvent(ByVal Var1 As String, ByVal Var2 As String)
If Me.InvokeRequired Then
'Makes the sub threadsafe (I.e. the event will only be raised in the UI Thread)
Dim oDel As New del_MyEvent(AddressOf RaiseEvent_MyEvent)
Me.Invoke(oDel, Var1, Var2)
Exit Sub
End If
'MAIN UI THREAD
RaiseEvent MyEvent(Var1, Var2)
End Sub