2011-05-04 24 views
0

我注意到,在Flex的結合意想不到的行爲,我的代碼如下:Flex的綁定:意外行爲

應用程序代碼

<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical" horizontalAlign="center" xmlns:Model="Model.*"> 
    <mx:Script> 
     <![CDATA[ 
      import Model.DataDummy; 
     ]]> 
    </mx:Script> 
    <mx:HBox width="100%" horizontalGap="30"> 
     <mx:Button id="buttonChange1" label="Change property value" click="myDummy._resetMyProperty();" /> 
     <mx:Label id="labelRaw" text="{'My Property=' + myDummy.MyProperty}" opaqueBackground="#DDDDDD" /> 
     <mx:Label id="labelFormatted" text="{'My Property formatted=' + myDummy.MyPropertyFormatted}" opaqueBackground="#DDDDDD" /> 
    </mx:HBox> 

    <Model:MyDummy id="myDummy" /> 

    <mx:DataGrid id="dataGrid" 
     width="100%" dataProvider="{DataDummy.Dummies}"> 
     <mx:columns> 
      <mx:DataGridColumn dataField="MyProperty" headerText="My property" /> 
      <mx:DataGridColumn dataField="MyPropertyFormatted" headerText="My property Formatted" /> 
     </mx:columns> 
    </mx:DataGrid> 
    <mx:Button id="buttonChange2" click="{for each (var d:MyDummy in DataDummy.Dummies){d._resetMyProperty();}}" label="Change property value in DataGrid" /> 
</mx:Application> 

Model.MyDummy類代碼

package Model 
{ 
    import flash.events.EventDispatcher; 

    import mx.formatters.NumberFormatter; 
    import mx.utils.StringUtil; 

    [Bindable] 
    public class MyDummy extends EventDispatcher 
    { 
     /*** Constructor ***/ 
     public function MyDummy() 
     { 
      this._resetMyProperty(); 
     } 

     /*** Properties ***/ 
     private var _myProperty:Number; 
     public function get MyProperty():Number 
     { 
      return _myProperty; 
     } 
     public function set MyProperty(value:Number):void 
     { 
      if (value !== _myProperty) 
      { 
       _myProperty = value; 

       //var event:Event = new Event("ID_Changed"); 
       //this.dispatchEvent(event); 
      } 
     } 

     //[Bindable (event="ID_Changed", type="flash.events.Event")] 
     public function get MyPropertyFormatted():String 
     { 
      var idFormatted:String = ""; 

      if (! isNaN(this.MyProperty)) 
      { 
       var formatter:NumberFormatter = new NumberFormatter(); 
       formatter.precision = 2; 
       idFormatted = formatter.format(this.MyProperty); 
      } 
      else 
       idFormatted = MyProperty.toString(); 

      return StringUtil.substitute("{0} (My property has been formatted)", idFormatted); 
     } 

     /*** Methods ***/ 
     public function _resetMyProperty():void 
     { 
      this.MyProperty = Math.round(Math.random() * 1000000000); 
     } 
    } 
} 

Model.DataDummy class code

package Model 
{ 
    import mx.collections.ArrayCollection; 

    public class DataDummy 
    { 
     private static var _dummies:ArrayCollection; 
     public static function get Dummies():ArrayCollection 
     { 
      if (_dummies == null) 
      { 
       _dummies = new ArrayCollection(); 
       _dummies.addItem(new MyDummy()); 
       _dummies.addItem(new MyDummy()); 
      } 

      return _dummies; 
     } 
    } 
} 

的行爲如下:

  • 當我點擊buttonChange1,_resetMyProperty被稱爲在實例myDummy。

    結果是標籤「labelRaw」的文本已更改,標籤「labelFormatted」的文本未更改。這種情況的發生是因爲MyPropertyFormatted是一個只讀屬性,而且只讀屬性僅在應用程序初始化時綁定,而不是之後,根據Flex文檔。有了這個,我同意。


  • 當我點擊buttonChange2,resetMyProperty方法稱爲ArrayCollection的Model.DataDummy.Dummies的每MyDummy元素(該靜態屬性綁定到DataGrid)。


    結果是DataGrid的兩列都將其值更改爲,儘管DataGrid的第二列鏈接到MyDummy對象的相同只讀屬性MyPropertyFormatted。我發現這與我描述的以前的行爲不一致。

我的觀點是:
1.一方面,因爲我結合我的控制到某個對象的單個實例,結合不會對他的只讀屬性觸發。另一方面,當我綁定一個相同的特定對象的集合上的控件時,綁定將在每個屬性上觸發(只讀或不是)。

如果我想在點1的只讀屬性上觸發綁定,我必須調度一個事件,並精確地在只讀屬性的MetaTag上根據這個事件觸發它們的綁定(如代碼中的註釋Model.MyDummy類的類)。

爲什麼這種行爲有所不同?我想精確地理解一個ArrayCollection實例的綁定所做的事情,即單個實例的綁定不會。

謝謝你的幫助。

回答

2

我想正確的代碼如下所示。

首先,我們model.MyDummy類:

