Adobe
Products
Acrobat
Creative Cloud
Creative Suite
Digital Marketing Suite
Digital Publishing Suite
Elements
Photoshop
Touch Apps
Student and Teacher Editions
More products
Solutions
Creative tools for business
Digital marketing
Digital media
Education
Financial services
Government
Web Experience Management
More solutions
Learning Help Downloads Company
Buy
Home use for personal and home office
Education for students, educators, and staff
Business for small and medium businesses
Licensing programs for businesses, schools, and government
Special offers
Search
 
Info Sign in
Welcome,
My cart
My orders My Adobe
My Adobe
My orders
My information
My preferences
My products and services
Sign out
Why sign in? Sign in to manage your account and access trial downloads, product extensions, community areas, and more.
Adobe
Products Sections Buy   Search  
Solutions Company
Help Learning
Sign in Sign out My orders My Adobe
Preorder Estimated Availability Date. Your credit card will not be charged until the product is shipped. Estimated availability date is subject to change. Preorder Estimated Availability Date. Your credit card will not be charged until the product is ready to download. Estimated availability date is subject to change.
Qty:
Purchase requires verification of academic eligibility
Subtotal
Review and Checkout
Adobe Developer Connection / Flash Developer Center /

Detecting when data is edited in the DataGrid component

by H. Paul Robertson

H. Paul Robertson
  • Blog

Created

12 November 2007

Page tools

Share on Facebook
Share on Twitter
Share on LinkedIn
Bookmark
Print
components DataGrid Flash Professional CS3

Requirements

Prerequisite knowledge

This article is geared toward intermediate to advanced developers creating applications using Flash CS3 Professional components—specifically, the DataGrid component. Some experience working with components in Flash is necessary, as well as experience with ActionScript 3.0 event handling. At its most advanced, this article briefly discusses applying these techniques when using custom cell renderers, so knowledge of creating a custom cell renderer will be necessary to apply that technique.

User level

Intermediate

Required products

  • Flash Professional (Download trial)

Sample files

  • detecting_datagrid_edits.zip (751 KB)

Suppose you are creating a user interface with Flash CS3 Professional using the new lightweight ActionScript 3.0 components. You're using the DataGrid component to display—what else?—a table of data. You want users to be able to edit the data, and you also want to know when the user changes the data so that you can update the application.

For example, suppose you're building something like a simple spreadsheet application (see the interactive example in Figure 1). Naturally, when the user edits a value in the Quantity column, you'd expect the Total value to automatically update itself. In order to make that happen, you need a way to determine when the user has actually made a change to a value in the Quantity column.

This content requires Flash To view this content, JavaScript must be enabled, and you need the latest version of the Adobe Flash Player. To view this content, JavaScript must be enabled, and you need the latest version of the Adobe Flash Player.

Figure 1. Sample inventory where Total should be updated when Quantity data changes

This sounds like something that should be built in to the DataGrid component, doesn't it? Alas, it's not—and getting it to work isn't as obvious as you'd think, as I discovered when I started down the road of trying to figure out how to make it work while building the Filter Workbench example for the Programming ActionScript 3.0 book in the Flash LiveDocs.

I was building an application using Flash CS3 Professional, which makes heavy use of the new ActionScript 3.0 components included with the authoring tool. In one part of the application, there is a DataGrid component that displays a table of values. In that particular case, the values define a set of colors that make up a gradient, so each row represents a single color and the columns have values for base color, transparency, and color distribution ratio.

In order to make it possible for users to alter these color settings and see different effects, I made the DataGrid component editable by setting its editable property to true. Consequently, by clicking in a cell, a user can change the value in the cell. In the code, the DataGrid component dispatches a few events related to editing in this order:

  1. itemEditBeginning event: Dispatched just before the editing of the cell begins—that is, after the user has clicked in the cell but just before the editing is available
  2. itemEditBegin event: Dispatched when editing of the cell actually begins
  3. itemEditEnd event: Dispatched when editing of the cell ends (for example, when the user tabs or clicks out of the cell) but before the new value is actually stored in the data provider or the screen is updated to display the new value

The key thing that's missing here is a way to determine when, as a result of the user's editing, the data actually changes. Unfortunately, under normal circumstances, when the itemEditEnd event is dispatched, the new value (if any) isn't available yet. For the rest of this article, I'll look at one approach that you might think would work but doesn't, and two approaches that do work, for determining if data actually changes when a user edits a cell of a DataGrid component.

What doesn't work: Using the DataProvider component's events

As I dug through the documentation, I noticed that the fl.data.DataProvider class (which I was using as the data source for my DataGrid component) has some interesting events: dataChange and preDataChange. "Ah-ha!" I thought. I could just add a listener for those events, and when the DataGrid component changes the underlying data in DataProvider, it will dispatch those events and (if I needed it, which I didn't) I can grab a snapshot of the old data in preDataChange and get the new data in dataChange. (In my case, I didn't even need to know which data had changed; it was sufficient for my purposes to know that some data had changed, and then just recalculate filter values using all the data in DataGrid.)

Unfortunately, that's not how the DataProvider component works. In fact, its events are designed to handle the opposite use case: If something other than DataGrid changes the data in DataProvider, then DataProvider notifies DataGrid so that it can update itself. But if DataGrid changes DataProvider, then DataProvider assumes that DataGrid already knows about the change (and nobody else would want to), so it doesn't dispatch its events in that case.

So, strike 1 for trying to use the DataProvider component's events.

What does work (with help): Using the DataGrid component's events

Since using the DataProvider component was not an option, I thought I'd take a look at the DataGrid component's events.

DataGrid exposes one event, itemEditEnd, that looks like it might be useful. However, there are a couple of problems:

  • The itemEditEnd event is dispatched every time cell editing ends, not just when the cell's data changes. So the fact that the event is dispatched doesn't automatically tell you that the data has changed.
  • Unlike the DataGrid component's itemEditBegin event, which has a corresponding itemEditBeginning event, the itemEditEnd event doesn't have a corresponding event you can use to get the value before it is edited. In fact, in most cases itemEditEnd gives you the value before it is changed, whether or not it actually changes.
  • The event object dispatched by itemEditEnd event is a fl.events.DataGridEvent class. That class doesn't have any property such as dataChanged or oldValue and newValue, so I didn't see any obvious way to identify whether the data had changed. (The DataGridEvent class does have some properties that end up being useful—but nothing that can be used without some work.)

Eventually, after stepping through the DataGrid source code several times as it processed editing events, I was able to figure out a couple of ways to identify when an edited item's value changes in an editable DataGrid component.

The premise behind both of these approaches is this: Normally, when you register a listener for the DataGrid component's itemEditEnd event, the underlying data in DataProvider still contains the old values, and hasn't been updated by DataGrid. In fact, DataGrid itself updates DataProvider by registering for its own itemEditEnd event, and then checking whether the original value is different from the new value and updating DataProvider if that's the case. The question is, if DataGrid is updating the underlying data as part of handling the itemEditEnd event, how can you get the value both before and after it changes, so that you can compare them and see if it's changed?

Using two event listener functions

This is the approach I recommend, although it does require more code because you have to write two event listener functions. The trick behind this approach is the concept of event priority.

As I mentioned earlier, DataGrid itself uses its own itemEditEnd event to update its DataProvider component. If you consider the conceptual model of how events work in ActionScript 3.0, basically when a user ends the item editing process (probably by clicking or tabbing away from a particular cell), the DataGrid component loops through the list of objects that have registered as listeners for the itemEditEnd event, calling each function in turn. Because DataGrid uses itemEditEnd to update its DataProvider component, that could theoretically lead to inconsistent results. Depending on whether your particular listener function is called before or after the DataGrid component's internal itemEditEnd listener function, the value in DataProvider may or may not be updated with the new value.

In order to prevent this potential problem, DataGrid uses event priority to specify that (in the normal case) its own itemEditEnd listener should be called after other itemEditEnd listeners—which is the reason why (in the normal case) DataProvider contains the pre-edit data rather than the new value, if any. Specifically, when DataGrid registers as a listener of its own itemEditEnd event, it does so using a priority of −50.

