您的位置:首页 > 其它

创建企业框架-用Flex进行企业开发(一)

2009-08-25 13:13 549 查看
原文来自:http://www.insideria.com/2009/05/chapter-preview-building-an-en.html

内容:程序员在跟宇宙赛跑,他们在努力开发出更大更好的傻瓜程序,而宇宙则努力培养出更大更好的白痴。到目前为止,宇宙领先。
--Rich Cook
简介
从来没有过完美的设计。Flex框架在进步,我们感激Flex团队的软件工程师们让这个框架可扩展。本书涵盖了在企业软件开发中Flex框架的使用,我们将确认和促进这些在商务RIA中广泛应用的元件。
对大部分企业应用程序而言,程序开发主要涉及以下几个活动:
创建数据网格
处理表单
数据有消化
打印
如果你,构架工程师,可以通过将常用的任务自动化,在这些领域快速进步,那应用程序开发员们就可以少花些时间重复地编写同样的代码。关键就是在可重复使用的Flex元件里封装此代码,以创建可以收录到程序库的更“聪明”的元件。
第一章, 回顾了构架框架如Cairngorm, PureMVC 和Mate, 最后选定Flex框架。这些框架主要帮助将代码分成多层,但是现在你将学会怎样通过促进现有的Flex元件,构建另外一种框架。特别是,本章说明了怎样构建一个框架,完全简化数据录入应用的创建:
确认常见的可重复使用的元件,减少在手动敲写代码时不可避免出现的错误;
选定元件内的构架模式的封装;
确定最佳方法,并且在具体的元件中执行,而非纸上谈兵;
你将学会继承一些现有的元件,从最基本的技巧开始,同时扩展简单的CheckBox,然后接触更为复杂的ComboBox元件。本章的后半部分探讨的是拓展每个企业应用都依赖的元件,即DataGrid, Form和Validator。

通过提供结合了程序员、商业分析师、设计师和高端用户的工作框架,你可以迅速简化企业应用程序的开发。

每一位WEB开发员都熟悉Cascading Style Sheets (CSS) 。CSS让设计师可以定义和更改应用程序的外表和感觉,而无需学习编程。当你随着本章逐步学习的时候,Business Style Sheets (BSS) ,作为企业应用开发员所熟悉的角色,可以让软件开发员给一个元件附加远程数据设置,而将编码工作最小化。例如,你会看到一个简单的资源文件怎样指导ComboBox (或任何其它元件) 在哪里获得数据,并且怎样显示数据。把它当成是数据扫描。有了BSS,你可以开发出在企业应用里可高度重复使用的产出物。
在这个过程中,你会了解更多有关BSS和其它促进和自动化Flex元件的技巧。虽然这里,你不能构建整个框架(最后一章探讨了打印和报告的问题),你将开始学会掌握有价值的技能,这些技能是任何Flex构架工程师和元件开发员必备的。
更新现有Flex元件
Flex是作为HTML对象模式的Flash框架进化而来的。它利用了HTML的简明性,是Flex控件的基本集。Flex开发员所要付出的“代价”就是:每一个控件都有其自己的(不同的)属性和行为。这样,构建一个企业框架就成为了挑战。CheckBox控件就是个例子。
为了快速把CheckBox并入不同的框架,开发员希望元件具有统一的属性值(on或off),可轻松地绑定到应用数据。现在,Flex的CheckBox具有一个属性为“选定”。开发员需要编写代码将Yes/No数据转换成选定的属性所期待的真或假。如果你随后使用另外一个控件,你必须再将这些Yes/No值转换成新的控件所要求的表单。很明显,一些共同点会减少冗余编码。

下面的章节会进一步探讨CheckBox 及其它每个应用程序都需要的主要的Flex元件,指出它们的缺憾,以及改进的方法:
元件程序库 clear.swc
你可能记得在第一章选定Flex框架的比较里,Clear Toolkit的元件程序库 clear.swc包含了许多强化的Flex元件(Figure 3.1, “The com.farata.components package from clear.swc”). 特别是,该元件程序库由三部分组成:
com.farata.components
com.farata.grid
com.farata.printing
为了展示怎样拓展元件,在下面的章节里,会说明我们是怎样从package com.farata.component里构建一些元件。如果你决定创建一个相似(或更好)的元件程序库,这些可给你提供一些参考。
将clear.swc与你的Flex项链接,你就可以独立使用clear.swc了。为了让你更好地了解其元件对你的帮助,下面将检测程序库一些控件的简化版:
创建价值识别CheckBox
Example 3.1 里的CheckBox, “具有值性和文本属性的CheckBox” 已经添加了额外的值性和文本属性进行强化。你可以确定要触发的值,以调制此控件的开/关。

