Use StyleManager.setStyleDeclaration to set button skins in Flex - apache-flex

I am trying to load an swf file which has button skins as images (In the library of the swf file i have given export properties which is 'TickMark') and set the skin of a flex button using StyleManager.setStyleDeclaration.
I am getting errors like 'Argument count mismatch on TickMark(). Expected 2, got 0.'
This is what i am trying to do:
private function init():void
{
loader = new Loader();
loader.contentLoaderInfo.addEventListener(Event.COMPLETE,styleSWFLoaded);
loader.load(new URLRequest("styles.swf"),new LoaderContext(false,ApplicationDomain.currentDomain));
}
private function createStyle(styleName:String):void
{
var style:CSSStyleDeclaration = new CSSStyleDeclaration(styleName);
var cls:Class = ApplicationDomain.currentDomain.getDefinition(ss) as Class;
style.setStyle("upSkin",cls);
}
}
StyleManager.setStyleDeclaration(".buttonme",style,true);
}
When I apply this new style 'buttonme' to a button i am getting below error:
ArgumentError: Error #1063: Argument count mismatch on TickMark(). Expected 2, got 0.
Not sure why is this happening, and strange thing is, when I embed the same swf file it works, like below:
[Embed(source="styles.swf", symbol="Tick")]
private var GraphicClass:Class;
If I use the class GraphicClass in setStyleDeclaration, it works... but basically I want it dynamically.
Or there are other easy methods to skin (image) a flex button dynamically?

You should be able to set your skin like that dynamically. It probably has to do with your TickMark class. I'm assuming when you do style.setStyle("upSkin", cls);, that cls is TickMark and it has two required constructor args: TickMark(arg1:Object, arg2:Object). Is that true? Somewhere in the setStyle method its doing new cls().
If so, just make sure there's no constructor arguments and it should work.
If not, try following the stack trace and using breakpoints in Flex Builder if you don't already, that should help pinpoint the problem.
Best,
Lance

I believe, when you embed the export-symbol in your flex-app, it would be taking care of size and perhaps just embedding the png directly.
As your symbol-class extends BitmapData, it has to be instantiated by passing required arguments in constructor. So whatever error you get, is by design and work as expected.
You can wrap png in some other type of symbols (sprite, movieclip, etc) and export. That should work fine when used in setStyle (..,..)

Related

Flex: NPE and Proper way to access component properties before creation complete

