Watching an ArrayCollection's length property in AS3/Flex - apache-flex

I would like to put an eventListener on an ArrayCollection's length proprety, but I don't know how to go about this.
I want to do this because I only want code to execute when a certain number of things are in a certain ArrayCollection. I want Flex to wait to execute this code over the next object until that length property drops back to an acceptable level. I think I should do this with events instead of a while loop that sits there spouting NOOPs forever (which I don't know how to do either).
Please help, and thanks in advance. SO has been a great help so far.

ArrayCollection will dispatch a "collectionChange" event when its items changed. So you can listen to that event and check the "length" property each time the event is dispatched. Alternatively you can also just bind to the length property via BindingUtils.bindSetter();

You could just add a 'collectionChange' event listener and check if the ArrayCollection.length has changed since the previous event. (You'd need to keep a static or instance copy of the ArrayCollection's length, for comparison.)
The other thought is to create your own lengthChangedEvent, extend ArrayCollection, override the length property and look for then call any lengthChangedEvent listeners in the setter. Then use your extended ArrayCollection in place of the base class wherever you need to listen for the event. This won't work though because the length property is read-only and there is no setter to override. You would have to override every method that could add or remove items from the ArrayCollection and test/call your lengthChangedEvent listener in each case.

Related

JavaFX binding in initialize method

I am trying to create a binding to provide a button a way to disable/enable itself whenever a listview contains items or not. However, I don't get it to work.
This is my initialize method
private void initialize(){
runButton.disableProperty().bind(Bindings.isEmpty(listView.getItems());
}
Adding items to the listView has no effects whatsoever on the disabled/enabled state of the button.
If I put the above line of code in another random function that is called later on in the program, it works.
I've read that the bindings might be garbage collected so I've also tried to create a field of the binding to then apply to the disabledProperty. This does not work either.
Any ideas?
EDIT:
listView.setItems(observableFiles)
is used to populate the list
Do you call listView.setItems() anywhere in the code? If so that will explain why it doesn't work when you put it in initialize because you're binding to the old list object whereas the ListView is using the new list object.
Edit: Just seen your edit, it seems you really did this. Try calling the bindings after you call setItems.
As another alternative you could add listener to ListView itemsProperty and if the changed list is empty trigger disable on your runButton.

Easy way to tell if data is dirty

I am currently using databinding on a group of spark form elements and want to know if the data class I am bound to is dirty.
It might be nice if spark "Form" elements would trigger a change event that bubbled up to and was caught by a spark "Form". Then I could just add an event listener to the form. But I don't see something like that.
So besides comparing a copy of the original data class to the bound data class OR adding a change event to each form element and capturing that event, what's an easy way of knowing a class object has changed?
Make an extension of TextInput or whatever other input elements you'll have in your form then in your components creation complete register a listener for the change event, in the handler dispatch a bubbling event, then in your document that contains the form use your custom elements and add a listener via AS3 to the Form for the event you dispatched and stop it's propagation at that point.
Otherwise I think your second solution of change handler on each wouldn't be too bad. If you have a lot of elements in the form or it's dynamic you could write a function that steps through the form items of the form and checks their children then you just have to write a switch that deals with each class type (you can use http://www.actionscriptdeveloper.co.uk/getting-the-class-of-an-object-in-as3/on each of the child elements to determine what type it is, then if it's a TextInput or whatever register the appropriate event to call your commonly used function, just be sure it has a generic Event as it's parameter since all other Events will be sub-classes thereof).
So far as I know there is no easy way to listen for changes to the underlying data from the ArrayCollection or other wrapper ListCollectionView data structures. Part of the problem is the elements added a collection aren't required to implement any sort of interface to allow for listeners to be registered. That is, the data elements aren't necessarily event dispatchers, the only other way for this information to be communicated to the List would be if the data elements had a handle on all lists that contain that element and they mark something on the lists to indicate "dirty" when any property is set. These are all achievable within the constraints of the language but aren't provided out of the box as the usage for them is probably limited and could potentially unnecessarily bloat the cpu usage of the ListCollectionView in other cases.

How Do I See All Events Fired by an Object?

I am having trouble wiring up an event listener to a fl.transitions.Transition.
Is it possible to somehow view all the events an object fires? That way I could check I am using the correct event (and possible view better ones to use).
The easiest is to override the dispatchEvent method in classes where you want to intercept events.
You can find the classes in %CS_ROOT%\Common\First Run\Classes\mx\transitions\easing\.
You can also create a subclass of EventDispatcher with a custom dispatchEvent-implementation and use that as a subclass for all classes where you'll be wanting to intercept events.
greetz
back2dos
The problem is, you have to have an event type to listen for. The only way to do this is to add listeners for all the possible events.
Now, you can add a handler with an indeterminate event type, such as:
private function myUniversalHandler(event:*) : void {
trace(event.type);
trace(event.constructor.toString());
}
And this will report any event passed to it. Nevertheless, it simply won't be called unless it is listening for an event of a particular type. And adding all those listeners is a lot of work to go through. Better to study the events available to you from whatever class you are dispatching the vent from.
I would look at TransitionManager, and the events allTransitionsOutDone and allTransitionsInDone which it dispatches. I haven't used these, but my understanding of their function matches what you seem to be looking for.
checkout the online reference, you should see there all events (and inherited events) of a Class.
On a side note if you are using flex you might be using mx.states.Transition
There is no way, at runtime, to find out all the events that fire from a component. You'll have to explore the component source code to get a complete list.
Reviewing the ASDocs, as others have suggested, is a good way to get a handle on the documented events of a component; and in most cases you'll be able to find one to suit your needs.
You cannot programmatically get a list of all events fired by any given object. You can however get a list of all events fired by a standard library object (that are part of it's public interface) from its documentation (cilck on the show inherited events link) and decide whether you're using the appropriate one.
I have scratched my head quite a bit on this issue as well.
The answer is this
you can get a list of all event listeners that an object is listening to only if attached though MXML
if not attached through MXML you cannot see the events an object is listening to (attached by AS)
if you want to see all the events an object is listening to you can check for hasEventListener although this is a long coding way
another efficient way (if you can use it, I couldn't) is to monkey patch the framework and create a dictionary of listeners for every object.
you can accomplish that by patching FlexSprite and overriding the addEventListener function.
you should keep in mind, this will not work when you are loading the framework through RSL.

Can I get changeWatcher to fire when XML is appended to?

So I had a requirement to add an undo function to my app and I discovered this nifty ChangeWatcher class, but it's not doing exactly what I want and I'm hoping someone knows how.
ChangeWatcher seems to monitor assignments to the variable reference. So when I say:
myXML = <xml/>
it fires off it's function just fine and dandy, but when I say:
myXML.appendChild(thisOtherXMLVar)
I get nothing, even though the variable changes the reference doesn't so ChangeWatcher doesn't fire off the function.
So does anyone know how to get ChangeWatcher to pick up on all changes to a variable?
ChangeWatcher is a really cool class. But it's not going to work unless the property you're trying to watch change is [Bindable].
If you're myXML variable is Bindable, then when you set it to the XML value, it will dispatch an event (default is propertyChange). ChangeWatcher adds an event listener to the object for whatever [Bindable] events have been declared. It does this by getting the Bindable metadata for that variable.
But the XML class itself doesn't have anything Bindable, so calling appendChild won't dispatch any events and ChangeWatcher wont work.
Updating based on your comment, you can just make all of your changes to the XML and then reset the variable. That will then cause the ChangeWatcher to register the update.
Hope that helps,
Lance

Can an Actionscript component listen to its own propertyChange events?

I have a CircleButton class in Actionscript.
I want to know when someone externally has changed the 'on' property.
I try listening to 'onChange' but it never hits that event handler.
I know I can write the 'on' property as a get/setter but I like the simplicity of just using [Bindable]
Can an object not listen to its own events?
public class CircleButton extends UIComponent
{
[Bindable]
public var on:Boolean;
public function CircleButton()
{
this.width = 20;
this.height = 20;
graphics.beginFill(0xff6600, 1);
graphics.drawCircle(width/2, height/2, width/2);
graphics.endFill();
this.addEventListener(MouseEvent.ROLL_OVER, rollover);
this.addEventListener(MouseEvent.ROLL_OUT, rollout);
this.addEventListener('onChange', onOnChange);
}
private function onOnChange(event:PropertyChangeEvent):void {
If you use the [Bindable] tag without specifying an event type, then when the property changes its value, an event of type: PropertyChangeEvent.PROPERTY_CHANGE, which is the string 'propertyChange', will be dispatched.
Therefore, to be able to register to listen to that event, you need to say:
this.addEventListener(PropertyChangeEvent.PROPERTY_CHANGE, onOnChange);
The reason why your listener function was never called is that the event type was not correct.
Note that the listener method will be called when any of the variables marked as Bindable in your class changes, not only 'on'. This event comes with a property called 'property' that indicates which variable was changed.
To avoid being called on each Bindable variable, you need to specify an event in the [Bindable] tag:
[Bindable(event="myOnChangeEvent")]
and dispatch that event manually when you consider that the property is changing (ie: in the setter), though that didn't seem to be what you wanted to do.
You could use BindingUtils.bindSetter()
An example is found here.
Just because it is possible for something to bind to the variable, doesn't mean something is bound to the variable. It's a bit like the event system - just because something can dispatch an event doesn't mean anything is listening.
The Classes around which the Flex binding is based are BindingUtils and ChangeWatcher. When you bind directly in MXML (which just gets converted to AS3 by the compiler) it uses these classes behind the scene to actually establish the binding. I've dug around in ChangeWatcher before and when it looks through the list of potentially bindable items it only dispatches if some object is actually listed as a listener. The whole binding system is a smart wrapper around the event system actually.
The semantics of binding in AS3 instead of MXML are different. Subtle differences (like chaining to child properties of Objects) that just work in MXML require work in AS3 to duplicate the behaviour (probably a result of the code generation between MXML to AS3).
Have a look at this Adobe doc for a little info on ChangeWatcher in AS code.
Personally - I do not use binding outside of MXML since I feel it is clumsy. I would suggest you write a setter function instead since it is more predictable (and very likely performant). I also suggest you read through the source code for ChangeWatcher and BindingUtils. That is definitely some of the most advanced AS3 you are likely to read.
One of my favorite approaches is the Observe class which is found here. It is essentially using a setter but it is a nice repeatable approach.

Resources