65.9K
CodeProject 正在变化。 阅读更多。
Home

Flex 数据绑定技巧

starIconstarIconstarIconstarIconemptyStarIcon

4.00/5 (2投票s)

2010年2月2日

CPOL

4分钟阅读

viewsIcon

47042

downloadIcon

342

本文介绍了在 Flex 应用程序中使用数据绑定的用法。

引言

不久前,在开发一个 Flex 客户端应用程序项目时,我发现了 Flex 数据绑定(我认为任何开始使用 Flex 的人很快都会遇到数据绑定)。现在,在我对这方面有了一些了解后,我想分享一些技巧,让初学者更容易在 Flex 应用程序中利用数据绑定。

通过使用数据绑定,我们可以轻松地在对象(数据源)之间建立链接,这使我们能够使它们保持同步。数据绑定可以使用的领域之一是用户界面元素之间的链接,目的是提供更具交互性的 UI。

MXML 中的简单数据绑定

假设我们有两个文本字段

<mx:TextInput id="sourceField" text="" />
<mx:TextInput id="destinationField" text="" />

我们希望在第一个输入框中进行的所有更改都能显示在第二个输入框中。为了实现这一点,可以使用以下 mxml 代码

<mx:Binding destination="destinationField.text" source="sourceField.text"/>

结果是,在第一个文本输入框中键入的文本将自动放入第二个文本输入框中。这就是简单数据绑定的含义。使用数据绑定的简单应用程序代码如下

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute">
   <mx:Binding destination="destinationField.text" source="sourceField.text"/>
   <mx:VBox>
      <mx:TextInput id="sourceField" text="" />
      <mx:TextInput id="destinationField" text="" />
   </mx:VBox>
</mx:Application>

ActionScript 中的简单数据绑定

上面的示例很容易转换为 ActionScript。我们在这里获得的优势是,我们可以将数据绑定用于动态创建的元素。我们有两个相同的文本字段

public var sourceField : TextInput = new TextInput(); 
public var destinationField : TextInput = new TextInput();

数据绑定看起来会像这样

BindingUtils.bindProperty(destinationField, "text", sourceField, "text");

第一个 bindProperty 函数的参数是目标对象,第二个参数是目标对象的属性名,第三个参数是源对象,第四个参数是链对象,在简单情况下,它表示源对象的属性名(我们稍后将更详细地介绍此参数)。这是应用程序代码

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute"
initialize="init()">
   <mx:Script>
      <![CDATA[
         import mx.controls.TextInput;
         import mx.binding.utils.BindingUtils;
      
         public var sourceField : TextInput = new TextInput();
         public var destinationField : TextInput = new TextInput();
         public function init():void
         {
            sourceField.text = "";
            parentContainer.addChild(sourceField);
            
            destinationField.text = "";
            parentContainer.addChild(destinationField);
            
            BindingUtils.bindProperty(destinationField, "text", sourceField, "text");
         }               
      ]]>
   </mx:Script>
   <mx:VBox id="parentContainer"/>
</mx:Application>

bindProperty 方法中的链对象

此对象用作 BindingUtils.bindProperty 函数的第四个参数。它描述了数据绑定应该如何以及应用于源对象的哪个参数。此对象可以表示为三种形式:字符串、数组和复合对象。

我们已经使用的字符串表示法包含源对象的属性名。

数组表示法保存了访问源对象内部属性的层级结构。为了理解它的用法,让我们创建一个使用两个对象的示例

package src
{
   public class SimpleObject
   {
      public var myText:String = new String();
      public function SimpleObject():void
      {
         myText = "empty";
      }
   }
}
 
package src
{
   public class ComplexObject
   {
      public var simpleInstance: SimpleObject = new SimpleObject();
      public function ComplexObject():void
      {
         //some logic
      }
   }
}

...

public var destinationField : TextInput = new TextInput();
public var complexInstance : ComplexObject = new ComplexObject();

所以我们有一个 ComplexObject 类实例,我们希望将 myText 字段(位于内部 SimpleObject 对象类中)与 TextInput 对象的 text 属性连接起来。也就是说,我们希望将 complexInstance.simpleInstance.myText 绑定到 destinationField.text。我们将使用数组作为链对象来实现这一点

 BindingUtils.bindProperty(
		destinationField, 
		"text", 
		complexInstance, 
		["simpleInstance","myText"]);

Flex 将使用点连接数组数据,并绑定到 complexInstance.simpleInstance.myText 属性。使用数组类型链的示例应用程序将如下所示

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute"
initialize="init()">
   <mx:Script>
      <![CDATA[
         import src.ComplexObject;
         import mx.controls.TextInput;
         import mx.binding.utils.BindingUtils;
      
         public var destinationField : TextInput = new TextInput();
         public var complexInstance :  ComplexObject = new ComplexObject();
         public function init():void
         {
            destinationField.text = "";
            parentContainer.addChild(destinationField);
            
            BindingUtils.bindProperty(destinationField, 
				"text",
				complexInstance, 
				["simpleInstance","myText"]);
         }
      ]]>
   </mx:Script>
   <mx:VBox id="parentContainer"/>
</mx:Application>

在复合对象情况下,链表示为

var object : Object = {name:<property name>, getter: <value receiver function>};

其中 *属性名* 是一个 string,包含源对象的属性名,*值接收器函数*

function (host:<object of a departure type >) : <type of a return value>
{
	// function body, where we can query the host parameter
	// since we know this is an object, used for departure
}

假设我们有一个 ArrayCollection 类实例和一个文本字段。

