How to send to the skin some value which have changed?
There are two approaches to this: one uses binding and is easier, the other is more complex but better for performance.
Using binding
Suppose your view class looks like this:
public class MyClass extends SkinnableComponent {
[Bindable] public var myValue:String;
}
then you can bind to that value in your skin like this:
<s:Skin xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark">
<fx:Metadata>
[HostComponent("MyClass")]
</fx:Metadata>
<s:Label id="myLabel" text="{hostComponent.myValue}" />
</s:Skin>
Overriding commitProperties
Assume the same skin without the binding, then you can set the label's text property by using a skinpart and overriding commitProperties in the host component. The skinpart's name must be exactly the same as the id of the component in the skin (in this case 'myLabel').
public class MyClass extends SkinnableComponent {
[SkinPart(required="true")]
public var myLabel:Label;
public var myValue:String;
override protected function commitProperties():void {
if (myLabel) myLabel.text = myValue;
super.commitProperties();
}
}
Of course you would have to call invalidateProperties() whenever you want to apply the new value (for instance in a setter function for 'myLabel'). Also notice that 'myLabel' no longer needs to be bindable, unless you would want to be able to bind on it externally.
edit: which approach to choose?
I have just answered a question that is closely related to this one in which I elaborate on the pro's and cons of each approach in different situations. You can find it here: Flex: Additional label in ButtonBarButton
Related
When I extend a class I want to override methods and change their accessibility like a protected method should be public in a certain class. When I compile it says: "Incompatible override". I can't reduce nor can I increase the visibility.
Reducing wouldn't make sense and I don't need it but I was able to increase method visibility in Java. Why not in ActionScript 3 ?
public class OldClass
{
protected function doStuff() : void
{}
}
public class NewClass extends OldClass
{
override public function doStuff() : void
{}
}
Am I doing something wrong ?
No, you're not doing anything wrong. That's just how the language works. From the documentation (emphasis added):
Static methods are not inherited and cannot be overridden. Instance methods, however, are inherited by subclasses and can be overridden as long as the following two criteria are met:
...
The override method must have the same level of access control as the base class method. Methods marked as internal have the same level of access control as methods that have no access control specifier.
The override method must have the same number of parameters as the base class method.
The override method parameters must have the same data type annotations as the parameters in the base class method.
The override method must have the same return type as the base class method.
I'm new to Flex and I'm missing something very basic about architecting a Flex application -- I've implemented a custom component MyReportGrid and extended GridColumn with MyColumn. I've tried to outline the application code below.
The interesting thing to notice is that class OfficeItems is used by class MyItems, and the MyColumn (which extends GridColumn) includes a customPath variable that is used to access variables pen, pencil, and stapler inside class OfficeItems.
I believe that is the reason why manually sorting (by clicking on the columns in the datagrid) only works for columns corresponding to zipCode and stateCode and NOT pen, pencil, and stapler.
Also, when I try to use a simple labelFunction for zipCode or stateCode it always works fine, but implementing a labelFunction for pen, pencil, or stapler never works. By "never works" I mean that the labelFunction is called correctly, and it performs it's required task in that the labelFunction receives the correct object and actually returns the correct formatted String, but this returned value is never displayed in the datagrid (I'm assuming the customPath variable confuses the labelFunction as to which variable the returned String maps to).
I think these are both the same issue in that the customPath aspect of the MyColumn confuses the application. Any idea how to fix the sorting and/or labelFunction? Thanks in advance for your patience in reading this (long) posting.
The classes are:
package com.supportClasses
{
public class OfficeItems {
public var pen:*;
public var pencil:*;
public var stapler:*;
}
}
and
package com.models
{
import com.supportClasses.OfficeItems;
[Bindable]
public class MyItems extends Model {
public function myItems() {
super();
}
public var office:OfficeItems;
public var zipCode:int;
public var stateCode:String;
}
}
The data grid looks like:
...
<components:MyReportGrid id="myGrid" dataProvider="{_myData}"...>
<components:columns>
<fx:Array>
<supportClasses:MyColumn customPath="office" dataField="pen"... />
<supportClasses:MyColumn customPath="office" dataField="pencil"... />
<supportClasses:MyColumn customPath="office" dataField="stapler"... />
<supportClasses:MyColumn dataField="zipCode"... />
<supportClasses:MyColumn dataField="stateCode"... />
...
where _myData has a class of MyItems (note: the customPath feature is ignored by MyColumn when not present here, such as for zipCode and stateCode). MyColumn is:
package com.components.supportClasses {
import spark.components.gridClasses.GridColumn;
public class MyColumn extends GridColumn
{
public var customPath:String="";
...
public function MyColumn(headerText:String="header" customPath:String="", dataField:String="data", ...) {
this.headerText=headerText;
this.customPath=customPath;
this.dataField=dataField;
...
}
}
}
and MyReportGrid is:
package com.models {
<?xml version="1.0" encoding="utf-8"?>
<s:Group xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
width="400" height="300">
import com.components.myClasses.MyColumn;
import com.itemRenderers.myItemRenderer;
import mx.collections.ArrayCollection;
import mx.collections.ArrayList;
import mx.collections.ListCollectionView;
import spark.components.gridClasses.GridColumn;
...
<s:DataGrid width="100%" ... />
</s:Group>
}
the labelFunction is:
private function redFormat(item:Object, column:MyColumn):String {
var formatResult:String = "red "+item.office.pen;
return formatResult; // returns "red Bic15938" (for example)
}
as called from:
<supportClasses:MyColumn customPath="office" dataField="pen" labelFunction="redFormat"... />
This is just a guess, but could it be something like adding a "toString" function to the OfficeItems class? (I would be using comments, but still working on getting reputation.)
As the title says, is there a way to alter the ResourceManager's getStringArray() in a way that it splits the resources by semicolon, not comma?
The actual method can be found in the ResourceManagerImpl class, which can be found in in the package mx.resources.
Overriding that method would be fine, but ideally I'd like to write my own getStringArray with a variable separator, however, there seems to be no way of extending either the ResourceManager or ResourceManagerImpl class to somehow add that method.
Anyone got a clue what to do here?
The problem is not that you can't extend ResourceManagerImpl since it's not final, but rather that you have to be able to register your implementation with the application instead of the default one. And doing this is a bit tricky.
So first create your implementation:
public class MyResourceManager extends ResourceManagerImpl {
private static var instance:IResourceManager;
static public function getInstance():IResourceManager
{
if (!instance) instance = new MyResourceManager();
return instance;
}
override public function getStringArray(bundleName:String,
resourceName:String,
locale:String = null):Array {
//do your stuff
}
}
So we've overriden the getStringArray method. Notice that we've done the same for getInstance, because we want it to return a new instance of MyResourceManager instead of ResourceManagerImpl (we don't have to mark override because it's a static method). Also, you may have to write some import statements manually, because some of the classes you're using are marked as 'excluded'.
Now we have to tell Flex to use MyResourceManager instead of ResourceManagerImpl. We can do this with the following code:
import mx.core.Singleton;
Singleton.registerClass("mx.resources::IResourceManager", MyResourceManager);
The problem is that we have to do this before Flex registers ResourceManagerImpl, because you can't override it once it's registered. For this we need to create a custom preloader in which we do the registering (sadly, the Application's 'preinitialize' phase is not early enough).
public class RegisteringPreloader extends DownloadProgressBar {
override public function initialize():void {
super.initialize();
Singleton.registerClass("mx.resources::IResourceManager",
MyResourceManager);
}
}
Now assign the custom preloader to the application and we're done:
<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"
preloader="RegisteringPreloader" >
For further info I refer you to a fairly similar, but somewhat more elaborate answer that I wrote for a different question: Is there a way to listen for events on the pop up manager class?
Just for the record: if you want to provide your localization with array of strings containing commas, it is way easier to use getObject method of IResourceManager.
In your properties file:
my.beloved.strings: ["That's it, string one", "Okay, string two"]
In your code:
var strings:Array = _resourceManager.getObject(_bundleId, 'my.beloved.strings') as Array;
var stringOne:String = strings[0];
You don't have to override anything this way.
I am trying to create a spark datagrid item renderer. This item renderer extends a checkbox, and implements IGridItemRenderer
public class CellCheckBoxItemRenderer extends CheckBox implements IGridItemRenderer
When I implement IGridItemRenderer, I need to implement the interface methods, I am having a problem with the following methods:
public function get hovered():Boolean
{
}
public function set hovered(value:Boolean):void
{
}
since the methods are inherited as well from the checkbox
EDIT
The signatures of the functions
//spark checkbox signature
protected function get hovered():Boolean
protected function set hovered(value:Boolean):void
and the signature above belongs to the interface IGridItemRenderer
I guess the implementation of IGridItemRenderer is the more important part, so you can use it in a datagrid. The CheckBox provides just the functionality, you don't have to extend it if there are conflicts in my opinion.
public class CellCheckBoxItemRenderer implements IGridItemRenderer {
private var checkBox:CheckBox;
public function getCheckBox {
return checkBox;
}
//...
}
If CheckBox would implement any useful interfaces, you could also implement them in your renderer and delegate the methods to the checkbox, which may let you encapsulate the whole checkbox. That's not the case here though.
The problem is that interfaces, by design, only specify the signature for public functions, whereas the function in Checkbox is set as protected.
The only solutions:
remove the interface/Checkbox class from CellCheckBoxItemRenderer
remove the declaration from the interface
change Checkbox so hovered is a public property
it might be possible to change the accessor dynamically using the as3 commons bytecode project (http://www.as3commons.org/as3-commons-bytecode/emit.html), but I'm not 100% sure.
this is the code, i am using flex builder 4.5,
<?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:Declarations>
<!-- Place non-visual elements (e.g., services, value objects) here -->
</fx:Declarations>
<fx:Script>
<![CDATA[
public var ss:Array = ["a", "b", "c"];
trace(ss[0]);
public var str:String = "Good Luck";
trace(str);
]]>
</fx:Script>
</s:Application>
i get red cross next to trace statment and fault is
"1120:Access of defined property ss"
i have tried the commented line as well but no luck. i have tried with 3.5, 4.1 and 4.5 sdk.
Am i missing something? please guide!!
(i tried googling it but nothing came up )
thanks in advance.
(updated the code)
public var ss:String is a field declaration and trace(ss) is an action in code execution flow. Place trace(ss) in an appropriate scope (in a function aka method) and it will be compiled and executed without any problem.
I think you are getting confused between Class member properties and local variables. Inside a method, you can only declare local variables, for example:
public function debugString() : void {
// declare a local property, only this, 'debugString' function can acess
// or modify this property - it is deleted when this function finishes.
var myString : String = "Hello World";
trace(myString);
}
However, it would appear that you were trying to define Class member properties instead (because you were declaring the visibility of the property (ie: public)).
public class HelloWorld {
// Define a Class member property; all functions in this Class will be
// able to access, and modify it. This property will live for as long as the
// Class is still referenced.
public var myString : String = "Hello World";
public function debugString() : void {
// notice how we did not declare the 'myString' variable inside
// this function.
trace(myString);
}
}
Note that you can only access member properties once a Class has been constructed; so the earliest you can (sensibly) access them is in your Constructor, for example:
class HelloWorld {
public var myString : String = "Hello World";
// This will not work, because you can only access static properties
// outside of a function body. This makes sense because a member property
// belongs to each instance of a given Class, and you have not constructed
// that instance yet.
trace(myString);
// This is the Class Constructor, it will be called automatically when a
// new instance of the HelloWorld class is created.
public function HelloWorld() {
trace(myString);
}
}
What you might be trying to do is make use of a static property; these differ from Class member properties as they are Globally shared amongst all instances of a given class. By convention, static properties are defined in CAPS:
public class HelloWorld {
// Here we define a static property; this is accessible straight away and you
// don't even need to create a new instance of the host class in order to
// access it, for example, you can call HelloWorld.MY_STRING from anywhere in your
// application's codebase - ie: trace(HelloWorld.MY_STRING)
public static var MY_STRING : String = "Hello World";
}