Adobe Flex 3 Help

Collection events and manual change notification

Collections use events to indicate changes to the collection. You can use these events to monitor changes and update the display accordingly.

Collection events

Collections use CollectionEvent, PropertyChangeEvent, and FlexEvent objects in the following ways:

  • Collections dispatch a CollectionEvent (mx.events.CollectionEvent) event whenever the collection changes. All collection events have the type property value CollectionEvent.COLLECTION_CHANGE.
  • The CollectionEvent object includes a kind property that indicates the way in which the collection changed. You can determine the change by comparing the kind property value with the CollectionEventKind constants; for example, UPDATE.
  • The CollectionEvent object includes an items property that is an Array of objects whose type varies depending on the event kind. For ADD and REMOVE kind events, the array contains the added or removed items. For UPDATE events, the items property contains an Array of PropertyChangeEvent event objects. This object's properties indicate the type of change and the property value before and after the change.
  • The PropertyChangeEvent class kind property indicates the way in which the property changed. You can determine the change type by comparing the kind property value with the PropertyChangeEventKind constants; for example, UPDATE.
  • View cursor objects dispatch a FlexEvent class event with the type property value of mx.events.FlexEvent.CURSOR_UPDATE when the cursor position changes.

You use collection events to monitor changes to a collection to update the display. For example, if a custom control uses a collection as its data provider, and you want the control to be updated dynamically and to display the revised data each time the collection changes, the control can monitor the collection events and update accordingly.

You could, for example, build a simple rental-car reservation system that uses collection events. This application uses COLLECTION_CHANGE event type listeners for changes to its reservations and cars data collections.

The CollectionEvent listener method, named reservationsChanged, tests the event kind field and does the following:

  • If the event kind property is ADD, iterates through the objects in the event's items property and calls a function to update the reservation information display with boxes that display the time span of each reservation.
  • If the event kind property is REMOVE, iterates through the objects in the event's items property and calls a function to remove the reservation box for each item.
  • If the event kind property is UPDATE, iterates through the PropertyChangeEvent objects in the event's items property and calls the update function to update each item.
  • If the event kind property is RESET, calls a function to reset the reservation information.

The following example shows the reservationsChanged CollectionEvent event listener function:

private function reservationsChanged(event:CollectionEvent):void {
    switch (event.kind) {
        case CollectionEventKind.ADD:
            for (var i:uint = 0; i < event.items.length; i++) {
                    updateReservationBox(Reservation(event.items[i]));
            }
            break;

        case CollectionEventKind.REMOVE:
            for (var i:uint = 0; i < event.items.length; i++) {
                removeReservationBox(Reservation(event.items[i]));
            }
            break;

        case CollectionEventKind.UPDATE:
            for (var i:uint = 0; i < event.items.length; i++) {
                if (event.items[i] is PropertyChangeEvent) {
                    if (PropertyChangeEvent(event.items[i]) != null) {
                         updateReservationBox(Reservation(PropertyChangeEvent(
                            event.items[i]).source));
                    }
                }
                else if (event.items[i] is Reservation) {
                    updateReservationBox(Reservation(event.items[i]));
                }
        }
        break;

        case CollectionEventKind.RESET:
            refreshReservations();
            break;
    }
}

The updateReservationBox() method either shows or hides a box that shows the time span of the reservation. The removeReservationBox() method removes a reservation box. The refreshReservations() method redisplays all current reservation information.

For more information on the application and the individual methods, see the sample code.

Collection change notification

Collections include the itemUpdated() method, which notifies a collection that the underlying data has changed and ensures that the collection's data view is up to date when items in the underlying data object do not implement the IEventDispatcher interface. This method takes the item that was modified, the property in the item that was updated, and its old and new values as parameters. Collections also provide the enableAutoUpdate() and disableAutoUpdate() methods, which enable and disable the automatic updating of the data view when the underlying data provider changes.

Using the itemUpdated() method