public var array : ArrayCollection = new ArrayCollection();
public var destinationField : TextInput = new TextInput(); 

当集合中的对象数量超过 10 个时,我们将禁用文本字段,否则启用它。

BindingUtils.bindProperty(
		destinationField, 
		"enabled", 
		array, 
		{
			name:"length",
			getter : function (host : ArrayCollection):Boolean 
						{ return host.length<10; }
		});

这是使用复合对象作为链的完整应用程序代码

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute"
initialize="init()">
   <mx:Script>
      <![CDATA[
         import mx.collections.ArrayCollection;
         import mx.controls.TextInput;
         import mx.binding.utils.BindingUtils;
      
         public var destinationField : TextInput = new TextInput();
         [Bindable]
         public var array : ArrayCollection = new ArrayCollection();
         public function init():void
         {
            destinationField.text = "some text";
            parentContainer.addChild(destinationField);
            
            BindingUtils.bindProperty(
				destinationField, 
				"enabled", 
				array, 
				{
					name:"length",
					getter : function (host : 
						ArrayCollection):Boolean 
						{ return host.length>=10; }
				});
         }
      ]]>
   </mx:Script>
   <mx:VBox id="parentContainer">
      <mx:Button label="add element" click="array.addItem(0);"/>
      <mx:Text text="array length: {array.length}"/>
   </mx:VBox>
</mx:Application>

BindingUtils 类中的 bindSetter 方法

数据绑定的另一种用法是,在源对象的属性更改时调用回调函数。假设我们有一个集合和一个文本字段。

public var destinationField : TextInput = new TextInput();
public var array : ArrayCollection = new ArrayCollection();

集合包含从外部添加到集合的数字。我们需要在文本字段中显示所有集合元素的总和(并且我们需要保持此总和的最新状态)。这意味着我们需要在集合长度改变时重新计算元素的总和。为了实现这一点,我们将使用 BindingUtils 类的 bindSetter 方法

BindingUtils.bindSetter(calculateSum , array, "length");

其中 calculateSum 将是一个函数

function ( input : <departure>):void
{
	//function contents
}

在我们的例子中,calculateSum 将包含以下内容

public function calculateSum(input : Number):void
{
	var sum:Number = 0;
	for (var i:int = 0; i<input; i++)
	{
		sum += array[i];
	}
	destinationField.text =  sum.toString();
}

这是用于回调函数用法的应用程序代码

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute"
initialize="init()">
   <mx:Script>
      <![CDATA[
         import mx.collections.ArrayCollection;
         import mx.controls.TextInput;
         import mx.binding.utils.BindingUtils;
      
         public var destinationField : TextInput = new TextInput();
         [Bindable]
         public var array : ArrayCollection = new ArrayCollection();
         public function init():void
         {
            destinationField.text = "some text";
            parentContainer.addChild(destinationField);
            
            BindingUtils.bindSetter(calculateSum , array, "length");
         }
         
         public function calculateSum(input : Number):void
         {
            var sum:Number = 0;
            for (var i:int = 0; i<input; i++)
            {
               sum += array[i];
            }
            destinationField.text =  sum.toString();
         }
 
      ]]>
   </mx:Script>
   <mx:VBox id="parentContainer">
      <mx:Button label="add element" click="array.addItem(Math.random());"/>
      <mx:List width="100" height="200" dataProvider="{array}"/>
   </mx:VBox>
</mx:Application>

将 bindSetter 与复合链对象结合使用

这是最复杂的数据绑定类型,但很容易理解。假设我们需要编写一个类,该类收集有关被观察按钮组件的宽度变化的 数据。首先,我们编写类本身

package src
{
   import mx.collections.ArrayCollection;  
   public class Collector
   {
      public var array : ArrayCollection;      
      public function Collector():void
      {
         array = new ArrayCollection();
      }      
      public function collect(str:String):void
      {
         array.addItem(str);
      }
   }
}

以及一个 return 函数,该函数准备按钮的数据

public function returnInfo(host:UIComponent):String
		{
           return "width:" + host.width + "; height:" + host.height;
        }

现在,使用 bindSetter 方法,我们将定义当按钮组件的 width 属性发生变化时,将调用 returnInfo 函数

BindingUtils.bindSetter(collector.collect, button, 
		{name:"width", getter: returnInfo});

这是演示复合链对象用法的应用程序

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute"
initialize="init()">
   <mx:Script>
      <![CDATA[
         import mx.core.UIComponent;
         import mx.binding.utils.BindingUtils;
         import src.Collector;
         public var collector : Collector = new Collector();
         
         public function init():void
         {
            BindingUtils.bindSetter(collector.collect, button, 
            		{name:"width", getter: returnInfo});
            data.dataProvider = collector.array;
         }
         
         public function returnInfo(host:UIComponent):String
         {
            return "width:" + host.width + 
            	"; height:" + host.height;
         }
 
         public function changeButton():void
         {
            button.width = Math.round(Math.random() * 200);
            button.height = Math.round(Math.random() * 100);   
         }
      ]]>
   </mx:Script>
   <mx:VBox>
   <mx:Button label="change" click="changeButton();"/>
   <mx:List id="data" width="200" height="400" />
   <mx:Button id="button" width="200" height="100" label="mybutton"/>
   </mx:VBox>
</mx:Application>

returnInfo 函数准备按钮 width 的数据,然后这些数据会自动传递到 collector.collect() 函数,并在那里进行适当的处理。

历史

  • 2010 年 2 月 2 日:初始发布
© . All rights reserved.