Accessing <mx:Image> from AS class - apache-flex

I want to draw something on an <mx:Image> I have in my .mxml file, but I want to keep my drawing implementation in a separate AS class.
How can I access my drawing AS class in my .mxml?

I think what your are asking is how to keep your actionscript files separate from the MXML files that contain your designs. The answer is simple:
Create your actionscript file. Include only methods in this file, and do not wrap the code in a package or class definition. The file should look like the following:
import mx.controls.Alert;
// ActionScript file
/**
*
* Created By jviers
* Created on Apr 14, 2011
*
*
*/
public function hello():Alert{
Alert.show("Hello World!");
}
Create your MXML file that contains your "Design" components. Create a Script element on this mxml file and set the source to the relative path of your ActionScript file. The MXML should look like this:
<?xml version = "1.0" encoding = "utf-8"?>
<s:Application xmlns:fx = "http://ns.adobe.com/mxml/2009"
xmlns:s = "library://ns.adobe.com/flex/spark"
xmlns:mx = "library://ns.adobe.com/flex/mx"
minWidth = "955"
minHeight = "600">
<fx:Script>
<![CDATA[
protected function button1_clickHandler ( event : MouseEvent ) : void {
// TODO Auto-generated method stub
hello ();
}
]]>
</fx:Script>
<fx:Declarations>
<!-- Place non-visual elements (e.g., services, value objects) here -->
</fx:Declarations>
<fx:Script source = "./scratch/MyFile.as" />
<s:Button label = "Show Alert" click = "button1_clickHandler(event)" />
</s:Application>
You'll notice that the ActionScript in the scratch.MyFile.as is executed when the application is run.
You can use this method to include your drawing logic in your application. The external ActionScript is treated as if it were the method definitions for the class generated by the MXML.
Let me caution you before everyone jumps all over me. This is NOT a best practice. There are specific use-cases in which to use this feature of Flex. Before I get to them let me explain why your notion of keeping "logic" separate from your "view" is inaccurate.
Flex MXML files are not view-only code. They are a declarative dialect that simplifies ActionScript class definitions. When a Flex Project is compiled using mxmlc or compc (those are the compiler programs for flex applications and component libraries, respectively), MXML files are pre-compiled into ActionScript class definitions. If you add the -keep-generated-actionscript directive to your compiler options in Flash/Flex Builder/Ant/command line compile command, the compiler will leave the generated classes that compose the ActionScript class derived from the declaritive MXML files in your project. Thus, an MXML file becomes a class.
Having ActionScript defined in a Script block in your MXML is NOT mixing "logic" with "presentation". Likewise, MXML does NOT define the presentation of your project. It simply is a declarative subset of the ActionScript language that makes it easier to define presentation classes. As proof, you can define ArrayCollections, Strings, and Vectors in MXML. Those classes are data containers and have absolutely nothing to do with presentation.
Now, the reason putting all your ActionScript in external files is not a good thing is that it makes maintaining your project a headache. Not only does someone have to look up your component defined in MXML, but now they have to hunt through the codebase to find the script named Logic.as relatively defined to your component.
The use-cases in which including the external ActionScript via Script.source are the following:
A group of predefined methods/properties are used in lots of components without change. Rather than copy-and-paste those methods into each component, you use the Script.source property to define the methods once and reference them throughout your application.
Similar, but different from use-case-1: You require a mix-in. Like an interface, a mix-in is a set of methods that must be defined for the component to work, and those methods must be reusable and define explicit input parameters and output parameters. Unlike an interface, those methods can be of any namespace type: protected, final, public, internal, mx_internal, etc., and can must function bodies, i.e. have code inside the {} function block. For an example of a "mix-in" think of an enumerable object, an object that has next(), previous(), reset(), and iterator(), methods. When you want to iterate over the object's properties, you call iterator(), which returns an iterator object that calls next() and previous() to fetch the next and previous property values from the object. You can mix this functionality into all kinds of objects and use them usefully. Also, your included functionality is encapsuled within the classes in which it is included. It works like an include directive in AS3.
Hope this helps both your perceptions of what "logic" and "presentation" are in Flex, and helps you solve your particular issue.