package com.farata.controls {
import flash.events.Event;
import flash.events.KeyboardEvent;
import flash.events.MouseEvent;

import mx.controls.CheckBox;
import mx.events.FlexEvent;

public class CheckBox extends mx.controls.CheckBox {

public var onValue:Object=true;
public var offValue:Object=false;
private var _value:*;

public function set text(o:Object):void {
value = o;
}
public function get text():Object {
return value;
}

[Bindable("valueCommit")]
public function set value(val:*) :void {
_value = val;
invalidateProperties();
dispatchEvent(new FlexEvent (FlexEvent.VALUE_COMMIT));
}

public function get value():Object  {
return selected?onValue:offValue;
}

override protected function commitProperties():void {
if (_value!==undefined)
selected = (_value == onValue);
super.commitProperties();
}
}
}


CheckBox将自动设置成“选定”或“非选定”状态:添加到你的视图里,设定ON/OFF值,赋字符串或对象值。请注意值设定项调用函数invalidateProperties(), 该函数内部安排在下一个UI更新调度程序调用函数commitProperties()。
commitProperties()函数可以让你一下子就改变一个元件的所有属性。这就是为什么我们在该函数里根据_value和 onValue的比较结果设定选定属性的值。
Example 3.1, “价值识别CheckBox的测试应用” 是一个测试应用程序,说明怎样采用Figure 3.2, 使用CheckBox在“Testing the value-aware CheckBox”里显示的结果。为了运行测试,点击第一个Set OnValue= button,让CheckBox在赋Male 值的时候打开;在属性文本的值是Female的时候关闭。然后,点击第一个或者第二个cbx_test.text 按钮赋值给该CheckBox新引入的属性文本,观察它的状态怎么变化。
Example 3.2.价值识别CheckBox的测试应用

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
xmlns:clear="com.farata.controls.*" layout="vertical">

<clear:CheckBox id="cbx_test" label="Assign me a value" />

<mx:Button label="Set OnValue='Male' and offValue='Female'"
click="cbx_test.onValue='Male';cbx_test.offValue='Female';"/>

<mx:Button label="cbx_test.text='Male'" click="cbx_test.text='Male'" />
<mx:Button label="cbx_test.text='Female'" click="cbx_test.text='Female'" />

<mx:Button label="Set OnValue=Number('1') and offValue=Number('0')"
click="cbx_test.onValue=Number('1');cbx_test.offValue=Number('0');"/>

<mx:Button label="cbx_test.value='Number('1')'"
click="cbx_test.value =new Number('1')" />
<mx:Button label="cbx_test. value='Number('0')"
click="cbx_test.value =new Number('0')" />

</mx:Application>


Figure 3.2.价值识别CheckBox的测试

创建中心的CheckBox
该范例展示了怎样在任何容器里,包括数据网格单元里,创建一个可以以自己为中心的CheckBox(水平方向)。
尽管你可以引入一个使用HBox内的CheckBox的条目渲染器,设置horizontalAlign的风格为“center”, 使用条目渲染器内的容器对数据网格控件的性能有负面影响。
更好的办法是:拓展CheckBox自身的风格。这里是一个代码扩充,“教导”标准的Flex CheckBox对textAlign风格做出反应,如果CheckBox的labelproperty未定义的话:
Example 3.3. CheckBox的自定心解决方案

override protected function updateDisplayList(unscaledWidth:Number,
unscaledHeight:Number):void {

super.updateDisplayList(unscaledWidth, unscaledHeight);
if (currentIcon) {
var style:String = getStyle("textAlign");
if ((!label) && (style=="center") ) {
currentIcon.x = (unscaledWidth - currentIcon.measuredWidth)/2;
}
}
}


在上面的代码里,CheckBox图标的x座标将位于封闭容器的中心。因为不会引入额外的容器,你可以使用具备风格选择功能(style selector)的DataGridColumn条目渲染器。当你使用这个强化了的CheckBox做为数组条目渲染器,textAlign 就会自动改变为该风格选择器的一个风格,而你可以简简单单地设定set textAlign=true on DataGridColumn。

