Delegates and Classes

  • Thread starter Thread starter Dave
  • Start date Start date
D

Dave

Good morning,

I have a class that must trigger an event in a module, which will in
turn update a Label on a form. Due to being in a seperate thread,I
must use a delegate.

Now here is my question. I have managed to create a delegate on a
Form using the Invoke method. In this case though, the class is
actually created as a global variable and it's instance is created in
a module.

What is the best way to implement this delegate since I can't perform
an Invoke from a module?

Thanks for the help,

Dave
 
I have attached a copy of the code at module end:

Module modSystemMonitor
Public Delegate Sub MyDelegate(ByVal intIndex As Integer)

Public frmOver As frmOverview
Public colSystem As New Collection
Public MyDel As MyDelegate

Public Sub LoadSystemId()
Dim n As Integer
Dim Dave As clsSystem
MyDel = AddressOf MyDelegateSub
n = 1
Dave = New clsSystem("Local Test", "Local Test", "WSDEV",
"MONITOR")
AddHandler Dave.DataAvailableEvent, AddressOf
colSystem_NewDataAvailable
Dave.Index = n
colSystem.Add(Dave, Dave.SystemID)
n = 2
Dave = New clsSystem("Backup Domain Controller", "Backup Domain
Controller", "MSS06", "MONITOR")
AddHandler Dave.DataAvailableEvent, AddressOf
colSystem_NewDataAvailable
Dave.Index = n
colSystem.Add(Dave, Dave.SystemID)
End Sub

'*********************************
Public Sub MyDelegateSub(ByVal intIndex As Integer)
frmOver.Label2.Text = "Testing123|" & intIndex

'*** <<This is where I get the Error Cross-Thread Operation Not Valid:
Control 'Label2' accessed form a thread other than the thread it was
created in >> *****

End Sub
'*********************************

Private Sub colSystem_NewDataAvailable(ByVal intIndex As Integer)
Dim Dave As New Data.DataTable
Try
MyDel.Invoke(intIndex)
MsgBox(intIndex)
Catch
MsgBox(Err.Description)
End Try
End Sub
End Module
 
Dave,

Dave said:
Module modSystemMonitor
Public Delegate Sub MyDelegate(ByVal intIndex As Integer)

Public frmOver As frmOverview
Public colSystem As New Collection
Public MyDel As MyDelegate

Public Sub LoadSystemId()
Dim n As Integer
Dim Dave As clsSystem
MyDel = AddressOf MyDelegateSub
n = 1
Dave = New clsSystem("Local Test", "Local Test", "WSDEV",
"MONITOR")
AddHandler Dave.DataAvailableEvent, AddressOf
colSystem_NewDataAvailable
Dave.Index = n
colSystem.Add(Dave, Dave.SystemID)
n = 2
Dave = New clsSystem("Backup Domain Controller", "Backup Domain
Controller", "MSS06", "MONITOR")
AddHandler Dave.DataAvailableEvent, AddressOf
colSystem_NewDataAvailable
Dave.Index = n
colSystem.Add(Dave, Dave.SystemID)
End Sub

'*********************************
Public Sub MyDelegateSub(ByVal intIndex As Integer)
frmOver.Label2.Text = "Testing123|" & intIndex

'*** <<This is where I get the Error Cross-Thread Operation Not Valid:
Control 'Label2' accessed form a thread other than the thread it was
created in >> *****

\\\
Private m_Text As String

Public Sub MyDelegateSub(ByVal intIndex As Integer)
m_Text = "Testing123|" & intIndex
If frmOver.Label2.InvokeRequired Then
frmOver.Label2.Invoke(CType(AddressOf SetText, MethodInvoker))
Else
SetText()
End If
End Sub

Private Sub SetText()
frmOver.Label2.Text = m_Text
End Sub
///
 
Hi Herfriend,

Thanks for the help, but still no success. The application freezes on
the Label2.Invoke method.

Here is the complete source. Every 3 seconds a ping is sent out to a
host, the ping response (good or bad) forces an Invoke. The idea is
to eventually pass a table with the latest pings.

I have looked through all of the web examples and this delege should
work, but doesn't. I've tried about 8 different ways with no success.

Thanks again.

////
Module modSystemMonitor
Public Dave As clsSystem
Public frmOver As frmOverview
Public Sub LoadSystemId()
Dim n As Integer
n = 1
Dave = New clsSystem("Local Test", "Local Test", "WSDEV",
"MONITOR")
Dave.Index = n
If frmOver Is Nothing Then
frmOver = New frmOverview
frmOver.Height = frmMain.SplitContainer2.Panel2.Height
frmOver.Width = frmMain.SplitContainer2.Panel2.Width
frmOver.TopLevel = False
frmOver.Parent = frmMain.SplitContainer2.Panel2
frmOver.Show()
frmOver.Anchor = AnchorStyles.Left Or AnchorStyles.Right
End If
frmOver.Label1.Text = "Overview"
frmOver.BringToFront()
End Sub
End Module

Public Class frmOverview
Private m_Text As String
Private Sub frmOverview_Load(ByVal sender As System.Object, ByVal e
As System.EventArgs) Handles MyBase.Load
Dave.NotifyOnDataChange(New
clsSystem.DelegateDataChanged(AddressOf MyDelegateSub))
End Sub
Private Sub MyDelegateSub(ByVal intIndex As Integer)
m_Text = "Received Something:" & Now & ":" & intIndex
If frmOver.Label2.InvokeRequired Then
MsgBox("Must Invoke:" & intIndex)
frmOver.Label2.Invoke(CType(AddressOf SetText, MethodInvoker))
Else
SetText()
End If
End Sub
Private Sub SetText()
Label2.Text = m_Text
End Sub

Imports nsoftware.IPWorks
Public Class clsSystem
Public Delegate Sub DelegateDataChanged(ByVal intIndex As Integer)
Private DataChanged As DelegateDataChanged
Private WithEvents timerPing As New Timer
Private WithEvents pingSystem As New nsoftware.IPWorks.Ping
Private mintIndex As Integer
Public Structure custSystemList
Public strSystemID As String
Public strDescription As String
Public strHost As String
Public strSystemType As String
End Structure
Private SystemList As custSystemList
Private PingHistory As New Data.DataTable
Public Sub New(ByVal strSystemID As String, ByVal strDescription As
String, ByVal strHost As String, ByVal strSystemType As String)
Dim DateTime As New Data.DataColumn("DateTime")
Dim PingResult As New Data.DataColumn("PingResult")
DateTime.DataType = System.Type.GetType("System.DateTime")
PingResult.DataType = System.Type.GetType("System.String")
PingHistory.Columns.Add(DateTime)
PingHistory.Columns.Add(PingResult)
SystemList.strSystemID = strSystemID
SystemList.strDescription = strDescription
SystemList.strHost = strHost
SystemList.strSystemType = strSystemType
timerPing.Interval = 3000
timerPing.Enabled = True
pingSystem.Timeout = 1
End Sub
Public Sub NotifyOnDataChange(ByVal value As DelegateDataChanged)
DataChanged = value
End Sub
Public Sub AddPingHistory(ByVal strDateTime As DateTime, ByVal
strPingResult As String)
PingHistory.Rows.Add()
PingHistory.Rows(PingHistory.Rows.Count - 1).Item("DateTime") =
strDateTime
PingHistory.Rows(PingHistory.Rows.Count - 1).Item("PingResult") =
strPingResult
If PingHistory.Rows.Count > 10 Then
PingHistory.Rows.Remove(PingHistory.Rows(0))
End If
DataChanged.Invoke(mintIndex)
End Sub
Public Function GetPingHistory() As Data.DataTable
GetPingHistory = PingHistory
End Function
Public Property Description()
Get
Description = SystemList.strDescription
End Get
Set(ByVal value)

End Set
End Property
Public Property Host()
Get
Host = SystemList.strHost
End Get
Set(ByVal value)

End Set
End Property
Public Property Index()
Get
Index = mintIndex
End Get
Set(ByVal value)
mintIndex = value
End Set
End Property
Public Property SystemID()
Get
SystemID = SystemList.strSystemID
End Get
Set(ByVal value)

End Set
End Property
Public Property SystemType()
Get
SystemType = SystemList.strSystemType
End Get
Set(ByVal value)

End Set
End Property
Private Sub timerPing_Tick(ByVal sender As Object, ByVal e As
System.EventArgs) Handles timerPing.Tick
pingSystem.PingHost(SystemList.strHost)
End Sub
Private Sub pingSystem_OnError(ByVal sender As Object, ByVal e As
nsoftware.IPWorks.PingErrorEventArgs) Handles pingSystem.OnError
AddPingHistory(Now, e.Description)
End Sub
Private Sub pingSystem_OnResponse(ByVal sender As Object, ByVal e As
nsoftware.IPWorks.PingResponseEventArgs) Handles pingSystem.OnResponse
AddPingHistory(Now, "Ping Responded in " & e.ResponseTime & "ms")
End Sub
End Class
\\\
 
Dave wrote:
Thanks for the help, but still no success. The application freezes on
the Label2.Invoke method.

Here is the complete source. Every 3 seconds a ping is sent out to a
host, the ping response (good or bad) forces an Invoke. The idea is
to eventually pass a table with the latest pings.

I have looked through all of the web examples and this delege should
work, but doesn't. I've tried about 8 different ways with no success.
<snip>

I tried to reproduce your scenario here and it worked without apparent
problems (notice that in my setup I just created a RunTest method in
clsSystem that would call AddPingHistory at random from a separate
thread).

Just to state that I didn't speak of flowers (as an old saying goes,
here), let me suggest you a couple of changes:

In clsSystem, it would be safe to test the delegate before Invoking:

If DataChanged IsNot Nothing Then DataChanged.Invoke(mintIndex)

Also, I made some cosmectic changes in frmOverview:

Private Sub MyDelegateSub(ByVal intIndex As Integer)
m_Text = "Received Something:" & Now & ":" & intIndex
SetText()
End Sub

Private Sub SetText()
Static Handler As MethodInvoker = AddressOf SetText
If InvokeRequired Then
Invoke(Handler)
Else
Label2.Text = m_Text
End If
End Sub

As you can see, the test for InvokeRequired is placed in SetText, I
suppose it simplyfies the coding of other methods that would need to
call SetText from other threads. But, as I said before, this is mostly
cosmectic, as your original code seems to be working (thread-wise).

Regards,

Branco.
 
Back
Top