带有ComboBox细胞的DataGrid

来源:互联网 发布:网络摄像头焦距 编辑:程序博客网 时间:2024/05/02 06:12

Adobe Flash Builder 4 简体中文正式版 下载: http://subject.csdn.net/adobedownload/index.html

Adobe平台技术峰会课程视频:http://adobev.csdn.net/

 

本文译自:http://www.actionscript.org/resources/articles/993/1/DataGrid-with-ComboBox-cells/Page1.html

我曾经是一名微软培训师,现在是瑞典的一家大型咨询公司的解决方案开发咨询师。我出版了两本关于编程的书,一些关于微软产品的教程材料。最近,我接触了Flash和Flex,一下子被吸引了。

项目设置

在本教程里,我们来看看怎样使用Action Script 3,将ComboBoxes动态添加到Flex 3中的DataGrid控件里的。我们将使用少量的mxml代码,专注于使用Action Script 3,动态添加内容。这种方法很有用,如果你开始时不确定列的具体数量,因为你可能是从一个数据库中读取的数据。我们将从“最低层”开始这个项目,因此如果你觉得你已经掌握了些知识,你可以跳到下一节。

最终的解决方案将包括一个被扩展的,含有ComboBoxes 的DataGrid,在两个列里,填满了不同的值。第一个列是文本列,包含有人名,第二列是ComboBox, 提供的是性别信息,第三列还是一个ComboBox,提供年龄信息。在DataGrid下有个按钮,被点击时,会显示格子中的信息。这些信息会马上被保存到数据库里。

 

类和对象

我们来看看项目中的类和对象。

dtGrid

这是DataGrid,显示信息

btnFetchValues

这是按钮,被点击时,将显示文本域中的格值。

txtResult

为文本域,当点击按钮时,显示的格值。

ComboGrid

被扩展的DataGrid类,将渲染ComboBoxes。这是格类,将代替缺省DataGrid控件显示数据。

ComboBoxItem

这个类代表的是ComboBox的一个条目。我们使用类更便捷地动态创建条目(可能是从XML或数据库数据)。

ComboBoxRenderer

该类定义我们的ComboBox如何操作,及当选定一个条目时,会发生什么。我们要确保当选定一个条目时,ComboBoxContent对象中的基础数据会更改。

ComboBoxContent

该类定义用于ComboBox的基础数据。我们使用该类的对象,以跟踪选定的数据。

最终的项目结构如下所示:

 

创建项目:

第一步就是:创建包含控件和类的应用程序:

a. 打开Adobe Flex Builder 3;

b. 在主菜单内点击File-New-Flex Project;

c. 在项目名称文本域中为项目选取一个恰当的名称,在本教程中,我使用ComboBoxGrid。

d. 点击Finish Button,创建项目。

现在,你应该有了一个项目,采用的是MXML应用程序,名称与包含空的应用程序MXML标签的项目一样。

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

我们首先要更改的就是,应用程序的版图。有了绝对版图,我们需要使用X和Y值定位控件,如果我们要将它变更成竖向布置,控件会自动被堆栈起来。很简单,删除文本,在应用程序报头竖向写入。

<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical">

我们再来将标准的蓝绿色背景颜色改成深灰色。向应用程序标签添加backgroundColor statement。

<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical" backgroundColor="#333333" >

我们还需要添加一个初始化函数,用于调用创建DataGrid内行,列和控件的函数。如果要在基本的应用程序创建之后,启用程序,调用函数,我们需要为程序creationComplete事件添加一个回调函数。首先,我们在应用程序标签内添加事件回调。你可根据自己的喜好命名函数,但是通常命名为init或类似的名称。

<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical" backgroundColor="#333333" creationComplete="init()"  >

下一步,我们需要添加包含真实函数的脚本语句快。最简便的方法就是在任何应用程序开始和结束标签之间的空线上放置光标,并写入<script and press enter followed by a >标志。应该添加一个脚本语句块,如下所示:

