Tree 控件的 DataProvider
可能是因为现在北半球是秋天的缘故,近来我一直在考虑关于树的问题。现在似乎很适合来讨论Flex Tree 控件。因为有很多东西要说,所以我打算针对Flex 2.0 Tree 控件写一系列的文章。在这篇文章中,我将提供一些关于Tree dataProviders, itemRenderers 以及 drag-and-drop 的信息.
Data Providers (数据提供者)
Tree使用的数据是分等级的。就是说,数据有多个等级:分支节点和叶子节点。与之相反的是,DataGrid 和 List 控件使用的数据都不是分等级的。
XMLListCollection 非常适合用来做Tree的dataProvider ,而且它是被用来做dataProvider的最常用的一个类。你也可以使用ArrayCollection,在下文中我会更加详细地说明这一点。
XML原本就是分等级的,所以它很好用。看一下这个简单的XML:
程序代码
[Bindable]
var company:XML =
<node>
<node label="Finance" dept="200">
<node label="John H" />
<node label="Sam K" />
</node>
<node label="Engineering" dept="300">
<node label="Erin M" />
<node label="Ann B" />
</node>
<node label="Operations" dept="400" isBranch="true" />
</node>
在这个XML中你可以很容易地看出树的结构。但是请注意:这个XML中所有的元素都是<node />元素。不管是分支节点还是叶子节点,每个元素都同样地有一个标签和一个名字:”node”。如果你需要一个没有叶子的分支节点,可以使用isBranch="true" 来告诉Tree这个节点是一个分支节点。
Tree和这个XML很相似。你可以将这个XML赋予Tree然后你就能看到它的结构了,这没什么好惊奇的。但是你可能会问:数据中所有的节点都是一样的吗?看下面的XML:
程序代码
[Bindable]
var company:XML =
<list>
<department title="Finance" code="200">
<employee name="John H" />
<employee name="Sam K" />
</department>
<department title="Engineering" code="300">
<employee name="Erin M" />
<employee name="Ann B" />
</department>
<department title="Operations" code="400" isBranch="true" />
</list>
这个XML的结构并不是统一的。如果你将它赋予一个Tree控件你会看到XML堆在控件里,因为没有额外的说明Tree不知道怎么处理它。
这个XML可以通过labelFunction被显示在Tree里。
程序代码
private function treeLabel( item:Object ) : String
{
var node:XML = XML(item);
if( node.localName() == "department" )
return node.@title;
else
return node.@name;
}
LabelFunction只是根据节点的类型简单地返回name或title属性的值。
XMLListCollection
开始的时候我提到了XMLListCollection是一个很好的Tree dataProvider,但是到现在为止我一直在使用XML。下面是为Tree提供数据的适当方法:
程序代码
[Bindable]
var companyList:XMLListCollection = new XMLListCollection( company.department );
...
<mx:Tree dataProvider="{companyList}" labelFunction="treeLabel" />
XMLListCollection可以更好地被用来作为dataProvider,因为Tree可以对它进行操作(编辑,拖放,等)而且XMLListCollection中的改变可以反映到Tree上。这就是说,如果我们改变了这个company XML对象,你不会看到Tree 发生变化。但是如果改变了companyList 这个XMLListCollection,那么就既改变了其中的XML,同时也改变了Tree。
使用XMLListCollection为Tree提供数据,你可以改变这个集合来使得Tree和其中的XML同时改变。
如果你无法为Tree提供一个统一的XML结构,使用 labelFunction(或者itemRenderer,以后会讲到) 为数据显示提供一个标签。
ArrayCollection
你也可以使用ArrayCollection。你可以通过在ArrayCollection里面嵌套ArrayCollection将ArrayCollection变成分等级的:
程序代码
[Bindable]
private var companyData:ArrayCollection = new ArrayCollection(
[ {type:"department", title:"Finance", children:new ArrayCollection(
[ {type:"employee", name:"John H"},
{type:"employee", name:"Sam K"} ] ) },
{type:"department", title:"Engineering", children: new ArrayCollection(
[ {type:"employee", name:"Erin M"},
{type:"employee", name:"Ann B"} ] ) },
{type:"department", title:"Operations", children: new ArrayCollection()}
] );
在这个结构里你会发现一个节点只要有子节点,子节点域的名字就是children –Tree会通过这个来区分分支节点和叶子节点。
对于这个数据你也需要一个labelFunction,这样Tree才知道在每个节点上显示什么:
程序代码
private function treeLabel( item:Object ) : String
{
if( item.type == "department" )
return item.title;
else
return item.name;
}
添加节点
你要通过dataProvider来改变Tree,而不是通过Tree控件自身。当你想要添加一个节点的时候将它添加到dataProvider然后Tree就会自动发生改变。
向XMLListCollection添加节点你需要获得父节点的句柄。例如,要添加一个新的department顶层节点,你可以像下面这样做:
程序代码
var newNode:XML = <department title="Administration" code="500" >
<employee name="Mark C" />
</department>;
companyList.addItem(newNode);
下面展示的是如何向已经存在的Operations department添加一个新的employee节点:
程序代码
var newNode:XML = <employee name="Beth T" />;
var dept:XMLList =company.department.(@title == "Operations");
if( dept.length() > 0 ) {
dept[0].appendChild(newNode);
}
一旦你确定了一个要添加的节点并且获得了它的相应的XML,你就可以使用appendChild()方法来添加它。
向ArrayCollection添加一个节点的时候你只需要在结构中需要该节点的地方添加它就可以了。下面的代码展示了如何添加一个新的department(顶层的)节点:
程序代码
var newNode:Object = {type:"department",title:"Administration"};
var newEmployee:Object = {type:"employee",name:"Mark C"};
newNode.children = new ArrayCollection( [newEmployee] );
companyData.addItem(newNode);
下面展示的是如何向已经存在的Operations department添加一个新的employee节点:
程序代码
var newNode:Object = {type:"employee", name:"Beth T"};
for(var i:Number=0; i < companyData.length; i++) {
var item:Object = companyData.getItemAt(i);
if( item.title == "Operations" ) {
var children:ArrayCollection = item.children;
children.addItem(newNode);
companyData.itemUpdated(item);
empName.text = "";
break;
}
}
正如你看到的那样,使用ArrayCollection来添加节点比使用XML要复杂一点。
删除节点
你可以通过XMLListCollection 的removeItemAt() 方法来删除一个顶层节点,只不过你需要知道该节点的索引。在下面的例子中,你只知道它的名字为“Operations”,所以你需要在节点中循环匹配直到找到该节点,然后就可以删除它了。
程序代码
var deptTitle:String = "Operations";
for(var i:Number=0; i < companyData.length; i++) {
var item:XML = XML(companyData.getItemAt(i));
if( item.@title == deptTitle ) {
companyData.removeItemAt(i);
break;
}
}
删除选中的顶层节点简单一点:
程序代码
var index:Number = tree.selectedIndex;
companyData.removeItemAt(index);
下面是如何删除一个叶子节点:
程序代码
var node:XML = XML(tree.selectedItem);
if( node == null ) return;
if( node.localName() != "employee" ) return;
var children:XMLList = XMLList(node.parent()).children();
for(var i:Number=0; i < children.length(); i++) {
if( children[i].@name == node.@name ) {
delete children[i];
}
}
在ArrayCollection中做删除操作你也许要查询该节点,但是一旦你找到了它你就可以使用ArrayCollection 的 removeItemAt() 方法,因为对于ArrayCollection来说索引总是合法的。
如果Tree的dataProvider是一个ArrayCollection的话,可以这样删除一个叶子节点:
程序代码
var node:Object = tree.selectedItem;
if( node == null ) return;
if( node.type != "employee" ) return;
var children:ArrayCollection = node.parent().children() as ArrayCollection;
for(var i:Number=0; i < children.length; i++) {
if( children[i].name == node.name ) {
children.removeItemAt(i);
break;
}
}
告诉Collection一个Tree的节点然后让Collection删除它是不太可能的——你必须找到它并亲自删除它。
文章转自:http://hi.baidu.com/ppdd521/blog/item/2d0c075284dd030b0ef3e364.html