丢失静默错误(SILENT ERROR)
在某些情形下,绑定操作似乎不能正常工作,此时你可能非常懊恼并且不知道如何进行处理。
由绑定表达式抛出的、或在绑定框架调用的绑定函数中出现的异常和错误可以被静默捕获。因此,你将不会看到运行时异常,而这在 Flash Player 的调试版本中是能够看到的。这时,不仅绑定操作不能工作,而且系统不会显示错误。
为什么错误被静默捕获?
在绑定操作发生之前,实现绑定机制的代码要求满足若干条件。绑定机制将吞没所有错误以便阻止在运行时过程中抛出运行时异常。这是一种很好的机制,因为你不希望在你的应用程序中看到这些(可能的)意外错误。
请查看下面简单绑定范例:
<?xml version="1.0" encoding="utf-8"?> <s:Application xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark" xmlns:mx="library://ns.adobe.com/flex/mx" minWidth="1024" minHeight="768" preinitialize="handlePreinitialize()"> <fx:Script> <![CDATA[ [Bindable] private var xml:XML = <users> <user> <name>EladElrom</name> <address>1 Wall Street</address> </user> </users>; protected function handlePreinitialize():void { xml = null; //BindingManager.debugBinding("label.text"); } ]]> </fx:Script> <s:Label id="label" text="{xml.user.name}"/> </s:Application>
我已经添加一个与Label
组件绑定的xml
变量。上面代码本可以支持执行。然而,我在组件的预初始化期间已经将该xml变量设置为null
。在组件初始化序列开始时系统会下发该事件,这样该xml
对象将没有name
属性。如果你运行该应用程序,你将会发现绑定操作不能执行并且错误将被系统静默捕获。
调试绑定
尽管系统可以静默地捕获错误,但你仍然可以通过某些手段了解应用程序的运行过程。 利用 BindingManager.as 和 Binding.as 代码进行调试不是轻而易举的事,因为你需要下载整个 Flex SDK 。
取而代之的是,你可以设置一个断点并且追踪到相关的绑定对象以发现错误的原因。在本例中,你将会发现 XML 对象的值被设置为 null,并且这就是绑定失败的原因(参见图1)。
另一个更为直观的方法是使用 BindingManager 类的 debugBinding 方法。你可以对你希望观察的组件和属性进行设置,然后你将看到错误的发生和被静默地捕获的过程。
在上面的范例代码中,我将下一行的代码加注了注释:
BindingManager.debugBinding("label.text");
去除该行的注释然后在调试模式运行该应用程序;你将会在 Console 视图中看到绑定错误(参见图2)。
查看一下Binding.as 和 BindingManager.as 类。该代码具有许多if 和try… catch 语句,这些语句确保执行有效绑定的条件能够满足。 下面是当使用绑定时,一些能够抛出的错误用例:
- Error #1006: Call attempted on an object that is not a function.
- Error #1009: Null has no properties.
- Error #1010: Undefined has no properties.
- Error #1055: Has no properties.
- Error #1069: Property - not found on - and there is no default value
当这些错误的任何一个出现时,绑定管理器将会静默地捕获它们并且不再执行绑定。你可以使用绑定管理器的调试选项查看它们。除了捕获这些错误之外,绑定管理器还能够捕获其它错误。
通常在许多情形下运行时错误均可以发生。例如,当对包含一个数据绑定表达式的 Label 组件进行初始化时,可能没有设置目标属性值,因此执行该绑定表达式将导致一个 “Error #1009 – Null has no properties” 的运行时异常。 类似地,某些数据绑定表达式仅仅在你的应用程序的用户执行在一个 List 中选择一个条目等操作时有效。 如果你的数据绑定表达式指向 List 的 selectedItem 属性,则它的值在用户实际点击并且选择 List 中的一个条目之前将为 null。
尝试绑定一个不包含 IPROPERTYCHANGENOTIFIER 接口的类
实现 IPropertyChangeNotifier 标记接口的类必须为类中的属性下发事件,并且任何嵌套类必须作为属性对外暴露。因此,你可以发现类中的属性在什么时间发生更改。例如,查看 UIComponent 类的签名(参见图3)。UIComponent 确实能够实现 dispatchPropertyChangeEvent,一旦属性发生更改,它将下发一个事件。
现在考虑下列包含用户信息的类:
package vo { public final class UserInfo { public var userName:String; public var password:String; public function UserInfo() { } } }
如果你把 Label 的 text 属性与 UserInfo 类的一个属性进行绑定,它将不能正常运行,如下所示:
<?xml version="1.0" encoding="utf-8"?> <s:Application xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark" xmlns:mx="library://ns.adobe.com/flex/mx" minWidth="1024" minHeight="768" creationComplete="creationCompleteHandler()"> <fx:Script> <![CDATA[ import vo.UserInfo; [Bindable] private var userInfo:UserInfo = new UserInfo(); protected function creationCompleteHandler():void { userInfo.userName = "EladElrom"; } ]]> </fx:Script> <s:Label id="lbl" text="{userInfo.userName}" /> </s:Application>
由于该代码试图绑定一个不包含IpropertyChangeNotifier
接口的类,因此,该绑定机制将不能正常工作。
在本例中,你将在 Problems 视图中看到下列消息(参见图4):
Data binding will not be able to detect assignments to "userName".
为了允许数据绑定功能能够在上面范例中正常工作,你可以将 [Bindable] 标签附着到该类。这将使得数据绑定功能可以使用该类的所有公有属性。Flex 编译器将会为你生成一个公有 getter 和 setter 函数,该函数将包含能够使得数据绑定功能正常工作的所有必要代码。另外,如果你不希望数据绑定功能使用所有属性,你可以将 [Bindable] 标签附着到该类的特定属性。
package vo {[Bindable] public final class UserInfo { public var userName:String; public var password:String; public function UserInfo() { } } }
ObjectProxy 类
数据绑定功能要求你绑定的类必须实现 IpropertyChangeNotifier 接口。否则,该对象是不能绑定的。
然而,没有标记 [Bindable] 的类/属性或变量(例如primitive变量)不能实现 IpropertyChangeNotifier 接口。 如果这是你自己的接口,则你需要做的全部事情是添加 [Bindable] 元数据标记。如果这不是你希望绑定的类,或你希望在运行时期间添加绑定功能,则你可以使用 ObjectProxy 类。当该类的任何属性发生更改时,ObjectProxy 将把一个不能绑定的类包起来并且下发一个 PropertyChangeEvent 事件,以便允许你应用程序的对象侦听属性更改。如需获得更多信息,参见 Adobe Flex 4语言参考资料(Adobe Flex 4 Language Reference)的 ObjectProxy 。
下面是一个 ObjectProxy
的使用范例。 我创建一个新的 ObjectProxy
实例并且传递我希望观察的对象(在该例中为UserInfo)。然后我添加一个事件侦听程序并且跟踪UserInfo的一个条目的更改情况。
<?xml version="1.0" encoding="utf-8"?> <s:Application xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark" xmlns:mx="library://ns.adobe.com/flex/mx" minWidth="1024" minHeight="768" creationComplete="creationCompleteHandler()"> <fx:Script> <![CDATA[ import mx.events.PropertyChangeEvent; import mx.utils.ObjectProxy; import vo.UserInfo; private var userInfo:UserInfo = new UserInfo(); private var objectProxy:ObjectProxy; protected function creationCompleteHandler():void { objectProxy = new ObjectProxy( userInfo ); objectProxy.addEventListener( PropertyChangeEvent.PROPERTY_CHANGE, onPropertyChange ); objectProxy.userName = "EladElrom"; objectProxy.password = "123"; } private function onPropertyChange( event:PropertyChangeEvent ):void { lbl.text = event.newValue.toString(); } ]]> </fx:Script> <s:Label id="lbl" /> </s:Application>
在使用 ObjectProxy
时必须记住一件事:为了便于发送赋值通知,每当目标对象的任何属性更改时将调用注册的侦听程序。这将会潜在地引入明显的过负荷,实际上,在我们的范例中,共调用两次 onPropertyChange
,因为发生两次更改。
使用绑定代替直接赋值
在你不需要进行绑定以及你可以利用直接赋值获得相同效果的情形下,尽量避免使用绑定。我已经看到各种形式的这类错误。
下面代码给出了一个范例:
<?xml version="1.0" encoding="utf-8"?> <s:Application xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark" minWidth="1024" minHeight="768"> <fx:Script> <![CDATA[ private var text:String; ]]> </fx:Script> <s:layout> <s:VerticalLayout/> </s:layout> <s:TextInput id="textInput2" text="{text}" /> </s:Application>
该代码定义了一个 TextInput 控件,该控件具有与 text 私有变量绑定的 text 属性。它看起来丝毫没有问题,对吗?我经常在 Flex 应用程序中看到这些类型的标记。Flex 编译器能够生成支持绑定功能的代码。你将会发现,尽管你不需要绑定该 text 字符串,因为它是一个一次性赋值,但编译器仍然生成可以提供属性绑定功能的代码。此外, 在某些情形下,你希望在赋值之后去除绑定或删除绑定代码以便降低开销,但你不能使用 MXML 的 标记来完成这一任务。
根据经验,应该避免与私有变量进行绑定。
在上面的范例中,你可以使用直接赋值的方式设置相关的值:
<s:TextInput id="textInput2" text="some text goes here" />
当你使用直接赋值方式时,你可以显著地降低你的开销,因为编译器不会产生不必要的绑定代码。
根据一般经验,除非你绑定的属性能够或将会更改,否则不使用数据绑定功能。
忘记解除和承担内查泄漏风险
你可以在 MXML 中使用 标记或大括号以便实现绑定功能,然而这些方法将会产生开销。此外,你不能够使用这些技术去除绑定。如果你希望对高性能应用程序进行优化,你可以使用BindingUtils
类来绑定你的对象。 BindingUtils
类的使用方式有两种:
bindProperty()& lt;/code> 方法是用于绑定共有属性的静态方法。
bindSetter()
方法是用于绑定setter函数的静态方法。
让我们来看一下
bindProperty
方法签名:
public static function bindProperty(
site:Object, prop:String,
host:Object, chain:Object,
commitOnly:Boolean = false,
useWeakReference:Boolean = false):ChangeWatcher
站点和主机参数分别表示目的地
和源
对象。当处理程序只基于承诺更改事件(committing change event)调用时,你可以将 commitOnly
设置为true
;而当处理程序能够基于承诺和非承诺更改事件(committing and non-committing change event)调用时,你可以将commitOnly 设置为 false
(默认设置)。
useWeakReference
参数允许你定义到主机的引用是strong还是weak。strong 引用(默认) 能够防止主机被垃圾收集。而weak 引用不能做到这一点。下面的范例包含一个text输入程序和一个简单组件。当对 TextInput 控件进行预初始化时,调用使用 bindProperty
方法的preinitializeHandler()
函数。
<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
minWidth="1024" minHeight="768">
<fx:Script>
<![CDATA[
import mx.binding.utils.BindingUtils;
import mx.events.FlexEvent;
protected function preinitializeHandler(event:FlexEvent):void
{
BindingUtils.bindProperty(label, "text", textInput, "text");
}
]]>
</fx:Script>
<s:layout>
<s:VerticalLayout/>
</s:layout>
<s:TextInput id="textInput" preinitialize="preinitializeHandler(event)" />
<s:Label id="label" />
</s:Application>
下面是一个 bindSetter
的使用范例:
public static function bindSetter(setter:Function, host:Object,
chain:Object,
commitOnly:Boolean = false,
useWeakReference:Boolean = false):ChangeWatcher
setter
参数定义了setter方法,以便当chain的当前值更改时利用该值的自变量调用setter方法。其中 host
表示源对象,而 chain
表示属性名称。 commitOnly
和useWeakReference
参数与 bindProperty
的工作方式是相同的。
Here is an example that uses bindSetter
:
<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
minWidth="1024" minHeight="768">
<fx:Script>
<![CDATA[
import mx.binding.utils.BindingUtils;
import mx.events.FlexEvent;
protected function preinitializeHandler(event:FlexEvent):void
{
BindingUtils.bindSetter(bindingSetter, textInput, "text");
}
private function bindingSetter(str:String):void
{
label.text = str;
}
]]>
</fx:Script>
<s:layout>
<s:VerticalLayout/>
</s:layout>
<s:TextInput id="textInput" preinitialize="preinitializeHandler(event)" />
<s:Label id="label" />
</s:Application>
在后台,ChangeWatcher
类可以在 BindingUtils
类中使用。它支持 weak
引用,因此,当你将 useWeakReference
设置为 true
时,垃圾收集器将自动地清扫主机,以避免潜在内存泄漏。
当你使用 weak
引用时,在你使用它完成任务之后, ChangeWatcher
对象需要解除监控。你有义务处理该任务以确保不会出现内存泄漏现象。处理该任务的最佳方法是将由bindProperty
返回的静态方法赋值给一个 ChangeWatcher
变量(由于静态方法能够返回 ChangeWatcher
实例,你可以将其赋值给一个变量)。当你不再需要绑定对象时,你可以使用 unwatch
方法,正如你在下面范例中看到的一样(参见图5)。
<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
minWidth="1024" minHeight="768">
<fx:Script>
<![CDATA[
import mx.binding.utils.ChangeWatcher;
import mx.binding.utils.BindingUtils;
import mx.events.FlexEvent;
private var change:ChangeWatcher;
protected function preinitializeHandler(event:FlexEvent):void
{
change = BindingUtils.bindProperty( label, "text",
textInput, "text", false, true);
}
protected function clickHandler(event:MouseEvent):void
{
change.unwatch();
change = null;
}
]]>
</fx:Script>
<s:layout>
<s:VerticalLayout/>
</s:layout>
<s:TextInput id="textInput"
preinitialize="preinitializeHandler(event)" />
<s:Label id="label" />
<s:Button label="Stop binding" click="clickHandler(event)" />
</s:Application>
TextInput
的 text
属性是与 Label
的 text
属性绑定的,当你在 TextInput
中输入文本时,该文本数据将被复制到 Label
组件的 text
属性中。当你准备去除绑定时,点击Stop Binding
。该操作将会解除对相应属性的监控并且将相应的对象设置为 null
,这样在垃圾收集期间,该对象将被清除。
不改变默认 PROPERTYCHANGE 事件常量
当你在没有添加事件设置的情形下使用 Bindable
标记时,propertyChange
是将被下发的默认事件类型。 因此,[Bindable]
标记等同于Bindable(event="propertyChange")
。 当你没有指定事件字符串时,编译器将为setter
和 getter
函数创建附加代码,因此建议你添加自己的名称常量以避免产生这种额外的开销。
请看下面绑定语句:
[Bindable]
public var testData:String;
由于没有设置事件名称,编译器将创建下列代码:
[Bindable(event="propertyChange")]
public function get testData():String
{
return this._1147269284testData;
}
public function set testData(value:String):void
{
var oldValue:Object = this._1147269284testData;
if (oldValue !== value) {
this._1147269284testData = value;
if (this.hasEventListener("propertyChange"))
this.dispatchEvent(mx.events.PropertyChangeEvent.createUpdateEvent
(this, "testData", oldValue, value));
}
}
正如你看到的那样,mxmlc
创建一个合成 setter
函数,该函数包含发送PropertyChangeEvent
事件的代码。当你更改默认常数,Flex
编译器将不会生成代码,并且你自己应该负责发送该事件。这样,你可以优化侦听代码。如果你没有更改默认常数,则每个 [Bindable]
属性将会发送 propertyChange
事件,并且侦听代码必须查询该事件以便确定哪个属性已经更改。如果一个类具有大量的 [Bindable]
属性,则这是一个代价非常高的过程。
一旦你自己设置了事件名称(如下面范例所示),则编译器将会复制该代码。
Private var _number:Number = 0;
[Bindable(event="changeNumberEvent")]
public function get number():Number
{
return _number;
}
public function set number(value:Number) : void
{
_number = value;
dispatchEvent(new Event("changeNumberEvent"));
}
使用错误可绑定事件名称
使用 [Bindable]
标记中的错误事件名称将会导致你的应用程序不能绑定你的属性,而且你甚至还不知道为什么。当你使用具有客户名称的 [Bindable]
标记时,下面的范例似乎是个很好的主意:
public static const EVENT_CHANGED_CONST:String = "eventChangedConst";
private var _number:Number = 0;
[Bindable(event=EVENT_CHANGED_CONST)]
public function get number():Number
{
return _number;
}
public function set number(value:Number) : void
{
_number = value;
dispatchEvent(new Event(EVENT_CHANGED_CONST));
}
上面的代码将一个静态属性赋给事件名称,然后使用相同的赋值发送该事件。然而,当该值更改时,绑定功能似乎不能工作。其原因是该事件的名称将是EVENT_CHANGED_CONST 而不是该变量的值。
你的代码应该如下所示:
public static const EVENT_CHANGED_CONST:String = "eventChangedConst";
private var _number:Number = 0;
[Bindable(event="eventChangedConst")]
public function get number():Number
{
return
_number;
}
public function set number(value:Number) : void
{
_number = value;
dispatchEvent(new Event(EVENT_CHANGED_CONST));
}
假定绑定的执行顺序
绑定的一个常见的错误是假定绑定是以同步的执行顺序发生的。这将会导致你的应用程序产生告警并且不能绑定你的属性。实际上,ActionScript的事件是以异步的方式执行的。
查看下面的代码范例:
<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
minWidth="1024" minHeight="768"
creationComplete="creationCompleteHandler(event)">
<fx:Script>
<![CDATA[
import mx.events.FlexEvent;
[Bindable]
public var buttonText:String = "Execution order mistake ";
]]>
</fx:Script>
<s:layout>
<s:HorizontalLayout />
</s:layout>
<s:Label id="label" text="{buttonText}" />
<s:Button label="{label.text}" />
</s:Application>
上面的代码也许能够运行,然而,它也许不能运行。它假定 Label
的 text
属性已经设置完毕,因为 button
组件的label
. text
将与 Label
. text
属性绑定。如果你编译该应用程序,你也会收到一个编译时间告警信息(参见图6)。
下面是另一个范例。它假定第一个 TextInput
控件的值已经设置。这一类型的赋值也会导致编译器产生绑定所需的所有代码。你必须决定是否需要该代码或是否选择直接赋值(y=35)。
<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
minWidth="1024" minHeight="768">
<s:TextInput id="textInput1" x="10" y="0" />
<s:TextInput id="textInput2" x="0" y="{textInput1.x+25}" />
</s:Application>
使用绑定代替事件
在许多情形下,你可以不使用数据绑定很方便地编写你的代码,而是使用事件事件进行赋值操作。你可以通过使用合适的组件生命周期事件或覆盖 childrenCreated()
和initializationComplete()
等组件设置一个值,以便进行赋值操作。此外,你可以ChangeWatcher
侦听数据的更改,这一方法代价较小。当使用 ChangeWatcher
时,记住在许多情形下,你可以避免使用 ChangeWatcher
,因为你可以人工获得通知。例如,Flex
的所有收集对象具有一个能够广播的 collectionChange
事件,你可以使用该事件在收集对象中人工获得更改通知。
请查看下面范例:
<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
minWidth="1024" minHeight="768">
<fx:Script>
<![CDATA[
import mx.events.FlexEvent;
[Bindable]
public var dp:Array = [ { label:"New York", data: "New York" },
{ label:"Miami Beach", data: "Miami Beach" } ];
]]>
</fx:Script>
<mx:ComboBox id="cb" editable="false" width="100" dataProvider="{dp}" />
</s:Application>
该代码看起来是一个 Flex
应用程序的相当标准代码。然而,这里给出的绑定类型是不需要的。你可以使用事件处理程序的直接赋值方式取代绑定方式。
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
minWidth="1024" minHeight="768"
creationComplete="creationCompleteHandler(event)">
<fx:Script>
<![CDATA[
import mx.events.FlexEvent;
public var dp:Array = [ { label:"New York", data: "New York" },
{ label:"Miami Beach", data: "Miami Beach" } ];
protected function creationCompleteHandler(event:FlexEvent):void
{
cb.dataProvider = dp;
}
]]>
</fx:Script>
<mx:ComboBox id="cb" editable="false" width="100" />
</s:Application>
使用错误可绑定事件名称
使用 [Bindable]
标记中的错误事件名称将会导致你的应用程序不能绑定你的属性,而且你甚至还不知道为什么。当你使用具有客户名称的 [Bindable]
标记时,下面的范例似乎是个很好的主意:
public static const EVENT_CHANGED_CONST:String = "eventChangedConst";
private var _number:Number = 0;
[Bindable(event=EVENT_CHANGED_CONST)]
public function get number():Number
{
return _number;
}
public function set number(value:Number) : void
{
_number = value;
dispatchEvent(new Event(EVENT_CHANGED_CONST));
}
上面的代码将一个静态属性赋给事件名称,然后使用相同的赋值发送该事件。然而,当该值更改时,绑定功能似乎不能工作。其原因是该事件的名称将是EVENT_CHANGED_CONST 而不是该变量的值。
你的代码应该如下所示:
public static const EVENT_CHANGED_CONST:String = "eventChangedConst";
private var _number:Number = 0;
[Bindable(event="eventChangedConst")]
public function get number():Number
{
return
_number;
}
public function set number(value:Number) : void
{
_number = value;
dispatchEvent(new Event(EVENT_CHANGED_CONST));
}
假定绑定的执行顺序
绑定的一个常见的错误是假定绑定是以同步的执行顺序发生的。这将会导致你的应用程序产生告警并且不能绑定你的属性。实际上,ActionScript的事件是以异步的方式执行的。
查看下面的代码范例:
<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
minWidth="1024" minHeight="768"
creationComplete="creationCompleteHandler(event)">
<fx:Script>
<![CDATA[
import mx.events.FlexEvent;
[Bindable]
public var buttonText:String = "Execution order mistake ";
]]>
</fx:Script>
<s:layout>
<s:HorizontalLayout />
</s:layout>
<s:Label id="label" text="{buttonText}" />
<s:Button label="{label.text}" />
</s:Application>
上面的代码也许能够运行,然而,它也许不能运行。它假定 Label
的 text
属性已经设置完毕,因为 button
组件的label
. text
将与 Label
. text
属性绑定。如果你编译该应用程序,你也会收到一个编译时间告警信息(参见图6)。
下面是另一个范例。它假定第一个 TextInput
控件的值已经设置。这一类型的赋值也会导致编译器产生绑定所需的所有代码。你必须决定是否需要该代码或是否选择直接赋值(y=35)。
<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
minWidth="1024" minHeight="768">
<s:TextInput id="textInput1" x="10" y="0" />
<s:TextInput id="textInput2" x="0" y="{textInput1.x+25}" />
</s:Application>
使用绑定代替事件
在许多情形下,你可以不使用数据绑定很方便地编写你的代码,而是使用事件事件进行赋值操作。你可以通过使用合适的组件生命周期事件或覆盖 childrenCreated()
和initializationComplete()
等组件设置一个值,以便进行赋值操作。此外,你可以ChangeWatcher
侦听数据的更改,这一方法代价较小。当使用 ChangeWatcher
时,记住在许多情形下,你可以避免使用 ChangeWatcher
,因为你可以人工获得通知。例如,Flex
的所有收集对象具有一个能够广播的 collectionChange
事件,你可以使用该事件在收集对象中人工获得更改通知。
请查看下面范例:
<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
minWidth="1024" minHeight="768">
<fx:Script>
<![CDATA[
import mx.events.FlexEvent;
[Bindable]
public var dp:Array = [ { label:"New York", data: "New York" },
{ label:"Miami Beach", data: "Miami Beach" } ];
]]>
</fx:Script>
<mx:ComboBox id="cb" editable="false" width="100" dataProvider="{dp}" />
</s:Application>
该代码看起来是一个 Flex
应用程序的相当标准代码。然而,这里给出的绑定类型是不需要的。你可以使用事件处理程序的直接赋值方式取代绑定方式。
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
minWidth="1024" minHeight="768"
creationComplete="creationCompleteHandler(event)">
<fx:Script>
<![CDATA[
import mx.events.FlexEvent;
public var dp:Array = [ { label:"New York", data: "New York" },
{ label:"Miami Beach", data: "Miami Beach" } ];
protected function creationCompleteHandler(event:FlexEvent):void
{
cb.dataProvider = dp;
}
]]>
</fx:Script>
<mx:ComboBox id="cb" editable="false" width="100" />
</s:Application>
使用错误可绑定事件名称
使用 [Bindable]
标记中的错误事件名称将会导致你的应用程序不能绑定你的属性,而且你甚至还不知道为什么。当你使用具有客户名称的 [Bindable]
标记时,下面的范例似乎是个很好的主意:
public static const EVENT_CHANGED_CONST:String = "eventChangedConst";
private var _number:Number = 0;
[Bindable(event=EVENT_CHANGED_CONST)]
public function get number():Number
{
return _number;
}
public function set number(value:Number) : void
{
_number = value;
dispatchEvent(new Event(EVENT_CHANGED_CONST));
}
上面的代码将一个静态属性赋给事件名称,然后使用相同的赋值发送该事件。然而,当该值更改时,绑定功能似乎不能工作。其原因是该事件的名称将是EVENT_CHANGED_CONST 而不是该变量的值。
你的代码应该如下所示:
public static const EVENT_CHANGED_CONST:String = "eventChangedConst";
private var _number:Number = 0;
[Bindable(event="eventChangedConst")]
public function get number():Number
{
return
_number;
}
public function set number(value:Number) : void
{
_number = value;
dispatchEvent(new Event(EVENT_CHANGED_CONST));
}
假定绑定的执行顺序
绑定的一个常见的错误是假定绑定是以同步的执行顺序发生的。这将会导致你的应用程序产生告警并且不能绑定你的属性。实际上,ActionScript的事件是以异步的方式执行的。
查看下面的代码范例:
<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
minWidth="1024" minHeight="768"
creationComplete="creationCompleteHandler(event)">
<fx:Script>
<![CDATA[
import mx.events.FlexEvent;
[Bindable]
public var buttonText:String = "Execution order mistake ";
]]>
</fx:Script>
<s:layout>
<s:HorizontalLayout />
</s:layout>
<s:Label id="label" text="{buttonText}" />
<s:Button label="{label.text}" />
</s:Application>
上面的代码也许能够运行,然而,它也许不能运行。它假定 Label
的 text
属性已经设置完毕,因为 button
组件的label
. text
将与 Label
. text
属性绑定。如果你编译该应用程序,你也会收到一个编译时间告警信息(参见图6)。
下面是另一个范例。它假定第一个 TextInput
控件的值已经设置。这一类型的赋值也会导致编译器产生绑定所需的所有代码。你必须决定是否需要该代码或是否选择直接赋值(y=35)。
<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
minWidth="1024" minHeight="768">
<s:TextInput id="textInput1" x="10" y="0" />
<s:TextInput id="textInput2" x="0" y="{textInput1.x+25}" />
</s:Application>
使用绑定代替事件
在许多情形下,你可以不使用数据绑定很方便地编写你的代码,而是使用事件事件进行赋值操作。你可以通过使用合适的组件生命周期事件或覆盖 childrenCreated()
和initializationComplete()
等组件设置一个值,以便进行赋值操作。此外,你可以ChangeWatcher
侦听数据的更改,这一方法代价较小。当使用 ChangeWatcher
时,记住在许多情形下,你可以避免使用 ChangeWatcher
,因为你可以人工获得通知。例如,Flex
的所有收集对象具有一个能够广播的 collectionChange
事件,你可以使用该事件在收集对象中人工获得更改通知。
请查看下面范例:
<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
minWidth="1024" minHeight="768">
<fx:Script>
<![CDATA[
import mx.events.FlexEvent;
[Bindable]
public var dp:Array = [ { label:"New York", data: "New York" },
{ label:"Miami Beach", data: "Miami Beach" } ];
]]>
</fx:Script>
<mx:ComboBox id="cb" editable="false" width="100" dataProvider="{dp}" />
</s:Application>
该代码看起来是一个 Flex
应用程序的相当标准代码。然而,这里给出的绑定类型是不需要的。你可以使用事件处理程序的直接赋值方式取代绑定方式。
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
minWidth="1024" minHeight="768"
creationComplete="creationCompleteHandler(event)">
<fx:Script>
<![CDATA[
import mx.events.FlexEvent;
public var dp:Array = [ { label:"New York", data: "New York" },
{ label:"Miami Beach", data: "Miami Beach" } ];
protected function creationCompleteHandler(event:FlexEvent):void
{
cb.dataProvider = dp;
}
]]>
</fx:Script>
<mx:ComboBox id="cb" editable="false" width="100" />
</s:Application>
使用错误可绑定事件名称
使用 [Bindable]
标记中的错误事件名称将会导致你的应用程序不能绑定你的属性,而且你甚至还不知道为什么。当你使用具有客户名称的 [Bindable]
标记时,下面的范例似乎是个很好的主意:
public static const EVENT_CHANGED_CONST:String = "eventChangedConst";
private var _number:Number = 0;
[Bindable(event=EVENT_CHANGED_CONST)]
public function get number():Number
{
return _number;
}
public function set number(value:Number) : void
{
_number = value;
dispatchEvent(new Event(EVENT_CHANGED_CONST));
}
上面的代码将一个静态属性赋给事件名称,然后使用相同的赋值发送该事件。然而,当该值更改时,绑定功能似乎不能工作。其原因是该事件的名称将是EVENT_CHANGED_CONST 而不是该变量的值。
你的代码应该如下所示:
public static const EVENT_CHANGED_CONST:String = "eventChangedConst";
private var _number:Number = 0;
[Bindable(event="eventChangedConst")]
public function get number():Number
{
return
_number;
}
public function set number(value:Number) : void
{
_number = value;
dispatchEvent(new Event(EVENT_CHANGED_CONST));
}
假定绑定的执行顺序
绑定的一个常见的错误是假定绑定是以同步的执行顺序发生的。这将会导致你的应用程序产生告警并且不能绑定你的属性。实际上,ActionScript的事件是以异步的方式执行的。
查看下面的代码范例:
<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
minWidth="1024" minHeight="768"
creationComplete="creationCompleteHandler(event)">
<fx:Script>
<![CDATA[
import mx.events.FlexEvent;
[Bindable]
public var buttonText:String = "Execution order mistake ";
]]>
</fx:Script>
<s:layout>
<s:HorizontalLayout />
</s:layout>
<s:Label id="label" text="{buttonText}" />
<s:Button label="{label.text}" />
</s:Application>
上面的代码也许能够运行,然而,它也许不能运行。它假定 Label
的 text
属性已经设置完毕,因为 button
组件的label
. text
将与 Label
. text
属性绑定。如果你编译该应用程序,你也会收到一个编译时间告警信息(参见图6)。
下面是另一个范例。它假定第一个 TextInput
控件的值已经设置。这一类型的赋值也会导致编译器产生绑定所需的所有代码。你必须决定是否需要该代码或是否选择直接赋值(y=35)。
<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
minWidth="1024" minHeight="768">
<s:TextInput id="textInput1" x="10" y="0" />
<s:TextInput id="textInput2" x="0" y="{textInput1.x+25}" />
</s:Application>
使用绑定代替事件
在许多情形下,你可以不使用数据绑定很方便地编写你的代码,而是使用事件事件进行赋值操作。你可以通过使用合适的组件生命周期事件或覆盖 childrenCreated()
和initializationComplete()
等组件设置一个值,以便进行赋值操作。此外,你可以ChangeWatcher
侦听数据的更改,这一方法代价较小。当使用 ChangeWatcher
时,记住在许多情形下,你可以避免使用 ChangeWatcher
,因为你可以人工获得通知。例如,Flex
的所有收集对象具有一个能够广播的 collectionChange
事件,你可以使用该事件在收集对象中人工获得更改通知。
请查看下面范例:
<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
minWidth="1024" minHeight="768">
<fx:Script>
<![CDATA[
import mx.events.FlexEvent;
[Bindable]
public var dp:Array = [ { label:"New York", data: "New York" },
{ label:"Miami Beach", data: "Miami Beach" } ];
]]>
</fx:Script>
<mx:ComboBox id="cb" editable="false" width="100" dataProvider="{dp}" />
</s:Application>
该代码看起来是一个 Flex
应用程序的相当标准代码。然而,这里给出的绑定类型是不需要的。你可以使用事件处理程序的直接赋值方式取代绑定方式。
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
minWidth="1024" minHeight="768"
creationComplete="creationCompleteHandler(event)">
<fx:Script>
<![CDATA[
import mx.events.FlexEvent;
public var dp:Array = [ { label:"New York", data: "New York" },
{ label:"Miami Beach", data: "Miami Beach" } ];
protected function creationCompleteHandler(event:FlexEvent):void
{
cb.dataProvider = dp;
}
]]>
</fx:Script>
<mx:ComboBox id="cb" editable="false" width="100" />
</s:Application>
将类与其属性进行绑定
另一个常见错误是将类设置为可绑定,然后也将类的每个属性设置为可绑定。例如:
package
{
[Bindable]
public class CustomerVO
{
[Bindable]
public var customerID:int;
public function CustomerVO(customerID:int)
{
this.customerID = customerID;
}
}
}
CustomerID
属性不需要 [Bindable]
标记,因为该类已经标记为可绑定,这样该类的每个属性均为可绑定。这将会产生编译时间告警(参见图7)并且编写额外代码将会浪费时间。除非你定义了事件名称,否则标记多余的,应该将其删除。
使用不支持属性的双向数据绑定
Flex 4 支持双向数据绑定。 你可以通过在大括号之前添加@
,而让其它字段处于未绑定状态来创建绑定功能。例如,如果你运行下面应用程序,然后在一个 text 字段输入一个值,则该值将在另一个 text 字段显示出来:
<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
minWidth="1024" minHeight="768">
<s:TextInput text="@{someTextInput.text}"/>
<s:TextInput id="someTextInput" y="30"/>
</s:Application>
在大多数情形下,双向数据绑定功能均能够正常工作。然而,它不适用于 style
或 effect
属性。此外,它也不适用于 RemoteObject
的 arguments
属性,或 HttpService、RemoteObject
或 WebService
的 request
属性。
查看下面的代码范例:
<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
minWidth="1024" minHeight="768">
<fx:Script>
<![CDATA[
import mx.rpc.events.FaultEvent;
import mx.rpc.events.ResultEvent;
protected function resultHandler(event:ResultEvent):void
{
// handle result
}
protected function faultHandler(event:FaultEvent):void
{
// handle fault
}
]]>
</fx:Script>
<fx:Declarations>
<s:HTTPService id="service" url="http://localhost/someservice.php"
result="resultHandler(event)"
fault="faultHandler(event)">
<mx:request xmlns="">
<username>@{someTextInput.text}</username>
</mx:request>
</s:HTTPService>
</fx:Declarations>
<s:TextInput text="@{someTextInput.text}"/>
<s:TextInput id="someTextInput" y="30"/>
<s:Button click="service.send()" />
</s:Application>
双向绑定失败的原因是 source 和 target 属性必须是可绑定的并且是可读写的,以便支持双向绑定。