关于 actionscript 3:Flex:是否存在无痛的编程数据绑定?

关于 actionscript 3:Flex:是否存在无痛的编程数据绑定?

Flex: does painless programmatic data binding exist?

到目前为止,我只做了一点 Flex 开发,但我更喜欢以编程方式创建控件而不是 mxml 文件的方法,因为(如果我错了,请纠正我!)我\\ '已经收集到你不能同时拥有这两种方式 - 也就是说,在单独的 ActionScript 类文件中拥有类功能,但在 mxml 中声明包含的元素。

在生产力方面似乎没有太大区别,但以编程方式进行数据绑定似乎并不简单。我看了一下 mxml 编译器如何转换数据绑定表达式。结果是一堆生成的回调和比 mxml 表示形式更多的行。那么问题来了:有没有一种方法可以以编程方式进行数据绑定,而不会涉及到伤害的世界?


不要害怕 MXML。它非常适合布置视图。如果您编写自己的可重用组件,那么在 ActionScript 中编写它们有时可能会给您更多的控制权,但对于不可重用的视图,MXML 更好。它更简洁,绑定非常容易设置等。

但是,纯 ActionScript 中的绑定不必那么痛苦。它永远不会像在 MXML 中为您完成很多事情那样简单,但它可以不费力气地完成。

你拥有的是 BindingUtils 和它的方法 bindSetterbindProperty。我几乎总是使用前者,因为我通常想做一些工作,或者当值发生变化时调用 invalidateProperties,我几乎从不只想设置一个属性。

你需要知道的是,这两个返回了一个ChangeWatcher类型的对象,如果你出于某种原因想要移除绑定,你必须抓住这个对象。这就是使 ActionScript 中的手动绑定不如 MXML 中方便的原因。

让我们从一个简单的例子开始:

1
BindingUtils.bindSetter(nameChanged, selectedEmployee,"name");

这将设置一个绑定,当变量 selectedEmployee 中对象的 name 属性发生变化时,该绑定将调用方法 nameChangednameChanged 方法将接收 name 属性的新值作为参数,因此它应该如下所示:

1
private function nameChanged( newName : String ) : void

这个简单示例的问题在于,一旦你设置了这个绑定,它就会在每次指定对象的属性发生变化时触发。变量 selectedEmployee 的值可能会改变,但仍然为变量之前指向的对象设置绑定。

有两种方法可以解决这个问题:保留 BindingUtils.bindSetter 返回的 ChangeWatcher 并在要删除绑定时调用 unwatch (然后设置新的绑定),或者绑定到自己。我将首先向您展示第一个选项,然后解释我所说的绑定到自己的意思。

currentEmployee 可以做成 getter/setter 对并像这样实现(仅显示 setter):

1
2
3
4
5
6
7
8
9
10
11
12
13
public function set currentEmployee( employee : Employee ) : void {
    if ( _currentEmployee != employee ) {
        if ( _currentEmployee != null ) {
            currentEmployeeNameCW.unwatch();
        }

        _currentEmployee = employee;

        if ( _currentEmployee != null ) {
            currentEmployeeNameCW = BindingUtils.bindSetter(currentEmployeeNameChanged, _currentEmployee,"name");
        }
    }
}

发生的情况是,当设置 currentEmployee 属性时,它会查看是否存在先前的值,如果是,则删除该对象 (currentEmployeeNameCW.unwatch()) 的绑定,然后设置私有变量,除非新值为 nullname 属性设置新绑定。最重要的是,它保存了绑定调用返回的 ChangeWatcher

这是一个基本的绑定模式,我认为它工作得很好。然而,有一个技巧可以让它变得更简单一些。您可以改为绑定到自己。无需在每次 currentEmployee 属性更改时设置和删除绑定,您可以让绑定系统为您完成。在您的 creationComplete 处理程序(或构造函数或至少提前一段时间)中,您可以像这样设置绑定:

1
BindingUtils.bindSetter(currentEmployeeNameChanged, this, ["currentEmployee","name"]);

这不仅设置了到 this 上的 currentEmployee 属性的绑定,而且还设置了到该对象上的 name 属性的绑定。因此,无论何时更改,都会调用 currentEmployeeNameChanged 方法。无需保存 ChangeWatcher,因为永远不必删除绑定。

第二个解决方案在很多情况下都有效,但我发现第一个解决方案有时是必要的,尤其是在处理非视图类中的绑定时(因为 this 必须是事件调度程序,而 必须是可绑定的才能工作)。


它存在于今天。 :)

我刚刚将我的 ActionScript 数据绑定项目作为开源发布:http://code.google.com/p/bindage-tools

BindageTools 是 BindingUtils 的替代品(参见那里的文字游戏?),它使用流畅的 API,您可以在管道样式中声明数据绑定:

1
2
Bind.fromProperty(person,"firstName")
    .toProperty(firstNameInput,"text");

双向绑定:

1
2
3
Bind.twoWay(
    Bind.fromProperty(person,"firstName"),
    Bind.fromProperty(firstNameInput,"text"));

显式数据转换和验证:

1
2
3
4
5
6
Bind.twoWay(
    Bind.fromProperty(person,"age")
        .convert(valueToString()),
    Bind.fromProperty(ageInput,"text")
        .validate(isNumeric()) // (Hamcrest-as3 matcher)
        .convert(toNumber()));

等等。网站上有更多示例。还有很多其他功能,快来看看吧。 --马修

编辑:更新的 API


将组件的 MXML 和 ActionScript 分离到单独的文件中的一种方法是执行类似于 ASP.Net 1.x 代码隐藏模型的操作。在此模型中,声明部分(在本例中为 MXML)是命令部分(ActionScript)的子类。所以我可能会为这样的类声明代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package CustomComponents
{
    import mx.containers.*;
    import mx.controls.*;
    import flash.events.Event;

    public class MyCanvasCode extends Canvas
    {
        public var myLabel : Label;

        protected function onInitialize(event : Event):void
        {
            MyLabel.text ="Lorem ipsum dolor sit amet, consectetuer adipiscing elit.";
        }
    }
}

...和这样的标记:

1
2
3
4
5
6
?xml version="1.0" encoding="utf-8"?
MyCanvasCode xmlns="CustomComponents.*"
    xmlns:mx="http://www.adobe.com/2006/mxml"
    initialize="onInitialize(event)"
    mx:Label id="myLabel"/    
/MyCanvasCode

从这个例子可以看出,这种方法的一个缺点是你必须在两个文件中声明像 myLabel 这样的控件。


我通常使用一种方法将 mxml 和actionscript一起使用:我所有的 mxml 组件都继承自一个actionscript类,我在其中添加了更复杂的代码。然后可以在mxml文件中引用这个类中实现的事件监听器。

问候,

露丝


推荐阅读