Threading & raising event question

A

Ajak

Hi all,

I would like to write a class (Task) with a method to do some lengthy
process based
on several of the class properties. The method is running on different
thread.
During the execution of the method, the class is suppose to fire events such
as
ProgressChanged. And it raises Completed and Failed event on completion and
error
respectively.

The question is, how do I raise the event in the main thread (where the
class is instantiated)
without having to write the InvokeRequired checking for each controls I want
to update, say,
ProgressBar and Labels. In other words, how to fire the events that this
class exposes so
that at the receiving end of the event, I do not have to write the tedious
Invoke function to
access/alter its controls properties.

FYI, the class is not a control. Just a plain Class Library.

Below is the simplified class structure I did:-

Class Task
Private m_prop1 as integer
Private m_prop2 as integer

Public Event ProgressChanged(ByVal currentvalue as integer)
Public Event Completed()
Public Event Failed(ByVal errmessage as String)

Public Property Property1() As Integer
....
End Property

Public Property Property2() As Integer
....
End Property

Public Sub ProcessTask()
Dim t As New Thead(AdressOf DoWork)
t.Start()
End

Private Sub DoWork()
Do
... (do processing here)
... (fires ProgressChanged event during the processing - RaiseEvent
ProgressChanged(val))
Loop While ....

RaiseEvent Completed()
End Sub

End Class

Thanks in advance.

Razak.
 
A

Armin Zingler

Ajak said:
Hi all,

I would like to write a class (Task) with a method to do some
lengthy process based
on several of the class properties. The method is running on
different thread.
During the execution of the method, the class is suppose to fire
events such as
ProgressChanged. And it raises Completed and Failed event on
completion and error
respectively.

The question is, how do I raise the event in the main thread (where
the class is instantiated)
without having to write the InvokeRequired checking for each
controls I want to update, say,
ProgressBar and Labels.

You must use InvokeRequired unless you know that the procedure is running in
the none-UI thread. But you don't have to call it for any control, just once
at the start of the event handler. In any case you must call Invoke once.
In other words, how to fire the events that
this class exposes so
that at the receiving end of the event, I do not have to write the
tedious Invoke function to
access/alter its controls properties.


Without Invoke/Invokerequired is not possible.
FYI, the class is not a control. Just a plain Class Library.

Below is the simplified class structure I did:-

Class Task
Private m_prop1 as integer
Private m_prop2 as integer

Public Event ProgressChanged(ByVal currentvalue as integer)
Public Event Completed()
Public Event Failed(ByVal errmessage as String)

Public Property Property1() As Integer
....
End Property

Public Property Property2() As Integer
....
End Property

Public Sub ProcessTask()
Dim t As New Thead(AdressOf DoWork)
t.Start()
End

Private Sub DoWork()
Do
... (do processing here)
... (fires ProgressChanged event during the processing -
RaiseEvent ProgressChanged(val))
Loop While ....

RaiseEvent Completed()
End Sub

End Class

Thanks in advance.

Razak.


Armin
 
A

Ajak

Thanks for your reply.

I think you get the wrong idea of what I'm trying to achieve. It's not that
I don't want to use the Invoke method at all.
But I need a way to do it in the class itself before the event is raised, so
that I don't have to use Invoke for every each
controls I want to update in the form's event that use the class.

However, I found the solution on the net as follows:-

Decalre the event in the class:
Public Event Found(ByVal sender As Object, ByVal e As FoundEventArgs)

Then create a sub to raise the event as follows:-

Protected Friend Sub OnFound(ByVal e As FoundEventArgs)
Dim handler As System.Delegate = FoundEvent
If handler IsNot Nothing Then
For Each singlecast As FoundEventHandler In
handler.GetInvocationList()
Dim syncInvoke As ISynchronizeInvoke =
DirectCast(singlecast.Target, ISynchronizeInvoke)
Try
If syncInvoke IsNot Nothing AndAlso
syncInvoke.InvokeRequired Then
syncInvoke.Invoke(singlecast, New Object() {Me, e})
Else
singlecast(Me, e)
End If
Catch
End Try
Next
End If
End Sub

In the processing method, in order to raise the event, call the sub above as
below:-

OnFound(New FoundEventArgs())

Therefore, in the parent form, I can instantiate the class:-

Dim WithEvents mytask As New Task

And write the event handler:-

Private Sub mytask_Found(ByVal sender as Object, ByVal e As
FoundEventArgs) Handles mytask.Found
lblStatus.Text = e.Message
Me.Refresh()
End Sub

No need to write the tedious lblStatus.InvokeRequired checking and
lblStatus.Invoke call!
Save time when you need to update several form controls at single event
raised by the class.

Thanks.
 
A

Armin Zingler

Ajak said:
Thanks for your reply.

I think you get the wrong idea of what I'm trying to achieve.

No, I did get it, but you were using Raiseevent in your code (like probably
99.99% of us), and that's what I was referring to. The code below is "a
little" different. All I can add is: Don't forget to query if the target
implements the ISynchronizeInvoke interface (because Directcast might fail).

Armin
 
A

Ajak

Thank you for the tips.

Armin Zingler said:
No, I did get it, but you were using Raiseevent in your code (like
probably 99.99% of us), and that's what I was referring to. The code below
is "a little" different. All I can add is: Don't forget to query if the
target implements the ISynchronizeInvoke interface (because Directcast
might fail).

Armin
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top