当为企业商务框架开发强化元件时,重点是确认应用程序开发商经常需要的可重复使用的功能,立即编写程序,将之加入元件本身。
创建受保护的CheckBox
标准的Flex CheckBox有一个Boolean属性,当你想要禁用控件时,非常便捷。不过,禁用CheckBox被视为“变灰”。如果你想在一些不可编辑的容器里使用CheckBox,比如DataGridColumnand里,你想让它不可更新,但是看上去正常,那该怎么办呢?
答案就是:使用一个新的类,CheckBoxProtected。它包含一个额外的可更新属性,窍门就是取消标准键盘和鼠标点击处理。添加:
if (!updateable) return;
可覆写事件处理函数;
很灵喔!
Example 3.4, “Class CheckBoxProtected” 列出了完整源代码:
Example 3.4. Class CheckBoxProtected

package com.farata.controls
{
import flash.events.Event;
import flash.events.KeyboardEvent;
import flash.events.MouseEvent;
import mx.controls.CheckBox;

public class CheckBoxProtected extends mx.controls.CheckBox {

public var updateable:Boolean = true;

public function CheckBoxProtected() {
super();
addEventListener(MouseEvent.CLICK, onClick);
}
private function onClick (event:MouseEvent):void {
dispatchEvent(new Event(Event.CHANGE));
}
override protected function keyDownHandler(event:KeyboardEvent):void {
if (!updateable) return;
super.keyDownHandler(event);
}
override protected function keyUpHandler(event:KeyboardEvent):void {
if (!updateable) return;
super.keyUpHandler(event);
}
override protected function mouseDownHandler(event:MouseEvent):void {
if (!updateable)return;
super.mouseDownHandler(event);
}
override protected function mouseUpHandler(event:MouseEvent):void {
if (!updateable)return;
super.mouseUpHandler(event);
}
override protected function clickHandler(event:MouseEvent):void {
if (!updateable)return;
super.clickHandler(event);
}
}
}


测试受保护的CheckBox,使用Example 3.5, “Test application for CheckBoxProtected”.
Example 3.5. Test application for CheckBoxProtected

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
xmlns:clear="com.farata.controls.*" layout="vertical">

<clear:CheckBoxProtected updateable="false"
label="I am protected" fontSize="18"/>
<mx:CheckBox enabled="false"
label="I am disabled" fontSize="18"/>

</mx:Application>


运行应用程序,会产生Figure 3.3, “Running CheckBoxProtectedApp” 所列结果。它显示了受保护和禁用checkboxes之间的区别。
Figure 3.3. Running CheckBoxProtectedApp

为什么不充分使用Flex框架的可拓展性呢?本章节讨论的是使用Flex元件你能够做什么。了解了这些,你将决定如何来使用这些元件。
例如,CheckBox的三态。底层的数据可能是Yes, No, 和 Null。如果值是Null (第三态),CheckBox 需要显示一个不同的图像,如里面是个小小的问号。除了支持三态(选定,未选定和null)外,控件应该支持从一个状态到另外一个状态的轻松转换。此强化方案包括一个skinning任务-在Photoshop里创建一个新的皮肤(带一个问号),确保控件根据底层数据转换到此状态。如需实例,参见CheckBox3Stated in the clear.swc component library。
更新ComboBox
CheckBox是最容易强化的,因为它是最简单的控件之一,只有两个状态(on或off)。你可以将同样的原则施用于更高级的ComboBox。确认可重复使用的功能,立即编写程序,将之加入元件本身。
如果你需要请求在ComboBox里选定一个特定的值,传统的做法就是编写在ComboBox数据提供者里轮巡条目表的代码,并且手动处理selectedIndex属性。设定Texas 为渲染状态的ComboBox的一个选定值,你可使用:

var val:String; val= 'Texas' ;
for (var i: int = 0; i < cbx.dataProdider.length; i++) {
if ( val == cbx_states.dataProvider[i].label) {
cbx_states.selectedIndex = i;
break;
}
}