I think you have it backwards. If you include the .as file into the .mxml using the <mx:script> tag, you will be able to see the function defined therein.
To address the image, set it an id attribute. From that point, it becomes addressable as if it were defined using ActiveScript like
var image:Image = new Image()

It is no very obvious what relations are between MXML and AS classes you're talking about but the simplest way is create public method in MXML which returns Image. Something like this:
…
<mx:Script>
<![CDATA[
public function getImage():Image
{
return myImage;
}
]]>
</mx:Script>
<mx:Image id="myImage" />
…
So as far as you can refer to your MXML from AS you can call this method.

If you want to draw something in an image by using your own drawing class, i suggest you add it to the constructor of your drawing class, like so:
/*********************************************
* Variables
*********************************************/
private var _myImageIWantToDrawIn:Image;
/*********************************************
* Properties
*********************************************/
public function set image(value:Image):void
{
_myImageIWantToDrawIn = value;
}
public function get image():Image
{
return _myImageIWantToDrawIn;
}
/*********************************************
* Constructor
*********************************************/
public function myDrawingclass(imageYouWantToDrawIn:Image)
{
_myImageIWantToDrawIn = imageYouWantToDrawIn;
}
like this, you can always access the image from that class and draw in it if you want to. (You can also access the _myImageIWantToDrawIn.graphics if you want to draw programmaticaly). you can add the properties if you want to change the image in runtime. you can then simply say: myDrawingclass.image = imageYouWantToDrawIn;
edit: this previous answer was actually the opposite of what you are asking, but I think this will do just fine for what you would want to do. If you want to access your as-class, just make an instance of your class and add public methods to it instead of private ones.

Related

Accessing a class from an externally loaded Flex SWF

Using Flex 4, I have created an SWF that has the following code:
<fx:Script>
<![CDATA[
import mx.events.FlexEvent;
import com.Index ;
protected function start(event:FlexEvent):void
{
this.addElement (new Index());
}
]]>
What I am trying to do is load the SWF into another SWF and access this class. The problem is the Flex class Main is what is recognized by the loading SWF. I have tried accessing its children(elements), but the added item is not there.
My ultimate goal is to load an SWF that has an object that can interact with the loading SWF.
lee
Addition:
Thanks for your addition. The code is working now, but it only access the Flex class created by the MXML. Here is the complete MXML:
<?xml version="1.0" encoding="utf-8"?>
width="1000"
height="700"
creationComplete="start(event)"
>
<fx:Declarations>
<!-- Place non-visual elements (e.g., services, value objects) here -->
</fx:Declarations>
<fx:Script>
<![CDATA[
import mx.events.FlexEvent;
import Test;
protected function start(event:FlexEvent):void
{
this.addElement(new Test ());
}
]]>
To try and extract the class Test, I did the following:
var MainC:Class = L.contentLoaderInfo .applicationDomain .getDefinition ("Main.Test") as Class;
var O:Object = new MainC();
This returns 'Error #1065: Variable Test is not defined.'
I get the same result if I use ...getDefinition("Test").
If I use ....getDefinition("Main"), it works fine. But I cannot seem to reach into the class it creates to extract anything.
lee
The best way to go is to have that swf as a library (swc) and link it to your project. That way you could achieve strong typing, and access the underlying classes.
If you don't have access to neither the library release of the swf object, nor it's source to build the swc library yourserlf, you can still use the external classes, but not with strong typing:
var ExtIndex : Class = loader.contentLoaderInfo.applicationDomain.getDefintiion("Index");
var instance : Object = new ExtIndex();
this.addElement(instance);
so you must define your instance as Object.
Although, in case the Index class in your swf implements an interface that you know of (you have it in your project), like I_Index, you can use it instead of Object:
var instance : I_Index = new ExtIndex();
this.addElement(instance);
You might also find this article useful in deciding which way to go.
Update 1
Declaring the loader in Flex:
var loader: flash.display.Loader;
[...]
var ExtIndex: Class = loader.contentLoaderInfo.applicationDomain.
getDefinition("your.package.Index") as Class;
In case you have your Index class in a package (other than the default), you should specify it in its name. The code above searches for the Index class inside the your/package package.
API Docs for getDefinition:
Gets a public definition from the
specified application domain. The
definition can be that of a class, a
namespace, or a function.
Parameters:
name The name of the definition.
Returns: The object associated with the definition.
Throws: ReferenceError - No public definition exists with the specified name.
Language Version: 3.0
Player Version: Flash 9, AIR 1.0
Update 2
To make the Test class defined in your external application accessible from other flex applications by loading the swf that contains it, you must have a variable declared with that type inside your swf's main application. Otherwise that class won't be compiled into the swf (to save the size of it). Flex only compiles classes actually in use into the swf, so it's not enough to just import a class. In the main application of your swf you should declare:
var _tmp:Index;
This makes sure that the Index class will be compiled into your swf, and you can access it from an other application which loads that swf.
Update 3
You can check out how this works at this link. The source code is included too.
Since there are two projects, in the [src] folder you can see the test's source, and in the [source path src] folder the source of the externally loaded swf file.
If you download the whole project, make sure that you separate the two projects.

