1
votes

I've created a component which is basically a meter (extends skinnable container). The skin to the component contains two rectangles (background and the actual meter). This component receives an arraycollection with the following fields value, max and min(the arraycollection I am passing is Bindable). When I initialize and pass the data to be used to draw It works great (keep in mind - only works the first time). I've created a button that changes the data array and shows the current meter value. Apparently the meter's value is changing everytime I modify the ArrayCollection I've set to be used for rendering.(dont want to say "dataProvider" because it is not a Flex dataprovider, just a variable )... here is the code...

    public class meter extends SkinnableContainer {


    [SkinPart( required = "true" )]
    public var meter:spark.primitives.Rect;

    [SkinPart( required = "true" )]
    public var meterBackground:spark.primitives.Rect;

    private var _dataProvider:ArrayCollection;
    private var _renderDirty:Boolean = false;

    public function Meter() {
        super();
    }

    public function set dataProvider( value:Object ):void {
        if ( value )
        {
            if(value is ArrayCollection)
               {
                   _renderDirty = true;
                _dataProvider = value as ArrayCollection;
               }                
            if(_dataProvider)
            {
                _dataProvider.addEventListener(PropertyChangeEvent.PROPERTY_CHANGE, propertyChanged);//used both eventlisteners but none actually ever fire off
                _dataProvider.addEventListener(CollectionEvent.COLLECTION_CHANGE,collectionChanged);
            }
            invalidateDisplayList();//only happens the first time
        }
    }

    private function collectionChanged(event:CollectionEvent):void
    {
        Alert.show("In collection Change");//this never goes off when I change the "dataProvider"
        _renderDirty = true;
        invalidateDisplayList();
    }

    private function propertyChanged(event:PropertyChangeEvent):void
    {
        Alert.show("In property Change");//this never goes off when I change the "dataProvider"
        _renderDirty=true;
         invalidateDisplayList();
    }

    public function get dataProvider():Object {
        return _dataProvider;
    }        

    override protected function updateDisplayList( unscaledWidth:Number, unscaledHeight:Number ):void {


        if(_dataProvider))
        {
        var span:Number = unscaledWidth / _dataProvider[0].max;
        var meterWidth:Number = _dataProvider[0].value * span;

        meter.width = meterWidth;
        _renderDirty = false;
        }

    }

And this is the mxml code where I change the "value" field....

   <fx:Script>
    <![CDATA[
    [Bindable]
        private var meterData:ArrayCollection = new ArrayCollection([                
            {value:80, max:100, min:0}
        ]);

        protected function mySlider_changeHandler(event:Event):void
        {
            meterData[0].value = Math.round(mySlider.value)*10;                
        }

        protected function button1_clickHandler(event:MouseEvent):void
        {
            // TODO Auto-generated method stub
            var array:ArrayCollection = testMeter.dataProvider as ArrayCollection;
            var value:Number = array[0].value as Number;
            Alert.show(value.toString());
           // testMeter.meter.width= Math.random()*100;
        }

    ]]>//initial value in "meterData" does get drawn...but when it's changed with the slider..nothing happens..

   <custom:Meter id="testMeter" dataProvider="{meterData}" /> 
    <s:HSlider id="mySlider" 
               liveDragging="true"
               dataTipPrecision="0" 
               change="mySlider_changeHandler(event)"
               value="3"/>
    <s:Button click="button1_clickHandler(event)"/>

Can you help me figure out what's going on??Thanks guys!!!

1
This isn't an answer, but I created a similar component recently as a proof of concept: jeffryhouser.com/index.cfm/2011/6/26/… You can be amused at our different approaches. :-)JeffryHouser

1 Answers

3
votes

The issue is that you are not resetting the dataProvider, nor doing anything to fire off a collectionChange event. I'll deal with each one individually. Here is how you reset the dataProvider:

testMeter.dataProvider = newDataPRovider;

But, you aren't doing that. So the set method will never execute after the first initial set.

If you need the collectionChange event to fire, you need to change the collection. You aren't actually doing that. You are changing the object in the collection. This code does not change the collection:

meterData[0].value = Math.round(mySlider.value)*10; 

It just changes a single property in one of the objects of the collection. Try something like this:

var newObject = meterData[0];
newObject['value'] = Math.round(mySlider.value)*10
meterData.addItem(newObject); 

This code should fire off the colletionChange event, even though the code you have does not.

I have a few more thoughts, unrelated to your primary question. Be sure to removeEventListeners in the dataProvider set method, like this:

public function set dataProvider( value:Object ):void {
    if ( value )
    {
// remove the event listeners here before changing the value
// that should allow the object to have no extra 'references' that prevent it from being garbage collected
                _dataProvider.removeEventListener(PropertyChangeEvent.PROPERTY_CHANGE, propertyChanged);
                _dataProvider.removeEventListener(CollectionEvent.COLLECTION_CHANGE,collectionChanged);

            if(value is ArrayCollection)
               {
                   _renderDirty = true;
                _dataProvider = value as ArrayCollection;
               }                
            if(_dataProvider)
            {
                _dataProvider.addEventListener(PropertyChangeEvent.PROPERTY_CHANGE, propertyChanged);//used both eventlisteners but none actually ever fire off
                _dataProvider.addEventListener(CollectionEvent.COLLECTION_CHANGE,collectionChanged);
            }
            invalidateDisplayList();//only happens the first time
        }
    }

And you said:

(dont want to say "dataProvider" because it is not a Flex dataprovider, just a variable )

A dataProvider on the Flex List, or DataGrid, or DataGroup is also "just a variable." I don't see how the "Flex Framework" implementation is all that different than what you've done, conceptually anyway. Since you're specifically expecting a min and max value perhaps you should use a Value Object with those explicit properties instead of an ArrayCollection.