它看上去不像风格吗?你也可以通过如,改变Y/N的on/off值为Д./Н(在俄文里是Да/Нет)或者西班牙语的Si/No,轻松地将它设为本地特性。当你把此类资源看做独立于应用程序组件的实体时,你开始看到了此技术的灵活性。这个功能是不是和CSS相似?
实际上,它比CSS更为成熟,因为该资源是风格和属性的混合,如Example 3.9, “StateComboBoxResource with hard-coded states”所示。名为StateComboBoxResource.mxml,它显示了在BSS里对属性的利用(如dataProvider)。这样一种资源可包含一个值列表,如名称和状态缩写:
Example 3.9. StateComboBoxResource with hard-coded states

<?xml version="1.0" encoding="utf-8"?>
<fx:ComboBoxResource
xmlns="com.farata.resources" xmlns:mx="http://www.adobe.com/2006/mxml"
xmlns:resources="com.theriabook.resources.*"
dropdownWidth="160"
width="160"
>
<fx:dataProvider>
<mx:Array>
<mx:Object data="AL" label="Alabama" />
<mx:Object data="AZ" label="Arizona" />
<mx:Object data="CA" label="California" />
<mx:Object data="CO" label="Colorado" />
<mx:Object data="CT" label="Connecticut" />
<mx:Object data="DE" label="Delaware" />
<mx:Object data="FL" label="Florida" />
<mx:Object data="GA" label="Georgia" />
<mx:Object data="WY" label="Wyoming" />
</mx:Array>
</fx:dataProvider>
</fx:ComboBoxResource>


另外一个资源范例 Example 3.10, “Sample DepartmentComboResource configured for a remote destination” 包含了远程目的指引,以自动检索来自DBMS的动态数据。
Example 3.10. Sample DepartmentComboResource configured for a remote destination

<?xml version="1.0" encoding="utf-8"?>
<fx:ComboBoxResource
xmlns="com.farata.resources" xmlns:mx="http://www.adobe.com/2006/mxml"
xmlns:resources="com.theriabook.resources.*"
width="160"
dropdownWidth="160"
destination="Employee"
keyField="DEPT_ID"
labelField="DEPT_NAME"
autoFill="true"
method="getDepartments"
>
</fx:ComboBoxResource>


事实上,你无法从此代码判断数据来自于 DBMS 或来自其他地方。。那样的数据明显和与这一特别资源相连的ComboBox对象的实例相分离,可以全局缓存(如果数据需要一次性检索到)或者依据框架缓存标准。在开发一个商业框架时,你可就每个应用程序或者每个视图上载一次查询对象。在基于单例的架构框架里不存在这样的灵活性。然而,利用资源技术/BSS创建框架则具备查询对象的灵活性。

基于此资源文件,你只能说数据从远程目的Employee(类的名称或类厂)处回来了。你也可以看到方法getDepartments()会返回包含DEPT_ID 和DEPT_NAME(与本章前半部分所述的强化ComboBox一起使用)的数据 (Example 3.6, “Class com.farata.control.ComboBoxBase”)。

然而除了这些资源外,你需要一个机制将它们附加到Flex UI元件上。为了指导
ComboBox 处理此类资源,添加一个资源属性:

private var _resource:Object;
public function get resource():Object
{
return _resource;
}

public function set resource(value:Object):void {
_resource = value;
var objInst:*  = ResourceBase.getResourceInstance(value);
if(objInst)
objInst.apply(this);
}


基类“The Base Class for Resources”小节将详细探讨ResourceBase类。现在,我们的重点是,资源属性可以让你编写:

<fx:ComboBox resource="{DepartmentComboResource}
在你的框架里的每一个强化了的UI元件应该包括此类属性。因为接口不允许默认实现此类setter和getter,而且ActionScript不支持MI多继承。包含资源属性的默认实现的最简单的方法是:通过利用语言编译时期指令(包括外部文件,如resource.as,的内容),包含到你的元件代码里:
#include "resource.as"
风格 VS. 属性
进一步深入BSS和资源方式之前,你需要了解风格和属性之间的一些关键区别。譬如,尽管简单的点标志(myObject.resource=value) 是属于有效的Flex语法,它不适用于风格。应用程序员们不得不使用函数setStyle()。StyleManager处理级联式风格,而属性不能级联。从框架程序员的观点来说,属性允许getters和setters定义类,并且利用了继承的方法。但是你不能对风格进行此操作。另外一方面,你不能给风格添加属性(即值和目的)。
Flex框架的设计师们为了更简便的操作内部流程,将风格从属性里分立出来――如果应用程序代码改变了风格,Flex框架执行一些秘密任务以确保级联的风格得到了适当地应用;例如,给Verdana font family发出指令的全局风格被应用于Panel或其子实例的风格所覆盖。
从企业框架设计师的角度来说,这意味着如果你为风格创建了一个基类,随后决定改变它,这种改变可能会影响所有派生类。假设你划分ComboBox的子类,定义了派生MyComboBox里的一些新风格,然后你改变了ComboBox的风格。对于子代类而言,这就意味着需要新的代码更改以恰当地(根据更改了的规则)应用被重写和添加的风格。
所有这些说明为什么所有的书和产品手册都警告风格是“昂贵“的,你应该限制在运行时setSyle()函数的使用。有了属性,一切简单多了。
有利的框架能让应用程序员定义特殊应用风格和属性的小命名集,以及使用选择器管理UI控件的工作。
为此,进入DataGrid。
你是否想过DataGridColumn对象是怎样设定宽和高及其它值的?DataGridColumn类是一个风格选择器CSSStyleSelector的子代。这意味着可以用它来修改风格,而不是属性。
DataGrid检测每个DataGridColumn,并询问“在我的缓存里,我是否有和这个列对象一样的?”如果答案是否,它回答,“不,没有我可以重复使用的。我需要创建一个新的类工厂以提供一个新的条目渲染器。”完成这个任务后,DataGrid 代码将提供的DataGridColumn作为风格分配给条目渲染器。(自己搜索以DataGridBase.as为代码的forrenderer.styleName=c)。在这点,特定栏的所有风格(高、宽、颜色、文本对齐)被做为风格应用到条目渲染器上。

把DataGridColumn 当成一个也包含有限定数量属性(如条目渲染器)的CSS风格选择器。DataGrid创建一个这样的选择器对象的实例,然后重新应用于该栏里的CELL单元。
不过,以这种方法设计DataGrid使得外部化这个CSS风格选择器几乎成为不可能;并且你也不能拓展数据网格栏的属性,以使得它们成为条目渲染器的特定属性。假定你想使用有属性值(on/off)的CheckBox做为条目渲染器。不好!--DataGridColumn不是动态对象,你不能仅仅就是添加这个作为新属性。
然而,Flex是一个可拓展的框架,你能添加的是一个具有你更喜欢的行为的新资源类。实际上,这就是ResourceBase类的功能,下一步我们会进行说明。

这个方法的不利之面就是:如果你的应用程序有了50个ComboBox控件,开发员们可能会编写类似的循环程序,而不是单行,如cbx_states.value="Texas"。
但是,ComboBox并不提供包含选定值的特定属性。它包含有此类属性如labelField, selectedIndex, 和selectedItem。它们中的哪个才是数据场?怎么通过值搜索?你真的在意ComboBox里选定行是哪行?不是。你需要了解选定值。
让我们再看下上面的代码段。ComboBox 的labelField从存储在后备存储器里的对象上得知属性的名称。但是与此标签通讯的数据场又是怎样呢(如Texas, TX 是一个很适合的ComboBox数据)?现在,找出此类数据是应用程序员的一个任务。

即使你不在意这些循环程序,考虑到填充数据提供者的异步性,此代码需要等到来自服务器数据的到来。如果你仅仅是赋值到一个ComboBox,也是可以的,你也无需担心事件的异步流。