I think I'm missing something important about flex sdk component lifecycle, but cannot sort it out, although read a lot of tutorials. Could you please share your experience on how do you operate with flex visual object's properties and how do you avoid NPE when accessing them before component creation complete.
Let's say we have a simple component MyTitleWindow.mxml:
<s:TitleWindow>
<s:DataGrid id="myDataGrid" />
</s:TitleWindow>
Another component got data from a remote object, and wants to apply the data into title window's datagrid and show it via PopUpManager:
private function handleDataReceived(data : ArrayCollection) : void {
var myTitleWindow : TitleWindow = new MyTitleWindow();
PopUpManager.addPopUp(myTitleWindow);
myTitleWindow.myDataGrid.dataProvider = data;
}
Ofcourse, the line myTitleWindow.myDataGrid.dataProvider = data will throw an NPE because we're trying to access myDataGrid that haven't been rendered yet.
Currently I can see only 2 options how to avoid NPE:
Create a setter for data in titleWindow, put the data into some
cache. Listen to creationComplete event, in it's handler apply data
from the cache to the datagrid. This approach works fine, but I'm
tired of adding this safe-guards across the application.
Make a
bindable property - works for me only with simple datatypes
(numbers, strings...)
Is there anything I'm missing in the area of using flex validation/invalidation cycle that could help to avoid excessive code?
The problem you are having and Crusader is reporting is because in Flex components are initialized lazy. This is generally a good thing but in your case it is what's causing your problems.
In general I wouldn't suggest to set the dataProvider on a view component from outside the component as you have no way of knowing if all is setup and ready to use.
What I usually do. In simple cases I simply add a public propberty which I make [Bindable]. The (i think) cleaner way would be to create a setter (and getter) and so save the dataProvider in a local variable (in your case probably ArrayCollection). In the setter I usually check if the "myDataGrid" exists and if it exists, to additionally set the dataProvider property. I would then add a CreationComplete callback in my component and in that I would also set the dataProvider.
So when setting the dataProvider before the component is finished initializing, the value would simply be saved in the local variable and as soon as it is finished setting up the dataProvider is automatically set. If the component is allready setup (you are changing the dataProvider) the setter would automatically update the dataProvider of "myDataGrid"
<s:TitleWindow creationComplete="onCreationComplete(event)">
...
private var myDataProvider:ArrayCollection;
private function onCreationComplete(event:FlexEvent):void {
myDataGrid.dataProvider = myDataProvider;
}
public function set myDdataProvider(myDataProvider:ArrayCollection):void {
myDataProvider = myDataProvider;
// Only update the dataProvider if the grid is available.
if(myDataGrid) {
myDataGrid.dataProvider = myDataProvider;
}
}
....
<s:DataGrid id="myDataGrid" />
</s:TitleWindow>
Yeah, this can be an annoyance, and it's not just with popups. It can also be a pain when using a ViewStack components with a default creationpolicy, which I tend to do fairly often.
I might get flamed for this, but I usually just use bindings. I'm not sure what you mean by "simple datatypes" - it works fine with custom types too. You'd have to provide an example.
One thing you could do (and I'll probably get flamed for this :p ) is create your popup component instance early on and re-use it rather than creating a new one each time.
No, I don't think you're "missing something" in the component lifecycle.
I always try to invert the responsibility for setting a dataProvider and prefer to have components observe a [Bindable] collection.
In simple examples such as yours I avoid giving my components an id. This prevents me from breaking encapsulation by referring to them externally.
<s:TitleWindow>
<s:DataGrid dataProvider="{data}" />
</s:TitleWindow>
The consumers of your MyTitleWindow component should not know that it has a DataGrid with id 'myDataGrid', or be required to set the dataProvider property of 'myDataGrid'.
Considering components declared in MXML require a no-arguement constructor (and that we're unable to declare multiple constructors) - one approach that has worked well for me in the past is to offer a static 'newInstance' method. I give this method a relevant name based on the domain I'm working in, and also any required parameters.
public static function withData(data : ArrayCollection) : MyTitleWindow
{
var myTitleWindow : MyTitleWindow = new MyTitleWindow();
myTitleWindow.data = data;
return myTitleWindow;
}
This clearly communicates the 'contract' of my component to any and all consumers. (Obviously things become clearer with more relevant naming).
private function handleDataReceived(data : ArrayCollection) : void
{
PopUpManager.addPopUp(MyTitleWindow.withData(data));
}

Disable databinding in MXML

When the Flex SDK converts MXML to actionscript it generates a lot of databinding code. Sometimes, however, I don't want to bind a variable, for example if I know the variable will not change.
I can't seem to find a work around in Flex to disable the autogenerated databinding.
Also, I was hoping this might also help with some of the runtime warnings thrown by databinding.
To get around them, I sometimes use the following, which only throws syntax warnings (and don't appear in my console at runtime).
Syntax warning:
Data binding will not be able to detect changes when using square bracket operator. For Array, please use ArrayCollection.getItemAt() instead.
The following tag will tell Flex SDK that variable do not really change and remove "Unable to bind ..." warnings:
[Bindable("__NoChangeEvent__")]
private var model:MyModel = MyModel.instance;
Next, move array[i]-like expressions to a separate function in order to remove warnings. If you had this:
<mx:Button label="{array[i]}"/>
Then create a function:
private function buttonLabel(i):String
{
return array[i];
}
And the MXML:
<mx:Button label="{buttonLabel(i)}"/>
P.S: If button label changes in runtime then you can add [Bindable(...)] metatags to the function:
[Bindable("stringsChange")]
private function buttonLabel(i):String
{
return array[i];
}
dispatchEvent(new Event("stringsChange"));

Editing A Library Symbol From ActionScript

In the Flash authoring environment I can edit a library symbol and all on-stage instances based upon it reflect the changes. How can I do the same thing in ActionScript? There seems to be no way to address a library symbol.
For example:
Inside Flash CS3, I have created a Square.swf file that has 100 instances of the library symbol Square.
Now, Square.swf is loaded into another file BlueSquare.swf and I want to change the Square symbol into a blue square so that all instances of Square become blue.
How do I do this using Actionscript?
Thanks for the help.
What's in a clip's library symbol is the author-time definition of that object - you can't change it at runtime. Instead the normal approach would be to dynamically change the contents (not definitions) of the clips you want to change, which can be done in various ways, but all the good ways of doing that involve making the dynamically-changing clip understand how to update its appearance. So you need to be able to re-author the changing clips to suit your needs.
If you're loading in an animation that somebody else made, and trying to go through and replace all instances of object A with object B, the only way to achieve that is to traverse through the content's display list looking for A, and when you find one, remove its children and replace them with the the contents of a B. Mind you, for animations that may not really solve your problem, since animations normally add and remove clips frequently, so at any given point you could replace all the "hand" clips with "hand2", but then a frame later new "hand" clips might come into existence. But short of opening up the SWF and changing the binary data inside, there's no other way to dynamically change all of a given object to something else unless the object knows how to change its contents.
If it is only about making sure that the square you are attaching is blue you could use the colorTransform to change its appearance:
var someSquare:Square = new Square();
someSquare.transform.colorTransform = new ColorTransform(0,0,0,1,0x00,0x00,0xff,0x00 );
addChild( someSquare );
Of course this does not change the color of all instances that you have already attached.
If you really wanted to change the actual SWF symbol in Actionscript the only way I see is to parse the swf with as3swf ( https://github.com/claus/as3swf/wiki ), find the shape tag of the symbol, change it and then load the ByteArray that contains the swf via loader.loadBytes() - but that's admittedly quite a complicated way and you can achieve the same result by simply putting some colorizing code into the shape symbol itself and then trigger the color change via an Event that is broadcasted by your main app.
Of course, if you make custom component, when you change it changes will appear on all instances of that component/class. Here's the example: http://livedocs.adobe.com/flex/3/html/intro_3.html
On the other hand, if you use modules whey pretty much do the same as swf-s you used in Flash, when you rebuild-recompile them changes will reflect on your main application which uses them. Here's th eexample for modules: http://blog.flexexamples.com/2007/08/06/building-a-simple-flex-module/
So MXML/AS component/class are your "symbols" which you can create or drop on stage on fly.
Modules are "movies" you can load and they run on their own with possibility to communicate to main movie.
The closest way of achieving this is to use Bitmaps. If you update the bitmapData they display, they will all update automatically.
However this approach is not good at all. You should maintain application state separately in an object model, and have the visualisation update, if the state changes.
What you want to do, is to misuse a feature for changing graphic appearence at design time, to change application state at runtime. In generally, ideas like these can be thought off as bad.
For example if you take the time to separate the state model and the visualisation layer, it will become fairly easy to save the game state on a server or to synchronize it with other clients to achieve multiuser features.
greetz
back2dos
If you are trying to build an Avatar and user can customize your Avatar parts e.g. hands, legs, face etc. and you want all these assets to be kept in separate swf file, that is pretty straightforward. You keep all the assets, in separate swf or one large swf file and load them at runtime. Now, maintain your Avatar object instance and place the child objects, which are chosen by the user.
You can create inside your class a static List with references all the created instances and then apply a change with static methods. For example:
package
{
import flash.display.MovieClip;
import flash.geom.ColorTransform;
public class Square extends MovieClip
{
public static var instances:Array = new Array();
public function Square():void
{
Square.instances.push(this); // This is the trick. Every time a square is created, it's inserted in the static list.
}
// This property gets the color of the current object (that will be the same of all others because the setter defined below).
public function get color():ColorTransform
{
return this.transform.colorTransform;
}
public function set color(arg:ColorTransform):void
{
// Sets the color transform of all Square instances created.
for each(var sqr:Square in Square.instances)
{
sqr.transform.colorTransform = arg;
}
}
}
}

FLEX: how can I get the width of my linkButton object?

How can I get the width of my LinkButton object ?
myLinkButton = new LinkButton();
myLinkButton.label = "blabla";
myLinkButton.setStyle("fontSize", 24);
myContainer.addChild(myLinkButton);
trace (myContainer.width); //this doesn't work because I haven't directly set the attribute
thanks
First, what does that trace() show? Is it null or undefined or NaN or simply a wrong value?
Then, there are several ways I can think of how you could get around this problem:
Try using getBounds() or getRect(). These methods return a Rectangle object working as the DisplayObject's bounding box (including all coordinates and dimensions). Sometimes Flex behaves a bit weird and returns wrong/off results for the coordinates or dimensions of objects.
Try experimenting with validateSize() and/or measuredWidth. Perhaps you're trying to access the width property too soon so that Flex cannot do the measuring/layouting in time.
Similar idea: what happens if you use myContainer.callLater(trace, [myContainer.width]); (assuming your myContainer inherits from UIComponent)? If you do get a valid result using callLater() but not when accessing width directly then Flex just hasn't had a chance to layout and update the container.
You could also try using this method, which creates a Bitmap from the object and returns the Bitmap's height/width. This is especially useful if you have components with visible = false in your container, because Flex doesn't handle invisible components well in that regard.
Finally, you could try accessing $width in the mx_internal namespace and check that property's value. However, using mx_internal is sort of a very ugly hack because these properties and methods weren't meant for external use and are subject to change any time (so your component could stop working when a new version is released) - so use with caution.

Sharing code between swfs

Lets say I have two swfs A and B, and at runtime, swf A loads swf B, and I wish to share code between them, to minimize file size and download times.
If swf B has some code (say. com.blah.HelloWorld), I tell the compiler to have swf B's source in swf A's classpath, but only do a compile-time link and not actually compile com.blah.HelloWorld into swf A.
This works, and I have tried it, using a the -includes and -externs compiler options.
However, My problem is that I wish to do this the other way. i.e. swf A and B (and potentially swf C) all need com.blah.HelloWorld, but I want com.blah.HelloWorld to be compiled into just swf A, have it as an external reference in swf B ( and potentially C as well.)
I tried doing this using the externs and includes, but I get ReferenceErrors when I do this.
I want to do this without a having a separate rsl, so I can reduce the number of http requests.
Is this possible?
You can split your flex application into modules.
Or you can access individual classes from an SWF loaded at runtime using the getDefinition method of the ApplicationDomain class:
var loader:Loader = new Loader();
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, onLoad);
loader.load(new URLRequest("c.swf"));
//..
private function onLoad(e:Event):void
{
var domain:ApplicationDomain = LoaderInfo(e.target).applicationDomain;
var Type:Class = domain.getDefinition("pack.MyComponent") as Class;
var myBox:Sprite = new Type();
addChild(myBox);
}
I'm not sure I'm quite understanding your question, but I think you can use the AS3 Loader class to do what you want. The format would be something like this - let's say we're creating your main app (which will be called "a.swf") and you want to access methods and properties that have been compiled into another app (called "b.swf"), you'd do this:
var SWFB:Object; // empty at first as a placeholder.
var url:URLRequest = new URLRequest("b.swf");
var l:Loader = new Loader();
l.contentLoaderInfo.addEventListener(Event.COMPLETE, loaded);
l.load(url);
function loaded(e:Event):void {
SWFB = e.currentTarget.content as Object;
initApp();
}
function initApp():void {
SWFB.someMethodCall();
}
... and I THINK that'll work. I can't test it right now, but try it and let me know. Basically you're going to be loading b.swf in as a basic object, and you can then call methods against that object.
Please let me know if that worked, and if not I can refine it for you tomorrow.
I'm not sure if this is exactly what you want, but you could use some javascript to allow the two swfs to work together.
Here is a tutorial:
http://greeneggsandcam.com/tutorials/connect-2-swfs

Resources