SDK 3.4新变化之一 Flex SDK 3.4 List/DataGrid/TileList/Menu/Tree throws Error if selectedItem is not in the dataprovider

png-0004 将原有的Flex project的SDK从3.2改到3.4后,如果你的代码足够多,那么这个问题你很可能会遇到,如下面是我的代码:

_selectionList = new List();
_selectionList.width = 360;
_selectionList.dataProvider = _selectionDataProvider;
_selectionList.selectedItem = _valueInput;
_selectionList.addEventListener(ListEvent.CHANGE, onListSelectionChange);

运行后结果:

TypeError: Error #1010: A term is undefined and has no properties.
    at mx.controls.listClasses::ListBase/setSelectionDataLoop()[C:\autobuild\3.4.0\frameworks\projects\framework\src\mx\controls\listClasses\ListBase.as:6473]
    at mx.controls.listClasses::ListBase/commitSelectedItems()[C:\autobuild\3.4.0\frameworks\projects\framework\src\mx\controls\listClasses\ListBase.as:6364]
    at mx.controls.listClasses::ListBase/commitSelectedItem()[C:\autobuild\3.4.0\frameworks\projects\framework\src\mx\controls\listClasses\ListBase.as:6329]

原因:上面的_valueInput与_selectionDataProvider里元素根本不是同类的所以不可能在它里面,这其实是一个bug,将第四行comment掉即可。以前没有注意到是因为使用旧版本SDK时,如果设置的selectedItem不在data provider中, 效果与selectedItem = null一样。编程时稍加注意即可避免此Error.

在SDK 3.4中,List/DataGrid/TileList/Menu/Tree依然支持如下代码:

  • selectedItem = null; // 清除选择
  • selectedIndex = –1; // 清除选择
  • selectedIndex = –3; // 清除选择
  • selectedIndex = 99; // 假如data provider里面元素少过100个,最后一个element将被选中。

Elegant Solutions 上乘之道

As always, we have elegant solutions to make your life easier:

package com.insprise.common.ui
{
import com.insprise.basis.eo.UnsupportedError;
import com.insprise.common.ObjectUtils;

import mx.collections.ListCollectionView;
import mx.controls.listClasses.ListBase;

/**
 * Provides util functions for list based controls.
 */
public class ListUtils
{
  /**
   * Sets the selected item of the given list based control to the specified object.
   * In Flex SDK v3.4 and later, setting the selectedItem to an object not in the data provider will throw error.
   * @param control the list based control, e.g. List, TileList, Tree, DataGrid
   * @param objectToSelect the object to be selected, usually but not necessarily it is one of elements in data provider of the list control
   * @return true if the specified object is in the data provider of the list and is selected; false if it is not in the data provider.
   */
  public static function setSelectedItem(control:ListBase, objectToSelect:Object):Boolean {
    if(control == null || control.dataProvider == null) {
      return false;
    }
    if(objectToSelect == null) {
      control.selectedItem = null;
      return false;
    }
    var dp:Object = control.dataProvider;
    if(dp is ListCollectionView) {
      var selIndex:int = ListCollectionView(control.dataProvider).getItemIndex(objectToSelect);
      if(selIndex >= 0) {
        control.selectedIndex = selIndex;
        return true;
      }else{
        return false;
      }
    }else{
      throw new UnsupportedError("dp class: " + ObjectUtils.getClassName(dp));
    }
  }

} // end class
} // end package

现在,你可以用如下的代码来进行选择而不必担心被选的object是否会引起Error:

ListUtils.setSelectedItem(list/tile/datagird, object);