为自动创建的Objects传值 Pass Value to Auto-Created Objects with DataGrid ItemEditors as an Example

diannaowenjian-03 有些objects是无法确定何时被创建的,给这些自动创建的objects传值有一定的挑战。我们假设自动生成的objects为o1, o2, … 其class为O,另外一组可以非自动生成的objects a1, a2, … 对应class为A。目的:我们需要o2来获取a2的reference. 可以考虑的方法有:

- 我们创建一个hash map, 而且此hash map是Singleton, 将a1, a2 …等都放进去,这样在任何地方,都可以通过HashMap.singleton.getA(“2”)类似的方法获取到a2. 实际上这就是设计模式Service Locator的一种典型应用;设计模式并非是万能药,这里使用它有点用牛刀杀鸡的感觉。这里还有一个问题是,要a2来传递”2“给service locator, 此”2“本身也得被传入,所以没有解决根本问题。

- 更多的情况下,自动生成的objects某些methods会被呼叫到或者其属性被设置,这样便打开了通向外界的大门。下面将以DataGrid ItemEditors为例演示此种方法。

下图是一个很典型的例子:当用户在ComboBox里选取新值时,DataGrid中的ItemEditor需要被更新 - 这里用户选择一个Entity, 基于ComboBox实现的ItemEditor需要更新其dataprovider为这个选中Entity的attributes.

itemeditor 

实现方法:我们首先把选中的Entity值放到DataGrid.data里 (因为这里DataGrid.data没有用处);然后我们的ItemEditor可以获取到这个DataGrid,及其.data的值,这就实现了传递。

代码:

public class ItemEditorComboAttrs extends ComboBox {
  protected var _entity:Entity;
  /** Attributes owned by the entity */
  public var attributtes:ArrayCollection;

  protected var lastOwner:DisplayObjectContainer = null;
  protected var watcherDataGridData:ChangeWatcher;
  override public function set data(value:Object):void {
    if(watcherDataGridData == null || lastOwner != owner) {
      if(watcherDataGridData != null) {
        watcherDataGridData.unwatch();
      }
      lastOwner = owner; // 获取DataGrid
      watcherDataGridData = BindingUtils.bindSetter(entityChanged, owner, ["data"]);
    }
    if(_entity != null) {
      selectedItem = _entity.getAttributeBySystemName(String(value["targetPath"]));
    }
  }

  // Called when datagrid.data changed.
  protected function entityChanged(entity_:Entity):void {
    entity = entity_;
  }

  public function set entity(value:Entity):void {
    if(_entity == value) {
      return;
    }
    if(_entity != null) {
      dataProvider = null;
    }
    _entity = value;
    if(_entity != null) {
      dataProvider = _entity.attributesOwned;
    }
  }
} // end class
} // end package

使用DataGrid.data是一种取巧的做法。追求纯粹的开发者可以通过监听itemEditorBegin event, 更改传到item editors里的data, 异曲同工。

以上实现方法不局限于ActionScript,更适用于包括Java在内的其他语言

UPDATE for Flex SDK 3.4 – Using Custom DataGrid Class

上面使用 .data的做法是一种取巧的做法,正式做法应该如下 - 定义专门的property用来传入外面的值:

public class DataGridWithExternalProperty extends DataGrid {
  // external property that can be set and used by ItemEditor of cells.
  [Bindable]
  public var externalProp:Object;
} // end class
...
watcherDataGridData = BindingUtils.bindSetter(entityChanged, owner, ["externalProp"]);

在Flex SDK 3.4中,使用.data将导致如下的错误:

TypeError: Error #1010: A term is undefined and has no properties.

at mx.controls.listClasses::ListBase/setSelectionDataLoop()

原因大约是:.data被默认为是选中的元素(在ListBase的subclass List中),而DataGrid里面没有对.data进行特别处理,这样就出现问题了。

使用正式做法不会产生问题。