flex sharing data between different components

i have in my flex application various mxml components that all need to show stats based on the same data. how can i go about doing that? do i need some mvc framework like cairngrom or puremvc for that or can i do it without them?
any design ideas?
You don't need any framework for that. Do you know about data-binding?
http://www.flexafterdark.com/docs/Flex-Binding
This way you can set your data as dataprovider for many components. For example to show your data in dataGrid you set in mxml it's attribute
dataProvider="{yourDataArrayCollectionIdentifier}"
and in your arrayCollection declaration you need to set metatag [Bindable]
[Bindable] var yourDataArrayCollectionIdentifier : ArrayCollection;
there are other datatypes you can use as dataprovider, just arrayCollection is the most common
There are a handful of approaches to this. For encapsulation purposes, you should isolate your shared data out into a separate class; possibly a Value Object although it does't have to be.
Then create a public variable property in each MXML Component of this classes type. When you create the instance of each mxml component, pass in your 'global' instance of the data class.
You don't need to use an MVC framework, but the Cairngorm Model Locator could also be used to address this problem. The Model Locator is a singleton. You'd store your data inside the singleton instance; and then each MXML Component would reference the singleton for the data. Creating an external dependency like this breaks encapsulation, though. I much prefer the parameter passing route for non-application specific components.
package
{
public class ApplicationViewModel
{
[Bindable] public var message:String = "";
}
}
You can now use this message across your MXML where you make the instance of that.
A singleton class is used in different scenarios where you want to hold some information of all states. A better example would be Chess Board, where your board is Singleton class and its state should never change as you have to keep track of all coins moved across the board and its position.
You are injecting this message variable in the views where you want to show the data.

1020 undefined property right after associative array defined via var

I'm really frustrated in this case.
While developing with Adobe Flex, I'm working on my first application - and use pretty much actionscript.
In my mxml application, I include as3 file via <mx:Script source="as/myas3file.as></mx:Script>.
In myas3file.as, I include (thru include "variables.as";) file variables.as, which contains following code:
var timer:Object = new Object();
timer.t = 60;
or (in other test case)
var timer:Object = {t:60, j:"80"};
timer.t = 80;
Neither case works! Even if I rewrite example code from official documentation, it throws an 1020 error. I'm banging my head to the table for last two hours and I can't figure out what I'm doing wrong.
Thank you
If the code is included from a <Script /> tag in an MXML application, then what you're defining is a member variable and you can't use statements. From the docs:
You use the <mx:Script> tag to insert
an ActionScript block in an MXML file.
ActionScript blocks can contain
ActionScript functions and variable
declarations used in MXML
applications.
...
Statements and expressions are allowed only if they are wrapped in a function. In addition, you cannot define new classes or interfaces in blocks. Instead, you must place new classes or interfaces in separate AS files and import them.
Instead, you can use an initializer as in your second example:
private var name:Object = { field: 80 };
or you can do the initialization in a function (constructor, initialize/creation complete event handler).

