What to do with the Bindevent return value?


The BINDEVENT( ) Function allows you to attach code to run when other code is executed. It’s sort of like an event hook.


The first example below has a method called “Foobar” which is called to return a value. After BindEvent is called, the method is called again. The “FoobarHandler” code is executed. Now there are 2 routines that were called when the original “Foobar” method was called, each of which return a value. Which Return value should be used?


 


Event handlers handle events. They don’t have return values (except maybe to say whether the event was indeed handled).


 


The released version of VFP8 used the return value of the delegate. It gets even more complicated when there are multiple routines bound to the original, some executing before and some executing after. Customers reported such issues so that was changed for the released version of VFP9: the Return value of the original method is used.  That way, the behavior is consistent and predictable. Also, adding a BindEvent to code won’t change the original author’s intended return value.


 


oevent = CREATEOBJECT(“cEvent”)


ohandler = CREATEOBJECT(“cHandler”)


?”Before bindevent”


? oevent.Foobar()


?


?


?”Bindevent”,BINDEVENT(oevent,”Foobar”,ohandler,”FoobarHandler”,1)


 


?”After bindevent”


 


? oevent.Foobar()


 


DEFINE CLASS cEvent AS custom


      PROCEDURE Foobar


            RETURN PROGRAM()+” Main Return Value”


ENDDEFINE


 


 


DEFINE CLASS cHandler AS custom


      PROCEDURE FoobarHandler


            ?”Here I am in “+PROGRAM()


            ?


            RETURN PROGRAM()+” Handler Return Value”


ENDDEFINE


 


 


 


This code binds 2 methods to the original Foobar:


 


oevent = CREATEOBJECT(“cEvent”)


ohandler = CREATEOBJECT(“cHandler”)


?”Before bindevent”


? oevent.Foobar()


?


?


?”Bindevent”,BINDEVENT(oevent,”Foobar”,ohandler,”FoobarHandler”,1)


?”Bindevent”,BINDEVENT(oevent,”Foobar”,ohandler,”FoobarHandler2″,1)


 


?”After bindevent”


 


? oevent.Foobar()


 


DEFINE CLASS cEvent AS custom


      PROCEDURE Foobar


            RETURN PROGRAM()+” Main Return Value”


ENDDEFINE


 


 


DEFINE CLASS cHandler AS custom


      PROCEDURE FoobarHandler


            ?”Here I am in “+PROGRAM()


            ?


            RETURN PROGRAM()+” Handler Return Value”


      PROCEDURE FoobarHandler2


            ?”Here I am in “+PROGRAM()


            ?


            RETURN PROGRAM()+” Handler2 Return Value”


ENDDEFINE


 


 

Comments (3)

  1. Tom says:

    Hi Calvin,

    Firstly, thanks for the great blog, I love hearing about the internals of VFP.

    I just came across the changed BindEvent() behaviour when trying to get my VFP8 code to run in VFP9, and you’re probably the perfect person to answer a question I have!

    My problem is that I was actually using the BindEvent() functionality, and in particular the ability to override the return value, as a kind of "on the fly subclassing". Now in VFP9, it doesn’t seem like this is possible anymore. BindEvent(…,nFlags=3) means I must call RaiseEvent() which changes my original code.

    I can see that "Event handlers handle events", but the BindEvent() method actually allowed the creation of something closer to "lambda methods" or "anonymous code blocks" — i.e. it provided a method for a class’s client to specify behaviour to be executed, not just be notified of an event.

    For example (pseudo code):

    DEFINE CLASS foobar AS custom

    PROCEDURE mainMethod()

    MessageBox("Result: "+this.subMethod())

    ENDPROC

    PROCEDURE subMethod()

    Return "default"

    ENDPROC

    ENDDEFINE

    DEFINE CLASS foobarClient AS custom

    PROCEDURE clientCode()

    LOCAL X

    X = createobject("foobar")

    BindEvent(X,"subMethod",this,"overriddenMethod",1)

    X.mainMethod()

    ENDPROC

    PROCEDURE overriddenMethod()

    Return "New behaviour"

    ENDPROC

    ENDDEFINE

    Calling foobarClient::ClientMethod() should fire a messagebox with "Result: New behaviour". The effect is similar to creating a subclass of foobar, but actually a bit closer to passing in a string and macro expanding or ExecScript()ing it.

    So, the question: Is there a way to retain the original behaviour or will I have to sprinkle the code with RaiseEvent()s?

  2. Rick Strahl says:

    Looks to me like in VFP 9 method invokation modes 2 and 3 don’t work at all with the delegate not firing…

    Also in those scenarios where the event is ‘skipped over’ if possible shouldn’t the delegate return the value?

  3. Here’s a blog post that I wrote a long time ago, but forgot to publish. Marco Cenzato commented on Why