Flex 绑定:意外行为

发布于 2024-11-04 15:50:37 字数 4401 浏览 7 评论 0原文

我注意到 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”的文本未更改。根据 Flex 文档,这种情况确实会发生,因为 MyPropertyFormatted 是只读属性,并且只读属性仅在应用程序初始化时绑定,而不是在初始化之后绑定。对此,我表示同意。


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

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

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

如果我希望在第 1 点中的只读属性上触发绑定,我必须分派一个事件并精确指定只读属性的 MetaTag,以便根据此事件触发它们的绑定(如 Model 类代码中的注释所示) .MyDummy 类)。

为什么这种行为有所不同?我想准确地了解 ArrayCollection 实例的绑定可以做什么,而单个实例的绑定则不能。

感谢您的帮助。

I have noticed an unexpected behaviour with binding in Flex, my code is as follow :

Application code

<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 class code

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;
        }
    }
}

The behaviour is as follow :

  • When I click on buttonChange1, _resetMyProperty is called on the instance myDummy.

    The result is that the label "labelRaw" has its text changed and the label "labelFormatted" does not have its text changed. This does happen because MyPropertyFormatted is a readonly property and that readonly properties are binded only at the initialisation of the application and not afterwards, according to Flex documentation. With this, I agree.

  • When I click on buttonChange2, resetMyProperty method is called on every MyDummy element of the ArrayCollection Model.DataDummy.Dummies (this static property is binded to the DataGrid).

    The result is that both columns of the DataGrid have their values changed, despite the fact that the DataGrid's second column is linked to the same readonly property MyPropertyFormatted of the MyDummy objects. I find this inconsistent with the previous behaviour I described.

My point is that :
1. On one hand, because I'm binding my controls to a single instance of an certain object, binding won't trigger on his readonly properties.

2. On the other hand, when I'm binding a control on a collection of the same certain objects, binding will trigger on every properties (readonly or not).

If I want binding to be triggered on readonly properties in point 1, I have to dispatch an event and precise on the readonly properties' MetaTag that their binding will be triggered according to this event (as show the commentaries in the code of the class Model.MyDummy class).

Why is this behaviour different ? I would like to precisely understand what an ArrayCollection instance's binding does that a single instance's binding does not.

Thank you for your help.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

回梦 2024-11-11 15:50:37

我想正确的代码如下所示。

首先,我们的 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 事件,以便可以从 ArrayCollection 触发 collectionChangeEvent > (它自动监听 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>

如您所见,所有绑定都按预期工作。

PS [Bindable(event="propertyChange")] 相当于简单的 [Bindable],但这样可以避免 myPropertyFormatted 上出现编译器警告吸气剂。实际上,使用简单的[Bindable]形式会导致mxmlc编译器自行生成dispatchEvent代码。您可以使用在 [Bindable] 标记中指向特定事件来进行更多控制。例如,在我们的例子中,我们可以触发 myPropertyFormatted 事件。

PPS 我已经更改了您的类似 C# 的命名约定,以反映实际的 ActionScript/MXML 命名约定。

I suppose the right code is something like the following.

First, our model.MyDummy class:

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);
    }
}
}

We're firing propertyChange event to have possibility to fire collectionChangeEvent from our ArrayCollection (it listens propertyChange event automatically).

Then our model.DataDummy class:

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;
}
}

We don't use statics to have advantage of data binding with [Bindable] metatag.

And finally our main class with minimal changes:

<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>

As you can see all the bindings works as expected.

P.S. [Bindable(event="propertyChange")] is an equivalent of simple [Bindable] but this way you can avoid compiler warnings on myPropertyFormatted getter. Actually, using simple [Bindable] form causes mxmlc compiler to generate dispatchEvent code by itself. And you can use pointing a particular event in [Bindable] tag to have more control. For example in our case we can fire event for myPropertyFormatted.

P.P.S. I've changed your C#-like naming conventions to reflect actual ActionScript/MXML ones.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文