考虑列表控制,ComboBox的同伴。譬如,用户选定五个条目,然后决定过滤备份数据存储器。用户的选择会丢失。列表同时也要求另外一种属性,可记忆选定的值,并且在使用时无需担心数据达到时间。
Example 3.6, “Class com.farata.control.ComboBoxBase” 提供了解决方案ComboBoxBase类(通过添加值拓展ComboBox,勿混淆)。在引入值性后,它利用数据场的属性告知ComboBox与该值通讯的底层数据存储器的对象里的数据场的名称。新的数据场属性可以让你把任何专用对象属性做为ComboBox数据。
你还会注意到另一个更公开的属性:keyField。从技术上讲,keyField是数据场dataField的同义词。你可以利用keyField来避免在其它也有属性为dataField的对象(如,DataGridColumn)里使用ComboBoxBase或其子类时命名的冲突。
Example 3.6. Class com.farata.control.ComboBoxBase
package com.farata.controls {
import flash.events.Event;

import mx.collections.CursorBookmark;
import mx.collections.ICollectionView;
import mx.collections.IViewCursor;
import mx.controls.ComboBox;
import mx.controls.dataGridClasses.DataGridListData;
import mx.controls.listClasses.ListData;
import mx.core.mx_internal;
use namespace mx_internal;

public class ComboBoxBase extends ComboBox {

public function ComboBoxBase() {
super();
addEventListener("change", onChange);
}

// Allow control to change dataProvider data on change
private function onChange(event:Event):void {
if (listData is DataGridListData) {
data[DataGridListData(listData).dataField] = value;
}else if (listData is ListData && ListData(listData).labelField
in data) {
data[ListData(listData).labelField] = value;
}
}

protected function applyValue(value:Object):void {
if ((value != null) && (dataProvider != null)) {
var cursor:IViewCursor = (dataProvider as
ICollectionView).createCursor( );
var i:uint = 0;
for (cursor.seek( CursorBookmark.FIRST ); !cursor.afterLast;
cursor.moveNext(), i++) {
var entry:Object = cursor.current;
if ( !entry ) continue;
if ( (dataField in entry && value == entry[dataField])) {
selectedIndex = i;
return;
}
}
}
selectedIndex = -1;
}

private var _dataField:String = "data";
private var _dataFieldChanged:Boolean = false;

[Bindable("dataFieldChanged")]
[Inspectable(category="Data", defaultValue="data")]

public function get dataField():String { return _dataField; }
public function set dataField(value:String):void {
if ( _dataField == value)
return;

_dataField = value;
_dataFieldChanged = true;
dispatchEvent(new Event("dataFieldChanged"));
invalidateProperties();
}

public function get keyField():String { return _dataField; }

public function set keyField(value:String):void {
if ( _dataField == value)
return;
dataField = value;
}

private var _candidateValue:Object = null;
private var _valueChanged:Boolean  = false;

[Bindable("change")]
[Bindable("valueCommit")]
[Inspectable(defaultValue="0", category="General", verbose="1")]

public function set value(value:Object) : void {
if (value == this.value)
return;

_candidateValue = value;
_valueChanged = true;
invalidateProperties();
}

override public function get value():Object {
if (editable)
return text;

var item:Object = selectedItem;

if (item == null )
return null;

return dataField in item ? item[dataField] : null/*item[labelField]*/;
}

override public function set dataProvider(value:Object):void {
if ( !_valueChanged ) {
_candidateValue = this.value;
_valueChanged = true;
}
super.dataProvider = value;
}

override public function set data(data:Object):void {
super.data = data;
if (listData is DataGridListData) {
_candidateValue = data[DataGridListData(listData).dataField];
_valueChanged = true;
invalidateProperties();
}else if (listData is ListData && ListData(listData).labelField
in data) {
_candidateValue = data[ListData(listData).labelField];
_valueChanged = true;
invalidateProperties();
}
}

override protected function commitProperties():void {
super.commitProperties();
if (_dataFieldChanged) {
if (!_valueChanged && !editable)
dispatchEvent( new Event(Event.CHANGE) );

_dataFieldChanged = false;
}

if (_valueChanged) {
applyValue(_candidateValue);
_candidateValue = null;
_valueChanged = false;
}
}

public function lookupValue(value:Object, lookupField:String = null):Object {
var result:Object = null;
var cursor:IViewCursor = collectionIterator;
for (cursor.seek(CursorBookmark.FIRST);!cursor.afterLast;cursor.moveNext()) {
var entry:Object = cursor.current;
if ( value == entry[dataField] ) {
result = !lookupField ? entry[labelField] : entry[lookupField];
return result;
}
}
return result;
}
}
}


新的属性值根据下面的属性值设定函数设定赋予:

[Bindable("change")]
[Bindable("valueCommit")]
[Inspectable(defaultValue="0", category="General", verbose="1")]
public function set value(value:Object) : void {
if (value == this.value)
return;

_candidateValue = value;
_valueChanged = true;
invalidateProperties();
}


注意当函数开始改变flag _value时,无效的Properties ()会内部安排调用方法commitProperties() 以确保所有的改变都会应用在所要求的序列中。在本例中,commitProperties()函数里的代码确保数据场的值在值性有明确改变(如有)之前就会得到处理。
ComboBox是异步控件,可通过服务器端调用予以填充。当你向值性赋数据时,不能确保远程数据会准时达到。在设值器里的_candidateValue是一个临时的支持性推迟赋值,采用方法commitProperties()。

