Flex绑定:意外行为

时间:2011-05-04 08:31:50

标签: flex binding readonly arraycollection bindable

我注意到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类代码

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时,在实例myDummy上调用_resetMyProperty。

    结果是标签“labelRaw”的文本已更改,标签“labelFormatted”的文本未更改。之所以会发生这种情况,是因为MyPropertyFormatted是一个只读属性,并且只有在应用程序初始化时才读取只读属性,而不是之后,根据Flex文档。有了这个,我同意。


  • 当我点击buttonChange2时,会在ArrayCollection Model.DataDummy.Dummies的每个MyDummy元素上调用resetMyProperty方法(此静态属性绑定到DataGrid)。

    结果是, DataGrid的两列都更改了值,尽管DataGrid的第二列链接到MyDummy对象的相同readonly属性MyPropertyFormatted。我发现这与我之前描述的行为不一致。

我的观点是:
1.一方面,因为我将控件绑定到某个对象的单个实例,所以绑定不会触发他的只读属性。
2.另一方面,当我在同一个特定对象的集合上绑定控件时,绑定将触发每个属性(只读或不读)。

如果我希望在第1点的readonly属性上触发绑定,我必须在readonly属性'MetaTag上调度一个事件,并根据此事件触发它们的绑定(如显示代码中的注释) Model.MyDummy类。)。

为什么这种行为有所不同?我想准确理解ArrayCollection实例的绑定对单个实例的绑定没有做什么。

感谢您的帮助。

1 个答案:

答案 0 :(得分: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> metatag的数据绑定。

最后我们的主要课程改变很小:

[Bindable(event="propertyChange")]

正如您所看到的,所有绑定都按预期工作。

P.S。 [Bindable]相当于简单myPropertyFormatted,但这样可以避免[Bindable] getter上的编译器警告。实际上,使用简单的mxmlc形式会导致dispatchEvent编译器自己生成[Bindable]代码。您可以使用myPropertyFormatted标记中的特定事件来控制更多内容。例如,在我们的例子中,我们可以为{{1}}启动事件。

P.P.S。我已经改变了类似C#的命名约定,以反映实际的ActionScript / MXML。