Double-click your Flex DataGrid rows
NOTE: In the comments below, someone named Chris left a note about using the doubleClickEnabled attribute of the DataGrid to accomplish the same thing I did by using the initialize event and the itemRenderer. I took Chris’s suggestion and applied it to my application and it does in fact accomplish the same thing, so there’s a simpler way to deal with the problem. I would imagine that, internally, they’re roughly equivalent and, while there’s a more straightforward solution it’s nice to know why things work, which is as much what this post is about as it is what to do to get it working in the first place.
No matter which approach you take to solving the problem, this is, hopefully, the last time someone will have to take hours to find the solution… ]It would seem to me that in the past 2 years I’d have had to make a Flex DataGrid respond to double-clicks on the rows, but apparently I haven’t, because over the weekend I was working on a project and had to do just that. Since it proved to be poorly documented (at least according to my Googling) and took me quite a while to work out, I figured I’d blog what I did to make it work.
It all started with a DataGrid, and the itemDoubleClick event handler…You see, the Flex 3 DataGrid component has a built-in event handler to deal with double-clicks on its rows, but (and this I found referenced a LOT in Google searches on the subject) even though you’ve implemented it, nothing happens. This is because the rows (which are instances of a subclass of InteractiveObject) have double-click support, but it’s disabled by default. So you have to turn it on for each row in the grid… but the question is HOW?
Because the rows are all held in the DataGrid.rendererArray (which isn’t even documented!!) which is read-only, the creation of the rows is hidden behind closed doors in the DataGrid itself, and the particular classes used by this process are effectively hard-coded into the DataGrid, how on earth do you turn doubleClickEnabled on in the first place?? The answer, it turns out, is fairly simple (or blowin’ in the wind, depending on your iTunes playlist for a Monday morning)… you use the DataGrid’s itemRenderer property to apply doubleClickEnabled to each object that it creates.
Let me explain. DataGrid.itemRenderer is actually an instance of ContextualObjectFactory, which in turn has it’s own property called, of all things, “properties”. By default, it’s null, but if you add name/value pairs to it (by assigning it an instance of Object), each name/value pair is copied into every object created by the factory. So all you have to do is something like this:
<mx:TitleWindow xmlns:mx="http://www.adobe.com/2006/mxml" width="400" height="300" title="DataGrid DoubleClick Example"> <![CDATA[ import mx.collections.ArrayCollection; private var data: ArrayCollection = new ArrayCollection(); [Bindable] private function get dataProvider() { if (data.length < 1) { data.addItem({ Column1: "This is the first row", Column2: "This is row 1 column 2 ", Column3: "column 3 from row 1 " }); data.addItem({ Column1: "This is the second row", Column2: "This is row 2 column 2 ", Column3: "column 3 from row 2 " }); } return data; } private function doubleClickHandler(event: ListEvent): void { mx.controls.Alert.show("Column1 is: " + event.itemRenderer.data.Column1); } private function gridInit(event: FlexEvent): void { event.currentTarget.itemRenderer.properties = { doubleClickEnabled: true }; } ]]> <mx:DataGrid id="myGrid" left="10" top="108" bottom="10" right="10" dataProvider="{dataProvider}" itemDoubleClick="doubleClickHandler(event);" initialize="gridInit(event);">
First off, check out the gridInit() method, where I’m assigning event.currentTarget.itemRenderer an object with the name/value pair I want to end up on all my grid rows. Since the initialize hander is on the grid, currentTarget is the grid itself and I can work with all it’s properties as though I were using this.myGrid. I have come to love the Flex built-in events a lot. They’re not perfect, but most excellent nonetheless.Let me hit the hight points for you:
Using the initialize event on the DataGrid component to apply an object to the intemRenderer property means that the itemRenderer exists but hasn’t done anything yet, so that you don’t get any errors or omissions as happened when I was trying to do this in the parent component’s creationComplete handler, in the DataGrid’s creationComplete handler, and anywhere else I tried to add the object to the itemRenderer’s properties collection. If you do it with the parent’s creationComplete handler, it blows up with compiler errors referring to class casting from IFactory, and if you do it from the grid’s creationComplete event you get no errors but only a few of the rows are actually double-clickable. Very odd, but using initialize on the grid itself works just fine.
Setting itemRenderer.properties to {doubleClickEnabled:true} means that every object created by the itemRenderer will have doubleClickEnabled=true applied to it as part of the object creation process. Since the factory exists to create all the rows in the grid, every row will have double-click turned on and since the itemDoubleClick event has a defined handler, that handler will be called when any row is enabled.
One more bit of niftiness: When you double click a row, the ListEvent that gets fired has an itemRenderer property, which is the actual itemRenderer you double-clicked on. It has a data property, which represents the entire item from the dataProvider. An example of where this is nice is in the case of AIR, working with a DataGrid that’s populated from a query against a local database. Say your query has 10 columns, but your DataGrid’s DataGridRow tags are only configured to deal with 3 of them. The itemRenderer.data property actually contains the entire row from the query (which is actually an ArrayCollection), and the itemRenderer.listData is actually the individual cell in the DataGrid that you double-clicked on. I actually like the fact that you have access to the whole row from the original dataset.
So the next time you have to make the rows in a grid accept double-clicks and you go googling for it, I hope this comes up. This article would have saved me a few hours over the weekend. Who knows? Maybe it’ll be another 2 years before I have to do this again and it’ll save me another couple hours when I go Googling for it myself!