Duplicate complex MXML binding in ActionScript

MXML lets you do some really quite powerful data binding such as:
<mx:Button id="myBtn" label="Buy an {itemName}" visible="{itemName!=null}"/>
I've found that the BindingUtils class can bind values to simple properties, but neither of the bindings above do this. Is it possible to do the same in AS3 code, or is Flex silently generating many lines of code from my MXML?
Can anyone duplicate the above in pure AS3, starting from:
var myBtn:Button = new Button();
myBtn.id="myBtn";
???
The way to do it is to use bindSetter. That is also how it is done behind the scenes when the MXML in your example is transformed to ActionScript before being compiled.
// assuming the itemName property is defined on this:
BindingUtils.bindSetter(itemNameChanged, this, ["itemName"]);
// ...
private function itemNameChanged( newValue : String ) : void {
myBtn.label = newValue;
myBtn.visible = newValue != null;
}
...except that the code generated by the MXML to ActionScript conversion is longer as it has to be more general. In this example it would likely have generated two functions, one for each binding expression.
You can also view the auto-generated code that flex makes when it compiles your mxml file, by adding a -keep argument to your compiler settings. You can find your settings by selecting your projects properties and looking at the "Flex Compiler" option, then under "Additional compiler arguments:" add "-keep" to what is already there.
Once done Flex will create a "generated" directory in your source folder and inside you'll find all teh temporary as files that were used during compilation.
I believe flex generates a small anonymous function to deal with this.
You could do similar using a ChangeWatcher. You could probably even make a new anonymous function in the changewatcher call.

Flex: does painless programmatic data binding exist?