Use the itemUpdated() method to notify the collection of changes to a data provider object if the object does not implement the IEventDispatcher interface; in this case the object is not monitorable. Adobe Flash and Flex Objects and other basic data types do not implement this interface. Therefore you must use the itemUpdated() method to update the collection when you modify the properties of a data provider such as an Array or through the display object.

You can also use the itemUpdated() method if you must use an Array, rather than a collection, as a control's data provider. Then the component wraps the Array in a collection wrapper. The wrapper must be manually notified of any changes made to the underlying Array data object, and you can use the itemUpdated()method for that notification.

You do not have to use the itemUpdated() method if you add or remove items directly in a collection or use any of the ICollectionView or IList methods to modify the collection.

Also, specifying the [Bindable] metadata tag above a class definition, or above a variable declaration within the class, ensures that the class implements the IEventDispatcher interface, and causes the class to dispatch propertyChange events. If you specify the [Bindable] tag above the class declaration, the class dispatches propertyChange events for all properties; if you mark only specific properties as [Bindable], the class dispatches events for only those properties. The collection listens for the propertyChange events. Therefore, if you have a collection called myCollection that consists of instances of a class that has a [Bindable] myVariable variable, an expression such as myCollection.getItemAt(0).myVariable="myText" causes the item to dispatch an event, and you do not have to use the itemUpdated() method. (For more information on the [Bindable] metadata tag and its use, see Binding Data.)

The most common use of t he itemUpdate() method is to notify a collection of changes to a custom class data source that you cannot make bindable or modify to implement the IEventDispatcher interface. The following schematic example shows how you could use the itemUpdated() method in such a circumstance.

Assume you have a class that you do not control or edit and that looks like the following:

public class ClassICantEdit {
    public var field1:String;
    public var field2:String;
}

You have an ArrayCollection that uses these objects, such as the following, which you populate with classICantEdit objects:

public var myCollection:ArrayCollection = new ArrayCollection();

You have a DataGrid control such as the following:

<mx:DataGrid dataProvider="{myCollection}"/>

When you update a field in the myCollection ArrayCollection, as follows, the DataGrid control is not automatically updated:

myCollection.getItemAt(0).field1="someOtherValue";

To update the DataGrid control, you must use the collection's itemUpdated() method:

myCollection.itemUpdated(collectionOfThoseClasses.getItemAt(0));

Disabling and enabling automatic updating

A collection's disableAutoUpdate() method prevents events that represent changes to the underlying data from being broadcast by the view. It also prevents the collection from being updated as a result of these changes.

Use this method to prevent the collection, and therefore the control that uses it as a data provider, from showing intermediate changes in a set of multiple changes. The DataGrid class, for example, uses the disableAutoUpdate() method to prevent updates to the collection while a specific item is selected. When the item is no longer selected, the DataGrid control calls the enableAutoUpdate() method. Doing this ensures that, if a DataGrid control uses a sorted collection view, items that you edit do not jump around while you're editing.

You can also use the disableAutoUpdate() method to optimize performance in cases where multiple items in a collection are being edited at once. By disabling the auto update until all changes are made, a control like the DataGrid control can receive an update event as a single batch instead of reacting to multiple events.

The following code snippet shows the use of the disableAutoUpdate() and enableAutoUpdate() methods:

var obj:myObject = myCollection.getItemAt(0);
myCollection.disableAutoUpdate();
obj.prop1 = 'foo';
obj.prop2 = 'bar';
myCollection.enableAutoUpdate();

Example: Modifying data in a DataGrid control

The following example lets you add, remove, or modify data in a DataGrid control:

<?xml version="1.0"?>
<!-- dpcontrols\ModifyDataGridData.mxml -->
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" width="500"
    height="600" >

    <mx:Script>
        <![CDATA[
            import mx.events.*;
            import mx.collections.*;
    
            // Add event information to a log (displayed in the TextArea).
            public function collectionEventHandler(event:CollectionEvent):void {
                switch(event.kind) {
                    case CollectionEventKind.ADD:
                        addLog("Item "+ event.location + " added");
                        break;
                    case CollectionEventKind.REMOVE:
                        addLog("Item "+ event.location + " removed");
                        break;
                    case CollectionEventKind.REPLACE:
                        addLog("Item "+ event.location + " Replaced");
                        break;
                    case CollectionEventKind.UPDATE:
                        addLog("Item updated");
                        break;
                }
            }
            // Helper function for adding information to the log.
            public function addLog(str:String):void {
                log.text += str + "\n";
            }
    
            // Add a person to the ArrayCollection.
            public function addPerson():void {
                ac.addItem({first:firstInput.text, last:lastInput.text,
                    email:emailInput.text});
                    clearInputs();
            }
    
            // Remove a person from the ArrayCollection.
            public function removePerson():void {
                // Make sure an item is selected.
                if (dg.selectedIndex >= 0) {
                    ac.removeItemAt(dg.selectedIndex);
            }
        }
    
        // Update an existing person in the ArrayCollection.
        public function updatePerson():void {
            // Make sure an item is selected.
            if (dg.selectedItem !== null) {
                ac.setItemAt({first:firstInput.text, last:lastInput.text,
                    email:emailInput.text}, dg.selectedIndex);
            }
        }
    
        // The change event listener for the DataGrid.
        // Clears the text input controls and updates them with the contents
        // of the selected item.
        public function dgChangeHandler():void {
            clearInputs();
            firstInput.text = dg.selectedItem.first;
            lastInput.text = dg.selectedItem.last;
            emailInput.text = dg.selectedItem.email;
        }
    
        // Clear the text from the input controls.
        public function clearInputs():void {
            firstInput.text = "";
            lastInput.text = "";
            emailInput.text = "";
        }

        // The labelFunction for the ComboBox;
        // Puts first and last names in the ComboBox.
        public function myLabelFunc(item:Object):String {
            return item.first + " " + item.last;
        }
        ]]>
    </mx:Script>
    
    <!-- The ArrayCollection used by the DataGrid and ComboBox. -->
    <mx:ArrayCollection id="ac"
            collectionChange="collectionEventHandler(event)">
        <mx:source>
            <mx:Object first="Matt" last="Matthews" email="matt@myco.com"/>
            <mx:Object first="Sue" last="Sanderson" email="sue@myco.com"/>
            <mx:Object first="Harry" last="Harrison" email="harry@myco.com"/>
        </mx:source>
    </mx:ArrayCollection>

    <mx:DataGrid width="450" id="dg" dataProvider="{ac}"
            change="dgChangeHandler()">
        <mx:columns>
            <mx:DataGridColumn dataField="first" headerText="First Name"/>
            <mx:DataGridColumn dataField="last" headerText="Last Name"/>
            <mx:DataGridColumn dataField="email" headerText="Email"/> 
        </mx:columns>
    </mx:DataGrid>

    <!-- The ComboBox and DataGrid controls share an ArrayCollection as their
        data provider.
        The ComboBox control uses the labelFunction property to construct the
        labels from the dataProvider fields. -->
    <mx:ComboBox id="cb" dataProvider="{ac}" labelFunction="myLabelFunc"/>
    
    <!-- Form for data to add or change in the ArrayCollection. -->
    <mx:Form>
       <mx:FormItem label="First Name">
            <mx:TextInput id="firstInput"/>
       </mx:FormItem>
       <mx:FormItem label="Last Name">
            <mx:TextInput id="lastInput"/>
       </mx:FormItem>
       <mx:FormItem label="Email">
            <mx:TextInput id="emailInput"/>
       </mx:FormItem>
    </mx:Form>
    
    <mx:HBox>
        <!-- Buttons to initiate operations on the collection. -->
        <mx:Button label="Add New" click="addPerson()"/>
        <mx:Button label="Update Selected" click="updatePerson()"/>
        <mx:Button label="Remove Selected" click="removePerson()"/>
        <!-- Clear the text input fields. -->
        <mx:Button label="Clear" click="clearInputs()"/>
    </mx:HBox>
    
    <!-- The application displays event information here -->
    <mx:Label text="Log"/>
    <mx:TextArea id="log" width="100" height="100%"/>
</mx:Application>

The executing SWF file for the previous example is shown below: