Some favorite site feeds aggregated locally: iPhone Development RSS   Adobe Labs RSS   Macrumors RSS

Monday, April 21, 2008

AS3 dispatching events with arguments...

Monday, April 21, 2008   

(There are two (2) updates to this post):

I am curious as to the best way to dispatch events from an AS3 class to a listener in another, passing arguments through (I'd prefer properties really). What I have so far actually works, but it requires some arcane knowledge of the inner-workings of the class doing the dispatching (it feels really bad to me at the moment). 

What I have is a custom Event class for dispatching events, and its imported and used from within another class. A third document class listens to the class for events. I'd like to get properties of the returned event like we could in AS2 hackery, but right now I only seem to be able to pass arguments, and then you'd have to know which argument is what to make them useful (bad).

The custom event class:

package
{
    import flash.events.Event;
  public class EventType extends Event
    {
        public var arg:*;
        public var properties:Object;

        public function EventType( type:String,
                                   bubbles:Boolean = false, 
                                   cancelable:Boolean = false, 
                                   ... a:* ):void
        {
            super( type, bubbles, cancelable );
            arg = a;
        }
        
        // Override clone
        override public function clone():Event
        {
            return new EventType( type, bubbles, cancelable, arg );
        };
  }
}


From within the class dispatching the event ("10" is just a test):
...
public static const UPDATED:String = "updated";
...
dispatchEvent( new EventType( ClassName.UPDATED, false, false, "10" ) );


And from the document class:
...
classInstance.addEventListener( PartitionGauge.UPDATED, updateListener );
...
private function updateListener( e:EventType ):void
{
    trace( e.type );
    trace( e.arg.length );
    trace( e.arg[0] ); // wtf is this really supposed to be?
}

So you can see that this approach does work, but you'd have to have special knowledge to get the .arg from the returned event, and you'd also need to know what each index of the returned arguments actually map to. It would be cool to be able to pass back a property on the event itself, like:

trace( e.value );

and thing like that. I haven't figured that part out quite yet, and I've spent time googling and haven't found a whole lot just yet. But there must be a pretty easy to use solution for this kind of thing. I'd hope.

Update:

Well, I've come across a different approach, and it's similar to the one above, but instead of arguments it uses properties on a returned object. At least that is more approachable, although you need an intermediary object to inspect through.

Here is the class:

package 
{
    import flash.events.Event;
    public class CustomEvent extends Event
    {
        public static const DEFAULT_NAME:String = "CustomEvent";
        public static const ON_UPDATE:String = "onUpdate";
        public var params:Object;
        
        public function CustomEvent( $type:String,
                                     $params:Object,
                                     $bubbles:Boolean = false,
                                     $cancelable:Boolean = false )
        {
            super( $type, $bubbles, $cancelable );
            this.params = $params;
        }
        
        public override function clone():Event
        {
            return new CustomEvent( type, this.params,
                                    bubbles, cancelable );
        }
        
        public override function toString():String
        {
            return formatToString( "CustomEvent", 
                                   "params", "type",
                                   "bubbles", "cancelable" );
        }
    }
}


And within my class that dispatches the event (test properties here):

...
import CustomEvent;
...
this.dispatchEvent( new CustomEvent( CustomEvent.ON_UPDATE,
                                     {value:"10", foo:"hello" } ) );


And now in the document class

...
classInstance.addEventListener( CustomEvent.ON_UPDATE, updateListener );
...
private function updateListener( e:CustomEvent ):void
{
    trace( e.type );//onUpdate
    trace( e.params.value );//10
    trace( e.params.foo );//hello
}

So, that's a little better. You still have to access through the params object instead of simply on the returned event object itself, but it's better, don't you think?

You don't have to parse through arcane argument indexes wondering what they are. At least with properties you can use the name of the property outright. And that makes the whole thing more approachable.

Update 2 (yes, another one):

Well, here is something that is more approachable still.

package 
{
    import flash.events.Event;
    public class CustomChangeEvent extends Event
    {
        public static const CHANGE:String = "EVENT_CUST_CHANGE";
        public var who:*;
        public var value:Number;
        
        public function CustomChangeEvent( type:String, who:*, 
                                           value:Number )
        {
            super( type );
            this.who = who;
            this.value = value;
        }
        
        public override function clone():Event
        {
            return new CustomChangeEvent( type,
                                          this.who,
                                          this.value );
        }
    }
}

You will get code completion in FlashDevelop, you can access the properties of the return event object, you get strong typing for some of it (I left who as a wildcard right now since it's not settled what it will be yet, probably a Sprite though). When I make that change, the class will have strong typing through it, and everything will be nice and neat.

I think this approach will truly work well in any circumstance.
 
 Return to the main page
Comments:

There are currently 5 Comments:

Anonymous Logrey said...
“I've used this method to pass parameters before (it involves calling a custom function which simply returns the Event function).

In this example, I want to change a button's alpha value based on predetermined amounts (and want to simply pass that value, instead of having a "switch" or "if" statement in the called function):
-----------------------

myButton1.addEventListener(MouseEvent.CLICK,customAlphaChange(.5));
myButton2.addEventListener(MouseEvent.CLICK,customAlphaChange(.8));

function customAlphaChange(theValue:Number):Function {
return function(e:MouseEvent) {
e.target.alpha=theValue;
}
}”
 
Blogger e.dolecki said...
April 21, 2008 12:19 PM
“That works, but you'll need a different listener for every single alpha value that you'd want. This is a good solution that fit your needs, I was hoping for something more general.

I need to pass positions, and values from within a class out, so it wouldn't really work in my case.”
 
Blogger e.dolecki said...
April 21, 2008 12:27 PM
“Actually, you could just use a generic function, interrogate the alpha value of the object being affected, and up it/down it as needed.”
 
Anonymous Josh Chernoff said...
April 21, 2008 1:44 PM
“I like to do things short hand if I can.

var someVar = .1;
my_mc.addEventListener(MouseEvent.CLICK, function(e:MouseEvent){e.target.alpha = someVar});

To tell the truth I don't understand the problem your facing.

Is it you just don't always know what the argument your passing is?

Could you tell me a example where it did not work using the custom event class to pass arguments?”
 
Blogger e.dolecki said...
April 21, 2008 2:03 PM
“Josh,

If you are addressing my solution post, imagine you have a class & it dispatches an update event based on a user interaction.

There are multiple items that can be affected via the interaction, and you might want to get the update event passed back and also which part of the UI created the update event, the new value of that element, etc.

The addEventListener might require data passed back to it from the class its listening to, instead of receiving the event & then inspecting via getters what might have changed, etc.”
 
 Leave a comment