I've got this working. It's not as universal a solution as it could have been 
if I'd adopted all of Jake's blogs, but it satisfies the current need.

In case anybody else is interested, these were the key elements:

The XML Menu data provider (you can see I added the "callbackclass" to the 
<menuitem/> as Jake suggested, but I didn't figure out how to avoid using the 
full class path):

<mx:XML id="xmlFormMenuOptions" >
  <Menu id="MenuOptions">
    <menuitem id="RenameChartMenuOption" label="Rename Chart..." 
callback="PopUpTextBox" arguments="RenameChart, RenameChartButtonTextBox, 
{nameChartSelected}" enabled="{selectedChartingPanelTableCell}" />
    <menuitem id="OpenAlertsWindowMenuOption" label="Open Alerts Window" 
callback="setValue" callbackclass="Classes.DataManagers.ProjectProperties" 
arguments="ShowAlertsWindow, true" />
  </Menu>
</mx:XML>

The Menu control event handler (I stuck with callLater as it seemed to work 
fine - I didn't check to see if I could pass an array of arguments to a 
Function variable):

private function formMenuButton_click(event:MenuEvent):void {
  var callback:String = event.it...@callback;
  var className:String = event.it...@callbackclass;
  var classDefinition:Class = className ? getDefinitionByName( className ) as 
Class : undefined;
  var arguments:String = event.it...@arguments;
  var arrayArguments:Array = UtilityFunctions.ToArgumentArray(arguments, this);

  if( callback ) {
    if( className ) {
      if( arguments ) {
        callLater(classDefinition[callback], arrayArguments);
      } else {
        callLater(classDefinition[callback]);
      }
    } else {
      if( arguments ) {
        callLater(this[callback], arrayArguments);
      } else {
        callLater(this[callback]);
      }
    }
  }
}

The ToArgumentArray scrubbing function:

public static function ToArgumentArray(arguments:String, 
container:Object):Array {
  var arrayArguments:Array = arguments.split(",");
  
  //Replace argument strings with object references when the strings represent 
real objects within this component
  var i:int = 0; //Argument offset counter
  for each( var arg:String in arrayArguments ) {
    //Replace all of the leading spaces in the arguments
    while( arg.search(" ") == 0 ) {
      var replacePattern:RegExp = /\s/; //Replace the space character (note 
that the global flag is not set)
      arg = arg.replace(replacePattern, ""); //Replace the first space with a 
blank character
    }
    arrayArguments[i] = arg;
    
    //Replace all of the lagging spaces in the arguments
    while( arg.charAt(arg.length - 1) == " " ) {
      arg = arg.substr(0, length - 2); //Replace the last space with a blank 
character
    }
    arrayArguments[i] = arg;
    
    //Replace any objects in this with the object reference
    if( container.hasOwnProperty(arg) )
      arrayArguments[i] = container[arg]; //Replace the argument string with a 
reference to the object

    //Replace the "null" string with null
    if( arg == "null" )
      arrayArguments[i] = null; //Replace the argument string the null operator
    i++;
  }
  
  return arrayArguments;
}