函数commitProperties() 通知值已被改变(如果其它应用程序对象与该值绑定),并且向方法applyValue()传递_candidateValue:

override protected function commitProperties():void {
super.commitProperties();
if (_dataFieldChanged) {
if (!_valueChanged && !editable)
dispatchEvent( new Event(Event.CHANGE) );

_dataFieldChanged = false;
}

if (_valueChanged) {
applyValue(_candidateValue);
_candidateValue = null;
_valueChanged = false;
}
}


方法applyValue()使用IViewCursoriterator在dataProvider里轮巡收集系统。当此代码在数据收集系统(具备在数据场里指定的属性,其值与该函数的引数相同)找到对象,它将此行标志为“选定”:

protected function applyValue(value:Object):void {
if ((value != null) && (dataProvider != null)) {
var cursor:IViewCursor = (dataProvider as
ICollectionView).createCursor( );
var i:uint = 0;
for (cursor.seek( CursorBookmark.FIRST ); !cursor.afterLast;
cursor.moveNext(), i++) {
var entry:Object = cursor.current;
if ( !entry ) continue;
if ( (dataField in entry && value == entry[dataField])) {
selectedIndex = i;
return;
}
}
}
selectedIndex = -1;
}
Tags such as
[Inspectable(defaultValue="0",category="General", verbose="1")]


以确保相应的属性会出现在Flex Builder的设计模式里的ComboBoxBase的属性对话框里(在本例中,是在General目录下,特定初始值indefaultValue和verbose)。Metatags 如[Bindable("dataFieldChanged")]确保一旦dataField值改变,dataField 更改事件会被发送(至关注者)。
小应用程序TestComboBoxApp.mxml 显示对ComboBoxBasecomponent的使用。
Example 3.7. Using the ComboBoxBase component

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
xmlns:clear="com.farata.controls.*" layout="vertical">
<mx:ArrayCollection id="cbData">
<mx:Array>
<mx:Object label="Adobe" data="ADBE" taxID="1111"/>
<mx:Object label="Microsoft" data="MSFT"  taxID="2222"/>
<mx:Object label="Farata Systems" data="FS"  taxID="3333"/>
</mx:Array>
</mx:ArrayCollection>

<clear:ComboBoxBase  dataProvider="{cbData}" value="FS"/>

<clear:ComboBoxBase  dataProvider="{cbData}" dataField="taxID" value="3333"/>

</mx:Application>


两个下拉框使用相同的dataProvider。当运行Example 3.7, “Using the ComboBoxBase component”'的应用程序时,你会看到一个与Figure 3.4, “Running an application with two ComboBoxBase components” 里相似的窗口。窗口:
Figure 3.4. Running an application with two ComboBoxBase components

第一个ComboBoxBase 显示Farata System,因为赋值="FS" 将之比较了来自cbData collection 收集系统的对象的数据场内的值。
第二个下拉框设定dataField="taxID" ,指导ComboBox利用底层数据收集系统内taxID属性的值。如果代码将赋一个新值totaxID, 即,外部数据更新,ComboBox内的选择会相应改变。它更好地与现实的情况联系起来了。在现实的情况里,来自服务器的具备多项属性的DTO收集系统到达,与一个或多个ComboBox控件一起使用,而ComboBox控件可能把不同的DTO属性作为它们的数据。
作为UI控件属性的资源
如需强化元件以更好地支持你的企业框架,一个更为灵活的解决方案就是使用一种我们称之为data styling或Business Style Sheets (BSS)的编程技术。基本的过程就是:创建叫做资源(resources)的小文件,然后将它们做为属性附加到通常的UI元件和DataGrid column上。
Example 3.8, “A CheckBox resource” 显示了此BSS 技术 ,并包含了一个小MXML文件叫YesNoCheckBoxResource.mxml:
Example 3.8. A CheckBox resource

<?xml version="1.0" encoding="utf-8"?>
<fx:CheckBoxResource
xmlns="com.farata.resources" xmlns:mx="http://www.adobe.com/2006/mxml"
xmlns:resources="com.theriabook.resources.*"
offValue = "N"
onValue = "Y"
textAlign="center"
>

</fx:CheckBoxResource>
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