Here is the actual code, from line 679 in the source code of the DataGrid class:

addEventListener(DataGridEvent.ITEM_EDIT_END, itemEditorItemEditEndHandler, false, -50);

When you register an event listener in ActionScript, you can optionally specify a priority for your listener. (The default value that most listeners use is 0.) Listeners with a higher priority get called first; listeners with a lower priority get called later. Since the default (rarely changed) priority is 0, any listeners that are registered using the default are called before DataGrid's internal itemEditEnd listener—so they'll get access to the pre-change value.

However, there's no reason that your event listener cannot register with a different priority. For that matter, you can register two different listener functions for the same event using different priorities, which is exactly how this approach works.

You register two functions as listeners for the DataGrid component's itemEditEnd event. The first one should be called before DataGrid updates DataProvider, so it must be registered with a priority greater than −50 (I use 100 here):

// This variable will store a temporary value // to check if the DataGrid's edited item changes var tempValue:Number; // register itemEditEnd listener that will be called before an item's value is updated myGrid.addEventListener(DataGridEvent.ITEM_EDIT_END, itemEditPreEnd, false, 100); function itemEditPreEnd(event:DataGridEvent):void { // get a reference to the datagrid var grid:DataGrid = event.target as DataGrid; // get a reference to the name of the property in the // underlying object corresponding to the cell that's being edited var field:String = event.dataField; // get a reference to the row number (the index in the // dataprovider of the row that's being edited) var row:Number = Number(event.rowIndex); if (grid != null) { // gets the value (pre-edit) from the grid's dataprovider tempValue = grid.dataProvider.getItemAt(row)[field]; // you could also use this line to get the value // directly from the cellrenderer that's showing the value // in the datagrid -- it's the same value. // That way you wouldn't need a reference to the DataGrid. //tempValue = event.itemRenderer.data[field]; } }

The second listener should be called after DataGrid updates its DataProvider component, so it must be registered with a priority less than −50 (I use −100 here):

// register itemEditEnd listener that will be called after an item's value is updated myGrid.addEventListener(DataGridEvent.ITEM_EDIT_END, itemEditPostEnd, false, -100); function itemEditPostEnd(event:DataGridEvent):void { var grid:DataGrid = event.target as DataGrid; var field:String = event.dataField; var row:Number = Number(event.rowIndex); if (grid != null) { // gets the value (post-edit) from the grid's dataprovider var newValue:Number = grid.dataProvider.getItemAt(row)[field]; // you could also use this line to get the value // directly from the cellrenderer that's showing the value // in the datagrid -- it's the same value. // That way you wouldn't need a reference to the DataGrid. //var newValue = event.itemRenderer.data[field]; // check if the value has changed if (newValue != tempValue) { // do actions that should happen when the data changes } } }

Notice that in the first listener function (itemEditPreEnd), the function stores the current value in a persistent variable, a variable that is declared outside the function scope. In the second listener function (itemEditPostEnd) that stored value is compared to the final value, and if they're different we know that the value of the edited item actually changed.

Figure 2 is a working example of this approach.

This content requires Flash To view this content, JavaScript must be enabled, and you need the latest version of the Adobe Flash Player. To view this content, JavaScript must be enabled, and you need the latest version of the Adobe Flash Player.

Using one event listener function

Although I recommend the other approach, if you don't like the idea of using two listeners for some reason, or if you're concerned about using two different priorities, there is an alternative approach that uses only one event listener.

In this approach, you create a single listener function and register it with a priority greater than −50 (the default 0 is fine). Let me repeat: Your listener must be triggered before DataGrid updates DataProvider, or you'll get a runtime error.

This approach works by pulling values from two different places. For the pre-change value, it looks in DataProvider (or optionally in the cellrenderer for the cell). For the post-change value, it looks at DataGrid's itemEditorInstance property, which is the actual display object that's used for the editing field, and pulls the raw data from there. Basically, it reads the text property of the TextField that's used in the default item editor.

Where it gets complicated is that it has to dynamically determine the name of the property that's used by the item editor. That way, if you create a DataGrid component that uses a custom item editor (for example, if you have numeric data in a column and use a NumericStepper component whenever that column is being edited, rather than the default text field) then you'll need to know the name of that item editor's property from which you should get the edited value.

Here's what the code looks like:

// register itemEditEnd listener to determine when an item is changed myGrid.addEventListener(DataGridEvent.ITEM_EDIT_END, itemEditEndHandler); function itemEditEndHandler(event:DataGridEvent):void { // get a reference to the datagrid var grid:DataGrid = event.target as DataGrid; // get a reference to the name of the property in the // underlying object corresponding to the cell that's being edited var field:String = event.dataField; // get a reference to the row number (the index in the // dataprovider of the row that's being edited) var row:Number = Number(event.rowIndex); // get a reference to the column number of // the cell that's being edited var col:int = event.columnIndex; if (grid != null) { // gets the value (pre-edit) from the grid's dataprovider var oldValue:Number = Number(grid.dataProvider.getItemAt(row)[field]); // you could also use the following line to get the value // directly from the cellrenderer that's showing the value // in the datagrid -- it's the same value. // That way you wouldn't need a reference to the DataGrid. //var oldValue = event.itemRenderer.data[field]; // get the value (post-edit) from the item editor var newValue:Number = Number(grid.itemEditorInstance[grid.columns[col].editorDataField]); // check if the value has changed if (newValue != oldValue) { // do actions that should happen when the data changes // Note that in this case, the dataprovider // hasn't been updated yet, so you can't do any // actions that require the dataprovider to have // the new data } } }

In fact, if you already know for certain what type of object is being used as the item editor, you don't need to use such convoluted code. For example, with the default item editor, grid.columns[col].editorDataField will always be text (the .text property), so you can just use code like this to get the post-edit value:

var newValue:Number = Number(grid.itemEditorInstance.text);

Of course, if you're using some custom item editor, you can just hard-code the appropriate property name. For example, if you're using a NumericStepper component instead of the default item editor, you can use the value property instead of the text property:

var newValue:Number = grid.itemEditorInstance.value;

Note: The biggest drawback to this approach, as explained in the code comments is that the DataProvider component hasn't been updated by the DataGrid component yet, so if you want to do anything using DataProvider, this approach won't work unless you use some technique to delay your actions until after DataGrid has updated DataProvider. As I stated previously, you cannot use this technique after DataGrid has updated DataProvider; if you do, you will not be able to get the pre-edit value (because DataProvider will have already changed) and you'll get a runtime exception because the item editor will have already been destroyed, and you won't be able to access it anymore to get the new value.

Figure 3 is a working example of this approach. (It looks the same as Figure 2, of course.)

This content requires Flash To view this content, JavaScript must be enabled, and you need the latest version of the Adobe Flash Player. To view this content, JavaScript must be enabled, and you need the latest version of the Adobe Flash Player.

Using components can make building applications much easier. However, it occasionally comes with some complications. Nevertheless, I think the Flash CS3 Professional components constitute the best Flash component set yet because they're lightweight (they don't bloat your SWF's file size too much) and they were designed to be easy to skin, so you can make your components match the appearance of your application's design with greater flexibility and ease than ever before in Flash.

Another important feature of the Flash CS3 components is that the source code is included with Flash. Not only can you see the source code, but it is possible to create your own version of the source code and instruct Flash to use your version rather than the original, without jumping through many hoops, compared to how things worked in previous versions.

Where to go from here

If you are feeling ambitious, you can look at the source code for the DataGrid component and add your own code to make it dispatch a new event (or some other mechanism), so that the component notifies you when a user's editing actually changes a cell's data. In the process of working out the solutions presented in this article, I looked through the source code in detail. I think it could be done with only a few lines of code, so if you're looking for a component-related challenge, then give it a shot.

Creative Commons License
This work is licensed under a Creative Commons Attribution-Noncommercial-Share Alike 3.0 Unported License

More Like This

  • Controlling web video with ActionScript 3 FLVPlayback programming
  • Creating ActionScript 3.0 components in Flash – Part 6: Invalidation model
  • Creating ActionScript 3.0 components in Flash – Part 7: Focus management
  • Creating ActionScript 3.0 components in Flash – Part 8: Keyboard support
  • Creating ActionScript 3.0 components in Flash – Part 9: Shim compiled clip
  • Modeling User Workflows for Rich Internet Applications
  • Creating ActionScript 3.0 components in Flash – Part 1: Introducing components
  • Skinning the ActionScript 3 FLVPlayback component
  • Creating the Kuler panel for Flash CS3 Professional
  • Exploring the Flash video templates and tutorials

Flash User Forum

More
04/23/2012 Auto-Save and Auto-Recovery
04/23/2012 Open hyperlinks in new window/tab/pop-up ?
04/21/2012 PNG transparencies glitched
04/01/2010 Workaround for JSFL shape selection bug?

Flash Cookbooks

More
02/13/2012 Randomize an array
02/11/2012 How to create a Facebook fan page with Flash
02/08/2012 Digital Clock
01/18/2012 Recording webcam video & audio in a flv file on local drive

Products

  • Acrobat
  • Creative Cloud
  • Creative Suite
  • Digital Marketing Suite
  • Digital Publishing Suite
  • Elements
  • Mobile Apps
  • Photoshop
  • Touch Apps
  • Student and Teacher Editions

Solutions

  • Digital marketing
  • Digital media
  • Web Experience Management

Industries

  • Education
  • Financial services
  • Government

Help

  • Product help centers
  • Orders and returns
  • Downloading and installing
  • My Adobe

Learning

  • Adobe Developer Connection
  • Adobe TV
  • Training and certification
  • Forums
  • Design Center

Ways to buy

  • For personal and home office
  • For students, educators, and staff
  • For small and medium businesses
  • For businesses, schools, and government
  • Special offers

Downloads

  • Adobe Reader
  • Adobe Flash Player
  • Adobe AIR
  • Adobe Shockwave Player

Company

  • News room
  • Partner programs
  • Corporate social responsibility
  • Career opportunities
  • Investor Relations
  • Events
  • Legal
  • Security
  • Contact Adobe
Choose your region United States (Change)
Choose your region Close

North America

Europe, Middle East and Africa

Asia Pacific

  • Canada - English
  • Canada - Français
  • Latinoamérica
  • México
  • United States

South America

  • Brasil
  • Africa - English
  • Österreich - Deutsch
  • Belgium - English
  • Belgique - Français
  • België - Nederlands
  • България
  • Hrvatska
  • Česká republika
  • Danmark
  • Eastern Europe - English
  • Eesti
  • Suomi
  • France
  • Deutschland
  • Magyarország
  • Ireland
  • Israel - English
  • ישראל - עברית
  • Italia
  • Latvija
  • Lietuva
  • Luxembourg - Deutsch
  • Luxembourg - English
  • Luxembourg - Français
  • الشرق الأوسط وشمال أفريقيا - اللغة العربية
  • Middle East and North Africa - English
  • Moyen-Orient et Afrique du Nord - Français
  • Nederland
  • Norge
  • Polska
  • Portugal
  • România
  • Россия
  • Srbija
  • Slovensko
  • Slovenija
  • España
  • Sverige
  • Schweiz - Deutsch
  • Suisse - Français
  • Svizzera - Italiano
  • Türkiye
  • Україна
  • United Kingdom
  • Australia
  • 中国
  • 中國香港特別行政區
  • Hong Kong S.A.R. of China
  • India - English
  • 日本
  • 한국
  • New Zealand
  • 台灣

Southeast Asia

  • Includes Indonesia, Malaysia, Philippines, Singapore, Thailand, and Vietnam - English

Copyright © 2012 Adobe Systems Incorporated. All rights reserved.

Terms of Use | Privacy Policy and Cookies (Updated)

Ad Choices

Reviewed by TRUSTe: site privacy statement