--- In [email protected], "edlueze" <edlu...@...> wrote:
>
> Ah - yes - this looks pretty cool! I'll start digging into it immediately. 
> Thanks for the reference!!
> 
> --- In [email protected], "Jake Churchill" <jake@> wrote:
> >
> > I would separate the classname out into another argument in your XML, so in
> > your example that's not working, you'd have:
> > 
> >  
> > 
> > <menuitem id="OpenAlertsWindowMenuOption" label="Open Alerts Window"
> > callbackClass="ProjectProperties" callback="setValue"
> > arguments="ShowAlertsWindow, true" />
> > 
> >  
> > 
> > Then you'd have to dynamically instantiate that class and call the assigned
> > method.  I have a couple tutorials on my blog about doing this:
> > 
> >  
> > 
> > http://jake.cfwebtools.com/2009/04/08/dynamically-instantiate-a-class/ 
> > 
> > http://jake.cfwebtools.com/2009/05/15/flex-dynamic-casting-of-data/ 
> > 
> >  
> > 
> > The 2nd one deals with data casting which might not be as relevant but I
> > still do the class stuff dynamically.  Here's an example of what this might
> > look like for you:
> > 
> >  
> > 
> > var className:Class = Class( getDefinitionByName( getQualifiedClassName(
> > "ProjectProperties" ) ) );
> > 
> > var function:Function = className["setValue"];
> > 
> > function.call(className[or null], parmaters.);
> > 
> >  
> > 
> > FYI, this code is completely untested, just trying to give you an idea.
> > 
> >  
> > 
> > Jake Churchill
> > 
> > CF Webtools
> > 
> > 11204 Davenport, Ste. 100
> > 
> > Omaha, NE  68154
> > 
> > http://www.cfwebtools.com
> > 
> > 402-408-3733 x103
> > 
> >  
> > 
> > From: [email protected] [mailto:[email protected]] On
> > Behalf Of edlueze
> > Sent: Tuesday, May 19, 2009 6:00 AM
> > To: [email protected]
> > Subject: [flexcoders] Call a Flex Function by Name
> > 
> >  
> > 
> > 
> > 
> > 
> > 
> > 
> > I'm using a Menu control. Normally I'd catch the Menu's control event and
> > then act on whatever selection the user made with something like a switch
> > block.
> > 
> > But I wanted to see if I could be more clever and embed the function name
> > and parameters in the XML Menu data provider itself, like this:
> > 
> > <mx:XML id="xmlFormMenuOptions" >
> > <Menu id="MenuOptions">
> > <menuitem id="AddNewChartMenuOption" label="Add Chart..."
> > callback="AddNewChartButton_click" arguments="" />
> > <menuitem id="RenameChartMenuOption" label="Rename Chart..."
> > callback="PopUpTextBox" arguments="RenameChart, RenameChartButtonTextBox,
> > {nameChartSelected}" enabled="{selectedChartingPanelTableCell}" />
> > <menuitem id="DeleteChartMenuOption" label="Delete Chart"
> > callback="PopUpCheckBox" arguments="DeleteChart, DeleteChartButtonCheckBox"
> > enabled="{selectedChartingPanelTableCell}" />
> > <menuitem id="RaiseChartOrderMenuOption" label="Raise Chart Order"
> > callback="RaiseChartOrderButton_click" arguments=""
> > enabled="{RaiseChartOrderButton.enabled}" />
> > <menuitem id="LowerChartOrderMenuOption" label="Lower Chart Order"
> > callback="LowerChartOrderButton_click" arguments=""
> > enabled="{LowerChartOrderButton.enabled}" />
> > <menuitem id="OpenAlertsWindowMenuOption" label="Open Alerts Window"
> > callback="ProjectProperties.setValue" arguments="ShowAlertsWindow, true" />
> > <menuitem id="ChartingPanelWhatAmIMenuOption" label="What am I?"
> > callback="PopUpMessage" arguments="ChartingPanelWhatAmIMenuOption, null " />
> > <menuitem id="HelpMenuOption" label="Help" callback="" />
> > </Menu>
> > </mx:XML>
> > 
> > If you look at each <menuitem/> you'll see a 'callback' attribute and an
> > 'arguments' attribute. The arguments are comma-delineated that I need to
> > parse into an Array and scrub: for example, by converting string references
> > into real ones like this:
> > 
> > if( this.hasOwnProperty(arg) )
> > arrayArguments[i] = this[arg];
> > 
> > I then have been successful in using the callLater function like this:
> > 
> > callLater(this[callback], arrayArguments);
> > 
> > So far so good - it works great!
> > 
> > But I've hit a tiny snag. If the function is in a foreign class then
> > everything falls apart. For example, this won't work:
> > 
> > callback = "ProjectProperties.setValue"
> > arguments = "ShowAlertsWindow, true"
> > 
> > Because there is no such thing as this[ProjectProperties.setValue] the
> > callLater function can't handle it. I can easily build a little stub but I'm
> > hoping to keep things clean and elegant.
> > 
> > I've been searching for a way to call-by-name in Flex but haven't found one
> > (except for callLater). Is there a way I can do this?
> >
>


Reply via email to