<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical" backgroundColor="#333333"  creationComplete="init()">
   <mx:Script>
      <![CDATA[
   
      ]]>
   </mx:Script>
</mx:Application>

在CDATA块里,我们写入想要在程序中使用的函数,域和属性。在本例中,我们将添加一个命名为init的函数,这个函数不会返回任何值。

<![CDATA[
   private function init():void
   {
   }
]]>

引入语句

当控件或类在程序中使用时,引入语句通常被自动添加,但是有时,如果你在程序之间复制粘贴代码,你需要手工添加。下面是在程序中使用的引入语句的列表。引入语句放置在CDATA块的顶部。

import classes.ComboBoxItem;
import mx.controls.dataGridClasses.DataGridColumn;
import mx.controls.Alert;
import mx.collections.IViewCursor;
import mx.events.ListEvent;
import mx.events.FlexEvent;
import classes.ComboBoxContent;
import mx.collections.ArrayCollection;

ComboBoxItems类

我们要写入的第一类是一个辅助类,与ComboBox.里的条目对应。如果我们想使用动态数据填入ComboBox,这是非常必要的。每个条目将有两个属性,标签和值,每个与ComboBox表中的缺省属性对应。首先,我们将添加一个文件夹来保存我们的类。记住,Action Script是区分大小写的。

a. 点击Flex Navigator中的程序src文件夹,选择上下文菜单中的New-Folder; 

b. 在Folder Name文本域中写入类,点击Finish按钮;

c. 现在,点击你刚创建的Classes文件夹,选定上下文菜单中的New-ActionScript类;

d. 在Name文本域中写入ComboBoxItem,点击Finish按钮

于是,我们向Classes文件夹添加具有空构造函数的类。

package
{
   public class ComboBoxItem
   {
      public function ComboBoxItem()
      {
      }
   }
}

在这个类中,我们想添加两个私有域,它们具有相应的属性,可保存和返回数据。我们还想改变构造函数,提取两个会设定域的私有值的参数。

首先,我们在类的顶部添加私有域。我们给域恰当的命名。在我们的例子中,为字符串类型的_value_label。我们使用下划线说明它是一个整体域。

public class ComboBoxItem
{
   private var _value:String;
    private var _label:String;

然后,我们更改构造函数,提取字符串类型的两个参数设置域值。

public function ComboBoxItem(value:String, label:String)
   {
      _value = value;
      _label = label;
   }

最后,我们需要写入两个属性,它们会返回私有域值。如果我们跳过这一步,ComboBox可能不能向它的列表恰当添加条目。

  public function get value():String
   {
      return _value;
   }  
  
   public function get label():String
   {
      return _label;
   }
}

ComboBoxContent类

这个类会保存单个的ComboBox值对象。我们想要用一系列值填充Combobox,还想要读取选定的值,其指数和标签。我们也想要用一系列存储在对象中的值填充列表。使用静态值填充ComboBox很简单,窍门就是从grid中读取值。就是在这里,从中创建的类和对象引起我们的注意。一个解决方案,也就是我们将要执行的方案,就是在包含ComboBox的DataGrid细胞内存储这个类的一个对象。这样,我们就能通过grid与来自程序的对象互动,而ComboBox可以读取和更改对象的值,因为它已经在DataGrid细胞中被渲染。

看一下没有被渲染的ComboBoxes的DataGrid,我们可以看到在细胞中存储有ComboBoxContent对象。

该类包含两个私有域,分别为_listArray_index,含有在ComboBox的下拉列表中显示的条目和现在被选定的ComboBox的指数。它还包含一个构造函数,提取两个将被赋予到私有域上的参数。我们首先创建类。

a. 点击类文件夹,选定上下文菜单中的New-ActionScript Class;

b. 在Name文本域中写入ComboBoxContent,点击Finish按钮