package model 
{ 
import flash.events.EventDispatcher; 

import mx.events.PropertyChangeEvent; 
import mx.formatters.NumberFormatter; 
import mx.utils.StringUtil; 

public class MyDummy extends EventDispatcher 
{ 

    //------------------------------------------------------------------------------ 
    // 
    // Constructor 
    // 
    //------------------------------------------------------------------------------ 

    public function MyDummy() 
    { 
     resetMyProperty(); 
    } 

    //------------------------------------------------------------------------------ 
    // 
    // Properties 
    // 
    //------------------------------------------------------------------------------ 

    //-------------------------------------- 
    // myProperty 
    //-------------------------------------- 

    private var _myProperty:Number; 

    [Bindable(event="propertyChange")] 
    public function get myProperty():Number 
    { 
     return _myProperty; 
    } 

    public function set myProperty(value:Number):void 
    { 
     if (_myProperty == value) 
      return; 
     var oldPropertyValue:Number = _myProperty; 
     var oldFormatted:String = myPropertyFormatted; 
     _myProperty = value; 
     dispatchEvent(PropertyChangeEvent.createUpdateEvent(this, "myProperty", oldPropertyValue, value)); 
     dispatchEvent(PropertyChangeEvent. 
      createUpdateEvent(this, "myPropertyFormatted", oldFormatted, myPropertyFormatted)); 
    } 

    [Bindable(event="propertyChange")] 
    public function get myPropertyFormatted():String 
    { 
     var idFormatted:String = ""; 

     if (!isNaN(myProperty)) 
     { 
      var formatter:NumberFormatter = new NumberFormatter(); 
      formatter.precision = 2; 
      idFormatted = formatter.format(myProperty); 
     } 
     else 
      idFormatted = myProperty.toString(); 

     return StringUtil.substitute("{0} (My property has been formatted)", idFormatted); 
    } 

    //------------------------------------------------------------------------------ 
    // 
    // Methods 
    // 
    //------------------------------------------------------------------------------ 

    public function resetMyProperty():void 
    { 
     myProperty = Math.round(Math.random() * 1000000000); 
    } 
} 
} 

我們射擊propertyChange事件有可能火從collectionChangeEvent我們ArrayCollection(它會自動監聽propertyChange事件)。

然後我們model.DataDummy類:

package model 
{ 
import mx.collections.ArrayCollection; 
import mx.events.CollectionEvent; 

public class DataDummy 
{ 

    //------------------------------------------------------------------------------ 
    // 
    // Constructor 
    // 
    //------------------------------------------------------------------------------ 

    public function DataDummy() 
    { 
     dummies = new ArrayCollection(); 
     dummies.addItem(new MyDummy()); 
     dummies.addItem(new MyDummy()); 
    } 

    //------------------------------------------------------------------------------ 
    // 
    // Variables 
    // 
    //------------------------------------------------------------------------------ 

    [Bindable] 
    public var dummies:ArrayCollection; 
} 
} 

我們不使用靜有優勢的數據與[Bindable]元標記綁定。

最後我們的主類最小的變化:

<mx:Application horizontalAlign="center" layout="vertical" xmlns:model="model.*" xmlns:mx="http://www.adobe.com/2006/mxml"> 
    <mx:Script> 
    <![CDATA[ 
     //------------------------------------------------------------------------------ 
     // 
     // Event Handlers 
     // 
     //------------------------------------------------------------------------------ 

     protected function buttonChange2_clickHandler(event:MouseEvent):void 
     { 
      for each (var d:MyDummy in dataProvider.dummies) 
      { 
       d.resetMyProperty(); 
      } 
     } 
    ]]> 
    </mx:Script> 
    <mx:HBox horizontalGap="30" width="100%"> 
     <mx:Button click="myDummy.resetMyProperty();" id="buttonChange1" label="Change property value" /> 
     <mx:Label id="labelRaw" opaqueBackground="#DDDDDD" text="{'My Property=' + myDummy.myProperty}" /> 
     <mx:Label id="labelFormatted" opaqueBackground="#DDDDDD" 
      text="{'My Property formatted=' + myDummy.myPropertyFormatted}" /> 
    </mx:HBox> 

    <model:MyDummy id="myDummy" /> 
    <model:DataDummy id="dataProvider" /> 

    <mx:DataGrid dataProvider="{dataProvider.dummies}" id="dataGrid" width="100%"> 
     <mx:columns> 
      <mx:DataGridColumn dataField="myProperty" headerText="My property" /> 
      <mx:DataGridColumn dataField="myPropertyFormatted" headerText="My property Formatted" /> 
     </mx:columns> 
    </mx:DataGrid> 
    <mx:Button click="buttonChange2_clickHandler(event)" id="buttonChange2" label="Change property value in DataGrid" /> 
</mx:Application> 

正如你可以看到所有的綁定按預期工作。

P.S. [Bindable(event="propertyChange")]相當於簡單的[Bindable],但這種方式可以避免編譯器警告myPropertyFormatted getter。實際上,使用簡單的[Bindable]窗體會導致mxmlc編譯器自行生成dispatchEvent代碼。並且您可以使用指向[Bindable]標記中的特定事件來獲得更多控制權。例如,在我們的案例中,我們可以觸發myPropertyFormatted的事件。

P.P.S.我已經改變了類似C#的命名約定,以反映實際的ActionScript/MXML。

+0

它能解決你的問題嗎? :) – Constantiner 2011-05-04 14:36:17

+0

謝謝你的回答,並糾正我的命名約定(我必須失去這種壞習慣!)。它解決了這個問題,但我仍然不明白我提供的示例中ArrayCollection的行爲。我的意思是,由於readonly屬性不會觸發綁定,然後更新標籤的文本,爲什麼在更改MyProperty的值時更新了只讀屬性的DataGridColumn?是否ArrayCollection類強制綁定它包含的每個項目的所有公共屬性,即使是隻讀的項目? – x80 2011-05-04 14:40:53

+0

對於你的情況,你有'[Bindable]'MyDummy'類的標籤,這意味着Flex爲所有公共變量和所有get-set對(例如代碼中的MyProperty)生成所有的'propertyChange'事件。這樣'ArrayCollection'監聽元素的變化,並用'event.kind =「update」'調度'collectionChangeEvent'。 'DataGrid'監聽這個事件並重繪其可見的原始數據。 – Constantiner 2011-05-04 14:51:25