Branco said:
Lothar (!!) wrote:
(inline)
In VB it would be:
Delegate Function lbEvHandler(Uk As IUnknown) As Boolean
Notice that only the method signature is necessary, not the base class.
In fact any method with this same signature (even shared (static)
methods) regardless of the class where it's implemented is a suitable
lbEventHandler delegate.
Delegates have single and multi dispatch built in. When you add a
handler to a delegate, whenever it's Invoke method is called, your
handler will be called also, automagically.
One possible scenario of using delegates is as simple function
pointers. For historical reasons, you pass the address of a method to a
delegate using the AddressOf operator. You execute the actual
'delegated' method by calling the delegate's Invoke method.
Function OnLblEvent(U As IUnknown) AS Boolean
'...
End Function
'...
Dim S As New lbEvHandler(AddressOf OnLblEvent)
'Which is similar to:
Dim R As lbEvHandler = AddressOf OnLblEvent
Dim I As IUnknwon
Dim Result As Boolean = S.Invoke(I)
You only register the function to the handler. (Line 1)
I need to dispatch an event to one S[eventname] in in a list of
multiple S'.
A mapping of ID/EventName to S must be applied (dispatch).
I have the following requirements:
A GUI client get's information, what menu and what entries it has to
build.
This information is passed by strings, because the GUI cannot call
function pointers.
To be prcicely, it is WinDev and our middle layer is .NET Assemblies.
' A plugin's code
Function OnMenuOpenFileEvent(U As IUnknown) AS Boolean
'...
End Function
Function OnMenuNewFileEvent(U As IUnknown) AS Boolean
'...
End Function
Function OnMenuSaveFileEvent(U As IUnknown) AS Boolean
'...
End Function
Sub Init()
Dim evM as EventManager = GetGlobalEventManager() ' Singleton
Dim appM as ApplicationManager = GetGlobalApplicationManager() '
Singleton
Dim disp as Dispatcher = GetGlobalDispatcher() ' Singleton
Dim evID as Integer
evM.registerHandler("OnMenuOpenFileEvent", evID) ' evID parameter
gets the id (ByRef)
disp.registerHandlerMethod("OnMenuOpenFileEvent", AddressOf
OnMenuOpenFileEvent)
or
disp.registerHandlerMethod(evID, AddressOf OnMenuOpenFileEvent)
to avoid multiple eventID lookups (by invoking
EventManager.resolveName("OnMenuOpenFileEvent") inside of the
dispatcher).
evM.registerHandler("OnMenuNewFileEvent", evID) ' evID parameter
gets the id (ByRef)
disp.registerHandlerMethod("OnMenuNewFileEvent", AddressOf
OnMenuNewFileEvent)
evM.registerHandler("OnMenuSaveFileEvent", evID) ' evID parameter
gets the id (ByRef)
disp.registerHandlerMethod("OnMenuSaveFileEvent", AddressOf
OnMenuSaveFileEvent)
appM.AddMenu("&File")
appM.AddMenuEntry("&File", "&New", "OnMenuNewFileEvent")
appM.AddMenuEntry("&File", "&Open", "OnMenuOpenFileEvent")
appM.AddMenuEntry("&File", "&Save", "OnMenuSaveFileEvent")
End Sub
The handling of information passing (AddMenu*) to the GUI is not shown
here.
Maybe it will be message passing.
As you see, the GUI did not directly expose it's menu Click's, because
it is not VB,
nor any .NET language (but can use .NET).
So I must use identifers like strings, or to fasten it, their
registered ID's.
The GUI could use .NET assemblies, thus it will have access to the
dispatcher et al.
Given, the GUI controls are proper set up to route its events to a
WinDev specific
handler (catch all), it is capable to build a bunch of information, the
handler needs and
pass it with the following code to the handler in behind:
myParameter is IParameter object dynamic // WinDev code !
myParameter = getParameterInstance() // WinDev code !
myResult is IParameter object dynamic // WinDev code !
myResult = getParameterInstance() // WinDev code !
// Fill the parameter with the required data (application specific)
// What to fill was given to the GUI with the handler to be 'called',
eg
// 'Fill with control 'Name', 'SureName', 'Age' ...
// This is very generic and highly dynamic.
MyParameter.addParameter('Name', {'Name'} .. Value) // WinDev code !
disp.dispatch(event.id, myParameter, myResult) // WinDev code !
AddHandler OkButton.Click AddressOf OnButtonClick
AddHandler OkButton.Click AddressOf ClickHandler
Addhandler OkButton.Click AddressOf AnotherClickHandler
Direct access to the control is impossible. Control is in GUI and
handler in plugin.
'...
Likewise, you can use a given handler to listen to any number of events
from whatever sources:
For Each C As Control In Controls
If TypeOf C is Button Then
AddHandler C.Click, AddressOf GlobalClickHandler
End If
Next
I'm not sure if I understand the question. The AddressOf returns a
specific instance of a method, be it a shared (static) or an non-shared
method of a class. I mean, it represents an actual method from a specic
object.
One navigation toolbar with buttons for 'First', 'Previous', 'Next' and
'Last' and multiple
data windows with their handlers. The GUI part of the data window is
built up upon
information from the middle layer. The handlers are specific to the
'bussiness' object.
On C++ I have solved this, by prepending the address od 'this' as
string to the handler
name. This is not yet one button to many handlers (one per active
window), but it should
be. The dispatcher may not find the eventhandler by searching for
'OnNext', but the GUI
may provide information, such as window name. Thus this could be
prepended and also
the bussiness logic, that requests for a data window may provide a
window name, if
multible windows are needed.
Maybe the bussiness logic registers generic handlers for 'OnFirst', ...
and do
subdispatching like this:
if (!disp.dispatch("OnFirst", ...)) // If a generic handler is
registered
// Fallback to new approach
disp.dispatch("current window's name, that who lost the focus to
the toolbar" & "OnFirst", ...)
I think the Toolbar for data navigation is a good example.
/---> Handler for DataWindow1
Toolbar ---<
\---> Handler for DataWindow2
Actually, this would by a candidat of implementation for my C++
project, but also for
this one.
Thus only the real listener of such events gets informed.
And no event handler has to be re-registered at window focus issues.
Now, suppose you have many instaces of a given class (Listener), and
all of then wanted to 'listen' to events of a given object (Source),
say, the good old Click event.
Doing this with the dispatcher, I must implement a registerListener
method.
I think, that is not required yet.
To make things clearer, suppose you have a method in some other class
(Dispatcher) which gets a list of listeners that must be attached to
the source event:
Class Listener
Public Sub Click(Sender As Object): end Sub
End Class
Class EventSource
Public Event Click(Sender As Object)
'...
Sub DoIt
'...
RaiseEvent Click(Me) '<-- Keep an eye on here
End If
End Class
Class Dispatcher
Dim mEventSource As New EventSource
Sub AddListeners(List As IEnumerable(Of Listener))
For Each L As Listener In List
AddHandler mEventSource.Click, AddressOf L.Click
Next
End Sub
Sub MeanwhileOnAnotherPlanet
mEventSource.DoIt
End Sub
End Class
Maybe I use this sample for the listener or observer pattern.
When the Source class executes the RaiseEvent action inside the DoIt
method, all event handlers attached to the Click event will be executed
in the order in wich they were registered.
As you've seen, the AddressOf returns both the Object and method
addresses (the Object will be null for shared methods, I guess), but it
does so implicitly
The main issues may be, that I have no direct access from the event
source to the
event target. Using a dispatching mechanism with unique ID's per
application instance,
it would be possible to route an event to it's target.
It's a little more than dynamically creating delegates :-(
It has helped
Regards
Lothar