于是,我们向Classes文件夹添加具有空构造函数的类。

package
{
   public class ComboBoxContent
   {
      public function ComboBoxContent ()
      {
      }
   }
}

下一步,我们向类添加两个私有域:

public class ComboBoxContent
   {
      private var _list:Array, _index:int;

然后,我们更改构造函数,提取参数,设定私有域的值。

  public function ComboBoxContent(list:Array, index:int = 0)
   {
      _list = list;
      _index = index;
   }

下一步,我们添加公共属性,设置并获取对象中的值。

public function get list():Array
   {
      return _list;
   }

   public function get index():int
   {
      return _index;
   }
  
   public function set index(value:int):void
   {
      _index = value;
   }
  
   public function get value():Object
   {
      if(_index < list.length)
         return list[_index];
      else
         return null;
   }
}

ComboboxRenderer类

这个类实现了所有的“魔法”。它是一个被拓展的ComboBox类,这就意味着我们的类“继承”了ComboBox行为,并且根据我们的需要定制化了这个行为。在我们的范例中,我们想读取,并更改存储在持有拓展的ComboBox的细胞中的ComboBoxContent对象的值。我们希望能够更改对象,和客户端程序通讯,或许能够将值保存到数据库中。

ComboboxRenderer类也需要执行IFactory界面,以在DataGrid细胞中被渲染。

a. 点击Classes文件夹,在上下文菜单内选定New-ActionScript Class;

b. Name文本域中写入ComboBoxRenderer;

c. 为了拓展另一个类,我们需要说明想要拓展的类。当点击Subclass 文本域右边的Browse 按钮时,会显示一个列表,我们选定列表中的ComboBox 类。选定列表中的ComboBox 类,点击Ok按钮。

d. 点击Finish按钮。

这样,就会向类文件夹增添一个有空构造函数的类。注意:已经向类定义添加了一个extends关键词。

package
{
   public class ComboboxRenderer extends ComboBox
   {
      public function ComboboxRenderer()
      {
      }
   }
}

下面这个是引入语句的列表,以防在执行该类的时候,它们不会被自动添加。

   import mx.controls.ComboBox;
   import mx.core.IFactory;
   import mx.events.DropdownEvent;
   import mx.events.FlexEvent;

下一步,我们需要执行IFactory接口。有了这个接口,才有可能在DataGrid里向细胞添加拓展的ComboBox控件。如果函数newInstance 必须被添加到类,IFactory包含有定义。

public class ComboboxRenderer extends ComboBox implements IFactory
   {
      //IFactory method
      public function newInstance():*{}

下一步,我们需要添加私有域,以存储基本的DataGrid行数据。我们需要这些数据,以读取和更改数据。我们将添加属性,以获取和设定值。

  //Fields
   private var _data:Object;

下一步,我们需要在构造函数中添加事件监听器,监听CREATION_COMPLETE和CLOSE事件。CREATION_COMPLETE事件会初始化ComboBox,并设定初始值。CLOSE事件会将现有设置保存到基本的ComboBoxContent对象。

   public function ComboboxRenderer()
   {
      super();
      addEventListener(FlexEvent.CREATION_COMPLETE, init);
      addEventListener(DropdownEvent.CLOSE, valueChanged);
   }

下一步,我们将写入init函数,设定ComboBox中的初始值。事件函数提取FlexEvent事件对象,我们可以用来获取现有的列指数,这个指数可用于获取正确的ComboBoxContent对象。

在受监控的程序块里,第一行获取现有列的指数。第二行将该列的数据转换回ComboBoxContent对象,从第三排,我们收集ComboBox中将要显示的列表。在第四行,我们将列表赋值到ComboBox dataProvider; 以确保列表显示在ComboBox的下拉单中。在第五行和最后一行,我们设定ComboBox;的selectedIndex属性,这将确定渲染selectedIndex时,选择列表中的哪个条目。

  private function init(event:FlexEvent):void
    {
      try
      {
         var idx:int = event.target.listData.columnIndex;
         var content:ComboBoxContent = _data[idx] as ComboBoxContent;
         var array:Array = content.list as Array;
          this.dataProvider = array;
         this.selectedIndex = content.index;
      }
      catch(err:Error){}
   }

下一步,我们写入将获取和设定ComboBox内数据的属性。两个数据属性需要被覆盖,以获取和设定正确的底层数据。valueChanged函数是事件函数,我们在构造函数中向其添加一个事件监听器。这个函数用来向细胞中的底层数据设定修改后的ComboBox数据。在受监控的程序块里,第一行从DataGrid中获取现有列指数。第二行将选定列的存储数据放入ComboBoxContent对象中,设定其指数属性。设定这个指数之后,我们可以从DataGrid中收集正确的数据,并在应用程序中使用。

 override public function get data():Object
   {
      return _data;
   }
 
   override public function set data(value:Object):void
   {
      _data = value;
   }
  
   private function valueChanged(event:DropdownEvent):void
   {
      try
      {
         var idx:int = event.target.listData.columnIndex;
         ComboBoxContent(_data[idx]).index = this.selectedIndex;
      }
      catch(err:Error){}
   }
}

