到目前为止,我只做了一点 Flex 开发,但我更喜欢以编程方式创建控件而不是 mxml 文件的方法,因为(如果我错了,请纠正我!)我\\ '已经收集到你不能同时拥有这两种方式 - 也就是说,在单独的 ActionScript 类文件中拥有类功能,但在 mxml 中声明包含的元素。
在生产力方面似乎没有太大区别,但以编程方式进行数据绑定似乎并不简单。我看了一下 mxml 编译器如何转换数据绑定表达式。结果是一堆生成的回调和比 mxml 表示形式更多的行。那么问题来了:有没有一种方法可以以编程方式进行数据绑定,而不会涉及到伤害的世界?
不要害怕 MXML。它非常适合布置视图。如果您编写自己的可重用组件,那么在 ActionScript 中编写它们有时可能会给您更多的控制权,但对于不可重用的视图,MXML 更好。它更简洁,绑定非常容易设置等。
但是,纯 ActionScript 中的绑定不必那么痛苦。它永远不会像在 MXML 中为您完成很多事情那样简单,但它可以不费力气地完成。
你拥有的是 BindingUtils 和它的方法 bindSetter 和 bindProperty。我几乎总是使用前者,因为我通常想做一些工作,或者当值发生变化时调用 invalidateProperties,我几乎从不只想设置一个属性。
你需要知道的是,这两个返回了一个ChangeWatcher类型的对象,如果你出于某种原因想要移除绑定,你必须抓住这个对象。这就是使 ActionScript 中的手动绑定不如 MXML 中方便的原因。
让我们从一个简单的例子开始:
1
| BindingUtils.bindSetter(nameChanged, selectedEmployee,"name"); |
这将设置一个绑定,当变量 selectedEmployee 中对象的 name 属性发生变化时,该绑定将调用方法 nameChanged。 nameChanged 方法将接收 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()) 的绑定,然后设置私有变量,除非新值为 null 为 name 属性设置新绑定。最重要的是,它保存了绑定调用返回的 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文件中引用这个类中实现的事件监听器。
问候,
露丝