1 Reply Latest reply on Jul 17, 2011 1:44 PM by MikisMM

    Custom Component Not Updating Display List

    JoGlez

      Hi all... 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. Re: Custom Component Not Updating Display List
          MikisMM Level 2

          Hi.

          Now there are some serious bugs in your code:

          • Put a call to super.updateDisplayList(unscaledWidth, unscaledHeight) in your overriden method (or some funky things will start happening or it won't work at all).
          • Why don't you check your _renderDirty flag in the updateDisplayList() method? You should do that (personally I would go with commitProperties() instead but it's not much of a difference I guess)
          • And now the reason why your events don't fire is that because you use plain objects (just generic istances of Object). Those are not IEventDispatchers and therefore do not fire any events when you change any of the properties (which are all dynamic by the way). You have to define a custom value object class which extends the EventDispatcher (or you can implement IEventDispatcher on your own). So instead of this:
            [Bindable]
            private var meterData:ArrayCollection = new ArrayCollection([
               {value:80, max:100, min:0}
            ]);
            

            You should do something like this:
            [Bindable]
            private var meterData:ArrayCollection = new ArrayCollection([
               new MyValueObject(80, 100, 0)
            ]);
            

            Where MyValueObject could look like this:
            // this instructs MXML compiler to automaticly implement IEventDispatcher
            // and make all public properties bindable
            [Bindable]
            public class MyValueObject
            {
               public function MyValueObject(value:Number, max:Number, min:Number) {
                  this.value = value;
                  this.max = max;
                  this.min = min;
               }
               public var value:Number;
               public var max:Number;
               public var min:Number;
            }