ComboGrid类

这是我们需要写入的最后一个类,它是一个拓展的DataGrid类,我们向它添加函数,这个拓展的DataGrid类向细胞添加ComboBoxRenderer对象。如果要让DataGrid在细胞中展示ComboBoxes,这一步很重要。

首先,我们创建类

a. 点击classes文件夹, 选定上下文菜单中的New-ActionScript Class;

b. Name文本域写入ComboGrid;

c. 为拓展另一个类,我们需要说明将要拓展的类.我们在

d. 为了拓展另一个类,我们需要说明想要拓展的类。当点击Subclass 文本域右边的Browse 按钮时,会显示一个列表,我们选定列表中的DataGrid 类。选定列表中的DataGrid 类,点击Ok按钮。

e. 点击Finish按钮

下一步,我们写入函数, 这个函数将创建通过ComboBoxRenderer类的ComboBox, 将其添加到DataGrid细胞中。在ClassFactory对象中,第一行wrap了ComboBoxRenderer类,这样我们就可以向各列的itemRenderer属性添加它, 然后会在各列的细胞中显示它。在循环中,我们添加列头文本。如果列细胞包含ComboBoxContent对象,我们设置列条目渲染器,以显示拓展的ComboBox控件(wrapped在ClassFactory对象中)。

package classes
{  
   public class ComboGrid extends DataGrid
   {
      public function ComboGrid()
      {
         super();
      }
   }
}

应用程序

我们首先向应用程序添加控件。我们需要ComboGrid,按钮和TextArea。ComboGrid是拓展的DataGrid控件。按钮会启动一个Click事件,onFetchValues, 读取ComboGrid里的数据,并且在TextArea控件中显示。

下一步,我们需要添加一些array域,以保存将在ComboBoxes中显示的值和列头。我们还需要一个可绑定的ArrayCollection,这个集合将与ComboGrid绑定。因为与grid绑定之后,它会自动反映出ComboGrid内细胞的改变。因此,我们在程序中总是有更新的数据使用。在CDATA块中的引入语句下添加以下代码,如果没有引入,仅在CDATA块顶部添加。

public function addComboBoxCellRenderer(columnHeaderText:Array):void
   {
      var cboFactory:ClassFactory = new ClassFactory(ComboboxRenderer);

      for(var i:int=0; i<this.columnCount; i++)
      {
         var col:DataGridColumn = this.columns[i];
         col.headerText = columnHeaderText[i];
         if(this.dataProvider[0][i] is ComboBoxContent)
         {
            col.itemRenderer = cboFactory;
         }
      }
   }

下一步,我们用一些数据填充array。创建一个函数,名为fillComboBoxList,它不会提取任何参数,也不会返回任何值。填充array当然可以使用来自外部数据源的数据,但是为了教程更为简明易懂,我们使用静态数据。

注意,我们使用ComboBoxItem类,创建单个条目,这些条目将在ComboBoxes中显示。我倾向于使用两个列中的不同内容,以显示如何执行。在Gender 列中使用Gender array,在ComboGrid的Age列中,使用ages array。 你可以直添加数组对象,但是我想说明的是你如何在值里循环。如果你使用外部数据,这可能是一个方法。

  private function fillComboBoxList():void
   {
      var gender:Array =
          [new ComboBoxItem("M", "Male"), new ComboBoxItem("F", "Female")];
      var ages:Array =
          [new ComboBoxItem("Child", "0-12"),
           new ComboBoxItem("Teenager", "13-19"), 
          new ComboBoxItem("Adult", "20+")];
    
      for each(var g:ComboBoxItem in gender)
         dropDownList1.push(g);
    
      for each(var a:ComboBoxItem in ages)
         dropDownList2.push(a);
   }

下一步,我们添加一个方法,叫createGridContent,会向ComboGrid添加数据。前四行创建ComboBoxContent对象,用于程序和ComboBoxes之间通信。

接下来的两行,向我们之前创建的ArrayCollection添加两行数据。这个集合与ComboGrid绑定。下一列实际上将ArrayCollection数据与ComboGrid绑定,利用的是dataProvider属性。最后一行调用ComboGrid对象中的addComboBoxCellRenderer函数,渲染拓展的ComboBox,并设定数据。

  private function createGridContent():void
   {
      var cbo1:ComboBoxContent = new ComboBoxContent(dropDownList1, 0);
      var cbo2:ComboBoxContent = new ComboBoxContent(dropDownList2, 1);
      var cbo3:ComboBoxContent = new ComboBoxContent(dropDownList1, 1);
      var cbo4:ComboBoxContent = new ComboBoxContent(dropDownList2, 0);
      gridData.addItem(['Jonas', cbo1, cbo2]);
      gridData.addItem(['Lisa', cbo3, cbo4]);
      dtGrid.dataProvider = gridData;
      dtGrid.addComboBoxCellRenderer(columnHeaderText); 
   }

为了运行之前提到的两个函数,我们需要从某处调用它们:init 函数似乎是个好的“地点”。找到init函数,添加函数调用。

private function init():void
 {
  fillComboBoxList();
  createGridContent();
 }

我们需要做的最后一件事就是,为按钮写入“点击处理器”。函数会从ComboGrid获取数据,在TextArea控件中显示。我们通过创建IViewCursor,读取网格数据。在ComboGrid内,进行while循环。在循环内,我们使用一个for循环,收集网格中来自细胞的数据。收集到的数据然后添加到TextArea,显示给用户。

  private function onFetchValues(e:MouseEvent):void
   {
      var dp:Object=dtGrid.dataProvider;
      var cursor:IViewCursor=dp.createCursor();
      var result:String = "";
      while( !cursor.afterLast )
      {
         result += "/nrow:" +
            (cursor.bookmark.getViewIndex() + 1).toString() + "   ";
         //Loop over the columns in each row.
         for(var i:int=0; i<cursor.current.length; i++)
         {
            var col:DataGridColumn = dtGrid.columns[i];
            var c:Object = cursor.current[i];
            if(c is String)
               result += col.headerText + ":" + c + "   ";
            else if(c is ComboBoxContent)
            {
               result += col.headerText + ": index=" +
                c.index.toString() + " label='" +
                c.value.label.toString() + "' value=" +
                c.value.value.toString() + "   ";
            }
         }
      
         // Don't forget to move to next row:
         cursor.moveNext();
      }
       txtResult.text = result;
   }

就是这样了!你现在有了一个网格,具有ComboBoxes。

原创粉丝点击