I've only done a bit of Flex development thus far, but I've preferred the approach of creating controls programmatically over mxml files, because (and please, correct me if I'm wrong!) I've gathered that you can't have it both ways -- that is to say, have the class functionality in a separate ActionScript class file but have the contained elements declared in mxml.
There doesn't seem to be much of a difference productivity-wise, but doing data binding programmatically seems somewhat less than trivial. I took a look at how the mxml compiler transforms the data binding expressions. The result is a bunch of generated callbacks and a lot more lines than in the mxml representation. So here's the question: is there a way to do data binding programmatically that doesn't involve a world of hurt?
Don't be afraid of MXML. It's great for laying out views. If you write your own reusable components then writing them in ActionScript may sometimes give you a little more control, but for non-reusable views MXML is much better. It's more terse, bindings are extemely easy to set up, etc.
However, bindings in pure ActionScript need not be that much of a pain. It will never be as simple as in MXML where a lot of things are done for you, but it can be done with not too much effort.
What you have is BindingUtils and it's methods bindSetter and bindProperty. I almost always use the former, since I usually want to do some work, or call invalidateProperties when values change, I almost never just want to set a property.
What you need to know is that these two return an object of the type ChangeWatcher, if you want to remove the binding for some reason, you have to hold on to this object. This is what makes manual bindings in ActionScript a little less convenient than those in MXML.
Let's start with a simple example:
BindingUtils.bindSetter(nameChanged, selectedEmployee, "name");
This sets up a binding that will call the method nameChanged when the name property on the object in the variable selectedEmployee changes. The nameChanged method will recieve the new value of the name property as an argument, so it should look like this:
private function nameChanged( newName : String ) : void
The problem with this simple example is that once you have set up this binding it will fire each time the property of the specified object changes. The value of the variable selectedEmployee may change, but the binding is still set up for the object that the variable pointed to before.
There are two ways to solve this: either to keep the ChangeWatcher returned by BindingUtils.bindSetter around and call unwatch on it when you want to remove the binding (and then setting up a new binding instead), or bind to yourself. I'll show you the first option first, and then explain what I mean by binding to yourself.
The currentEmployee could be made into a getter/setter pair and implemented like this (only showing the setter):
public function set currentEmployee( employee : Employee ) : void {
if ( _currentEmployee != employee ) {
if ( _currentEmployee != null ) {
currentEmployeeNameCW.unwatch();
}
_currentEmployee = employee;
if ( _currentEmployee != null ) {
currentEmployeeNameCW = BindingUtils.bindSetter(currentEmployeeNameChanged, _currentEmployee, "name");
}
}
}
What happens is that when the currentEmployee property is set it looks to see if there was a previous value, and if so removes the binding for that object (currentEmployeeNameCW.unwatch()), then it sets the private variable, and unless the new value was null sets up a new binding for the name property. Most importantly it saves the ChangeWatcher returned by the binding call.
This is a basic binding pattern and I think it works fine. There is, however, a trick that can be used to make it a bit simpler. You can bind to yourself instead. Instead of setting up and removing bindings each time the currentEmployee property changes you can have the binding system do it for you. In your creationComplete handler (or constructor or at least some time early) you can set up a binding like so:
BindingUtils.bindSetter(currentEmployeeNameChanged, this, ["currentEmployee", "name"]);
This sets up a binding not only to the currentEmployee property on this, but also to the name property on this object. So anytime either changes the method currentEmployeeNameChanged will be called. There's no need to save the ChangeWatcher because the binding will never have to be removed.
The second solution works in many cases, but I've found that the first one is sometimes necessary, especially when working with bindings in non-view classes (since this has to be an event dispatcher and the currentEmployee has to be bindable for it to work).
It exists as of today. :)
I just released my ActionScript data binding project as open source: http://code.google.com/p/bindage-tools
BindageTools is an alternative to BindingUtils (see the play on words there?) that uses a fluent API where you declare your data bindings in a pipeline style:
Bind.fromProperty(person, "firstName")
.toProperty(firstNameInput, "text");
Two-way bindings:
Bind.twoWay(
Bind.fromProperty(person, "firstName"),
Bind.fromProperty(firstNameInput, "text"));
Explicit data conversion and validation:
Bind.twoWay(
Bind.fromProperty(person, "age")
.convert(valueToString()),
Bind.fromProperty(ageInput, "text")
.validate(isNumeric()) // (Hamcrest-as3 matcher)
.convert(toNumber()));
Etc. There are lots more examples on the site. There's lots of other features too-come have a look. --Matthew
Edit: updated APIs
One way to separate the MXML and ActionScript for a component into separate files is by doing something similar to the ASP.Net 1.x code behind model. In this model the declarative part (the MXML in this case) is a subclass of the imperative part (the ActionScript). So I might declare the code behind for a class like this:
package CustomComponents
{
import mx.containers.*;
import mx.controls.*;
import flash.events.Event;
public class MyCanvasCode extends Canvas
{
public var myLabel : Label;
protected function onInitialize(event : Event):void
{
MyLabel.text = "Lorem ipsum dolor sit amet, consectetuer adipiscing elit.";
}
}
}
...and the markup like this:
<?xml version="1.0" encoding="utf-8"?>
<MyCanvasCode xmlns="CustomComponents.*"
xmlns:mx="http://www.adobe.com/2006/mxml"
initialize="onInitialize(event)">
<mx:Label id="myLabel"/>
</MyCanvasCode>
As you can see from this example, a disadvatage of this approach is that you have to declare controls like myLabel in both files.
there is a way that I usually use to use mxml and action script together: All my mxml components inherit from a action script class where I add the more complex code. Then you can refer to event listeners implemented in this class in the mxml file.
Regards,
Ruth

Resources