Easy Type-Safe DataProvider for Flex Lists and DataGrids

by David Salahi on March 14, 2011

In my past three posts I’ve been evolving a technique for creating a type-safe collection class that can be used a DataProvider for Flex Lists and DataGrids. As I’ve discussed previously, the problem with these controls is that they use the ArrayList or ArrayCollection classes as their data providers. And the problem those collections is that they are not type-safe. You can put any type of object into an ArrayList or ArrayCollection. That’s not a problem until you put the wrong type into one. Then, you get an error at run-time. It would, of course, be far better to catch the error at compile time. In this post I present the culmination of my technique which makes it convenient to use a type-safe container with Flex Lists and DataGrids.

This application wraps an instance of Vector.<Person> in a class to provide type-safe access. This class implements IList so that Lists and DataGrids can use it as a DataProvider. Right-click the app to view source or download an FXP file here.

Having honed the technique over the course of four blog posts I’m now making available a TypeSafeList abstract class which you can easily and quickly extend. With only about 20 lines of code you can create a subclass of TypeSafeList that will contain any single data type of your choice. Any attempt to insert an object of the wrong type will be caught at compile time. And, because TypeSafeList implements IList your subclass can still be used as a data provider for Lists and DataGrids.

If you want to know how the technique works, see my previous posts:

  1. Type-Safe DataProviders for Flex Lists with the ActionScript Vector
  2. An Enhanced Type-Safe Data Provider Technique for Flex IList-Based Controls
  3. Type-Safe DataProvider for Flex DataGrid; Data Loading Encapsulated w/Parsley DynamicCommand

If you just want to use TypeSafeList to quickly create your own type-safe collection classes you can skip the previous posts and just read this one.

To use TypeSafeList you need to create a container class that extends TypeSafeList and has a Vector as an instance variable. You should, of course, type the Vector to contain the object type that you’re interested in. For this example, we’re going store Person objects so we’ll have:

private var _people:Vector.<Person>;

Next, you need to define a type-safe interface for accessing your Vector. Here’s a minimal example:

public interface IPersonVectorDP
{
    function pop():Person;
    function push(value:Person):uint;
}

You can name your interface whatever you like; here, I’ve used IPersonVectorDP. You will add or remove Person objects to _people only through this interface. Your interface could have other methods; e.g., insertItemAt or deleteItemAt (note that you must not use any of the same method names as the IList methods). Whatever methods you define in your interface will need to be implemented in your type-safe collection’s implementation class.

Here’s the beginning of an example TypeSafeList subclass with our _people Vector:

public class PersonVectorDP extends TypeSafeList implements IPersonVectorDP
{
    private var _people:Vector.<Person>;
 
    public function PersonVectorDP()
    {
        _people = new Vector.<Person>;
    }
    .
    .
    .

Next, we implement the methods in our IPersonVectorDP  interface that we defined above. Here, we implement the push and pop methods we declared in IPersonVectorDP:

public function push(value:Person):uint
{
    var count:uint = _people.push(value);
    dispatchAddEvent(value, count);
    return count;
}
 
public function pop():Person
{
    var poppedPerson:Person = _people.pop();
    dispatchRemoveEvent(poppedPerson, _people.length);
    return poppedPerson;
}

These methods wrap Vector’s push and pop methods. In addition, they call event dispatchers in the TypeSafeList superclass. These event dispatchers notify the Flex List/DataGrid controls when the container’s elements have changed so that the controls can update their views of those elements.

At this point, you need to write a function to provide access to your Vector so that TypeSafeList can access elements as needed by Flex’s List & DataGrid controls (through IList). You have a choice between a clean, easy approach that can be wasteful of memory and an efficient approach which requires you to modify a few lines of code in TypeSafeList.

First, here’s the clean, easy approach. You define a function which returns a reference to your Vector as a Vector of Objects. In our example, we return a copy of the _people Vector.

override protected function getVector():Vector.<Object>
{
    var vector:Vector.<Object> = Vector.<Object>(_people);
    return vector;
}

This works fine for TypeSafeList because Objects are what IList uses. IList doesn’t need to know about Persons so neither does TypeSafeList.

The problem with this technique is that every time an element is added to your Vector a copy of the entire Vector is made in getVector. For small collections the overhead will be negligible. But for collections consisting of thousands of elements the overhead could be substantial. But without pointers (or pointers to references) as in C++ there’s nothing that can be done. Except to get in and muck around in the base class code.

So, here’s the efficient but slightly messy alternative: you edit a copy of TypeSafeList and modify the existing getVector function to return a Vector of your type. Here’s the stock getVector function from TypeSafeList:

protected function getVector():Vector.<Object>
{
    throw new IllegalOperationError("Error in TypeSafeList; you must subclass TypeSafeList and override getVector");
    return null;
}

And here’s a modified version:

override protected function getVector():Vector.<Person>
{
    throw new IllegalOperationError("Error in TypeSafeList; you must subclass TypeSafeList and override getVector");
return null;
 
}

Now, you can modify your override to return a Vector of your type; e.g.,

override protected function getVector():Vector.<Person>
{
    return _people;
}

You also need to make a couple more edits to TypeSafeList. First, there’s a Vector.<Object> declaration in getItemIndex; just change it to your type; e.g., Vector.<Person>. Second, there’s a loop in which an element var is declared:

var element:Object = vector[i];

You’ll also need to change the Object type to your type.

As you can see, there are actually only three lines of code to modify if you want to go the performance route. Unfortunately, that means you’ll need to create a separate copy of TypeSafeList for each data type you want to use it with. But until ActionScript has full support for generics this is the best we can do. But, at least, we can now have type-safe collections for use with Flex ListBase-derived controls and DataGrids.

Download the example project (FXP).

Leave a Comment

 

{ 1 trackback }

Previous post:

Next post: