Nov
3

Flex DataGrid ItemRenderers Explained

Posted by Aaron West at 10:36 AM in Flex

I'm still working through some minor DataGrid issues with custom ItemRenderers. Through reading various blogs and posts on FlexCoders, I came across the absolute best explanation of how DataGrids work behind-the-scenes. Ben (sorry I don't know his last name) has posted a sample DataGrid / ItemRenderer application where he explains how DataGrids operate on and display data from their data providers. Here is an excerpt from his posting:

from Ben's blog posting

If your DataGrid's dataProvider has 100 items, but it is only big enough to display 10 at any given time, the DataGrid only actually draws 10 items, in order to maximize performance. When you scroll the DataGrid there aren't any new items being drawn, it is simply swapping the data properties between the already drawn items. So scrolling down one row means that the piece of data for the second item is switched to be the data for the first item, the second item receives the third item's data, and so on and so on. During these reassignments, things like whether or not the CheckBox should be checked get all messed up. Some get checked, some get unchecked... its completely unpredictable. To handle this problem, we must override the data's setter method and use our own logic to determine whether or not the CheckBox should be checked.

What a clear explanation of how things work! As I mentioned earlier I'm still working through some issues with my application. I'll be finishing it up over the next few days at which point I'll be posting a follow-up to my previous "DataGrid Woes" post.

Aaron West's Gravatar
About this post:

This entry was posted by Aaron West on November 3, 2006 at 10:36 AM. It was filed in the following categories: Flex. It has been viewed 32704 times and has 20 comments.

1 related blog entries

20 Responses to Flex DataGrid ItemRenderers Explained

  1. Ben

    Hey Aaron, just wanted to let you know that during the exploration for my latest post I discovered the solution for your "one itemRenderer updates another" issue. If you make the whole class that is used in your dataProvider (PersonVO or whatever) Bindable, and update the values in the data override of one, the other should automatically update, as long as you've correctly overridden the data method in it. Make sense?

  2. pankaj

    Nice reserch buddy ! buck up ! But i m facing another problem
    with data Grid that i create a grid at run time now i want to
    item render component at run time if any body have solution then plz inform

  3. OMG! This was f'ing driving me crazy. I am not using the DataGrid but the List Component and the same madness is happening with simple stuff like graphics and other vars. This post seems old but the problem still remains. I wonder should we get a discount from Adobe every time we run into a problem like this and it makes us lose clients, time, or revenue? LOL. Never would happen. The Official Adobe Flex Blogger have to know about this stuff ... because it actually really really common for someone to use a checkbox of some type of data in a List control. In my app. I was getting duplicate items -- Items disappearing and all types of weirdness. Seems like a poorly written component -- how could it ever get out the door like that??

  4. @Brian - Yea, this post is old but I believe the issues have been resolved with the latest mxmlc compiler. I haven't attempted to replicate this issue using old Flex Builder projects and the latest Flex SDK, but I think this is fixed. At least, if your MXML / AS3 is written a certain way.

    I wound up fixing this on Flex 2 by taking some clues from other blog posts on how to wire up the item renderers and listeners better. Keep digging, I think a solution is out there for you.

  5. Actually I am using the most up to date sdk and compiler and the issue is still around. I will post my work around for it.

  6. @Brian - Even if you are on the latest Flex 3 SDK I believe there are ways to program your components and item renderers such that really odd behavior occurs. Item renderers have been real finicky in my experience and it's taken a lot of love and care to get them to work properly.

  7. Yeah, sorry my response was not clear. Yes the problems do appear, AND yes I have figured it out since my original post. I will post my solutions so other who run into it can benefit.

  8. Derrik

    @Brian

    So are you going to post your solution? I am an other who would benefit, thanks!

  9. Hon

    @Brian

    Errrr..... is the solution around yet? I am an "other who would benefit".

    I have this issue with a combobox rendered in a grid, and it is working in a similar fashion as Ben described in his original post... You guys already know the feeling.

    I have been searching for a solution for a couple of days now. I don't seem to find any that works, though.

    Regards.

  10. Hon

    ok, I finally got it.

    I DO post the solution so "other who run into it can benefit". I will try to keep it as simple as possible, avoiding any unnecessary code. Only the essential code is shown. The more simple, the better:

    This is the grid:
       <mx:DataGrid dataProvider="{myDataProvider}" editable="true">
          <mx:columns>
             <mx:DataGridColumn dataField="myDataField" editorDataField="myEditorField"
                itemRenderer="myRenderers.myCustomComboBox" rendererIsEditor="true"/>
          </mx:columns>
       </mx:DataGrid>

    (Note the "editable='true'" property of the dataField)

    This is the custom renderer, again as simple as possible:
       <mx:VBox height="100%" width="100%" xmlns:mx="http://www.adobe.com/2006/mxml">;
          <mx:Script>
             <![CDATA[
                // Define a property to return the new value to the cell.
                public var myEditorField : String = "";

                /**
                 * Override the set method for the data property
                 */
                override public function set data(value : Object) : void {
                   super.data = value;

                   if (value != null) {
                      for (var index : uint = 0; index < myComboDataProvider.length; index++) {
                         if (myComboDataProvider.getItemAt(index).data == value.importantField) {
                            myComboBox.selectedIndex = index;
                            break;
                         }
                      }
                   }
                }

                /**
                 * Changes the public variable, so that the data in the
                 * combobox can be accessed from outside.
                 */
                private function changeReference() : void {
                   myEditorField = myComboDataProvider.getItemAt(myComboBox.selectedIndex).data;
                }
             ]]>
          </mx:Script>

          <mx:ComboBox id="myComboBox" change="{changeReference();}" dataProvider="{myComboDataProvider}" width="100%"/>
       </mx:VBox>

    The DataProvider in the myCustomComboBox may be whichever you want (ArrayCollection, XMLListCollection, etc). I am supposing that such DataProvider has a field called "data" (myComboDataProvider.getItemAt(index).data).

    The input object (from the DataGrid to the myCustomComboBox) has a field called "importantField" which stores the value we want to change with the ComboBox (value.importantField).

    The event "change" triggers the function "changeReference" and saves into "myEditorField", which is the connection between the DataGrid and the itemRenderer (myCustomComboBox ).

    I really hope this helps other.
    Good luck.

  11. @Hon - Thanks for posting your solution. I love it when people follow up their own questions when answers. It will certainly benefit anyone who might read this post and the comment thread.

  12. Hon

    @ Aaron

    My pleasure :)

  13. I'm having problem with a datagrid and itemrenderer (a VBOX).
    ex. When I attach an itemrenderer (flex 3.2) to my datagrid and then tweak the height of the VBOX (root) itemrenderer of the last Cell-field-row, the rest (all empty row below) of the datagrid layout will inherit from the last change.
    Strange! The only way to avoid that problem was to add a null object on every update of the datagrid provider.
    Fabrice

  14. Chaitanya Akkineni

    I have a problem with flex datagrid. I have a datagrid with 5 columns in that. Based on the row selected, i need to handle some buttons ( either enable or disable). All this is working fine. Here comes the problem. For the last column, i have the data like A with a link ( used renderer for that ). but when i select that A or click on it, the row is not getting selected. so the enabling and disabling of buttons is a proble. How can i achieve that

  15. @Chaitanya - I'd have to see your code in order to offer any advice. Feel free to use Pastebin.com or some other code sharing service to share your code. Then, use my Contact Me page to send me the details.

  16. I'm still having problems with this. I have the event set up and I'm saving the data correctly. I have a field that requires an object, and in that object is a field that sends and handles the primary index of a dropdown box.

    When I get the set from the data provider, I get the data as an object, but whenever I change the data in the combo box I get an empty string instead. I have nothing that does this, but it seems to be happening no matter what I save to the super.data data object.

    Does anyone have any thoughts?

  17. Hon

    @Greg Lindquist

    I'm not sure I understand what your problem is. Here it goes my guessing, anyways: have you notice that you must also implement the "change" event in your custom element?

    Not seeing any code makes it a bit difficult for me to give you a more adequate response.

  18. Sorry about that. I have a data grid, and a field with an itemRenderer called 'secondaryUser'. The data type for this is an object, with three fields. One field sets the index for a combo box and the other two fields are arrays that gets set in a mx:List component. For now, I'm just trying to set the combo box, but want to keep the object and pass around the first field only to get everything working.

    Here's my item Renderer set data method:
    override public function set data(value:Object):void {
    if (value!= null && value.secondaryUsers as Object != null && value.secondaryUsers.length == undefined){
    super.data = value;

    if(value.truckOwners.selectedIndex >= 0){
    cbo.selectedIndex = value.secondaryUsers.selectedIndex;
    }
    }
    }

    Here's the method that's called with the change event for the combobox. It grabs all three fields and bubbles it up to the datagrid
    private function changeReference(e:Event) : void {
    var currentSI:int = cbo.selectedIndex;
    dispatchEvent(new DataChangeEvent(DataChangeEvent.DATA_CHANGE, super.data,currentSI,non_selectedArray,selectedArray)); // my data, combobox selected index, and two arrays for the mx:List components
    }

    This is the event that's being fired off:
    public function DataChangeEvent(type:String, _objData:Object, _selectedPrimaryIndex:int, _nonSelectedUsers:Array , _selectedUsers:Array)

    This is the event listener on the data grid:
    private function onDataChange(e:DataChangeEvent):void{

    var objData:allUserData = e.objData as allUserData; // class for the data I'm sending back and forth
    objData.secondaryUsers.selectedIndex = e.selectedPrimaryIndex;
    objData.secondaryUsers.non_selectedUsers = e.nonSelectedUsers;
    objData.secondaryUsers.selectedUsers = e.selectedUsers;

    };

    So, I have it so that I'm sending the data back up and saving to the entire row of data whenever it changes, however; when the data function gets called when I scroll or click somewhere the entire object is just erased. I guess I'm confused, because it feels like there's something happening behind the scenes that I'm not getting access to?

    Is this enough to see the holes in my logic?

  19. Thank you Hon!

    I'll be as prompt as I can if you need more information.

  20. Hon

    @Greg Lindquist

    I've had not much time to study your code (BTW, thanks for posting it), but the first thing that caught my eye is the fact that you only call the super.data method under certain circumstances.

    It's been a couple of years since I faced this problem, but I will try to help. The first thing I'd do is rewriting the "override" piece of code as follows:

    override public function set data(value : Object) : void
    {
    super.data = value;

    if (value && Object(value.secondaryUsers) &&
    value.secondaryUsers.length == undefined)
    {
    if(value.truckOwners.selectedIndex >= 0)
    {
    cbo.selectedIndex = value.secondaryUsers.selectedIndex;
    }
    }
    }

    I've changed the line where you assign the value to the parent class.

    I've done two more changes (which are NOT important and I encourage you to COMPLETELY ignore them if you don't find them minimally useful); I am aware that they are offtopic:
    1.- When checking if an object is null, in AS3 you don't need to write "if (instance != null)". The correct way should be "if (instance)". I know I did it in my previous post, but back then I was still ignorant regarding certain things...... now I am ignorant regarding some other things :-P
    2.- When casting elements (as said in the Adobe's coding conventions) the correct code is "ClassName(objectToCast)" and not "objectToCast as ClassName". One exception to this rule is Array, as this particular class has a constructor "Array(something)" which can lead to confusion, so in this case the right coding is "objectToCast as Array".

    I recommend (in case you are interested) reading http://opensource.adobe.com/wiki/display/flexsdk/C...

    Back to the important thing, which is your posted problem, please keep me up with the result of this little test and let's see if we can reach the solution. Contact me through private if it is your desire (you know my address, if you have subscribed to this publication).